Skip to content

Commit 3b3f8c5

Browse files
authored
[asan] Re-exec without ASLR if needed on 32-bit Linux (#131975)
High-entropy ASLR allows up to 16-bits of entropy (2**16 4KB pages == 256MB; a bit more in practice because of implementation details), which is a significant chunk of the user address space on 32-bit systems (4GB or less). This, combined with ASan's shadow (512MB) and ASan's fixed shadow offset (512MB), makes it possible for large binaries to fail to map the shadow. This patch changes ASan to do a one-time re-exec without ASLR if it cannot map the shadow, thus reclaiming the ~256MB of address space. Alternatives considered: 1) We don't lower ASan's fixed shadow offset, because that would limit non-PIE binaries. 2) We don't switch to a dynamic shadow offset, because ASan for 32-bit Linux relies on the compile-time constant offset to optimize its instrumentation and compiler-rt. This is loosely inspired by #78351, #85142, and #85674, though those were required because there were no static shadow mappings that could fully shadow the range of user mappings; this is not the case for ASan.
1 parent fdeb2ff commit 3b3f8c5

File tree

5 files changed

+46
-0
lines changed

5 files changed

+46
-0
lines changed

compiler-rt/lib/asan/asan_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ void ReplaceSystemMalloc();
8282
uptr FindDynamicShadowStart();
8383
void AsanCheckDynamicRTPrereqs();
8484
void AsanCheckIncompatibleRT();
85+
void TryReExecWithoutASLR();
8586

8687
// Unpoisons platform-specific stacks.
8788
// Returns true if all stacks have been unpoisoned.

compiler-rt/lib/asan/asan_linux.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
# include <pthread.h>
2222
# include <stdio.h>
2323
# include <sys/mman.h>
24+
# include <sys/personality.h>
2425
# include <sys/resource.h>
2526
# include <sys/syscall.h>
2627
# include <sys/time.h>
@@ -107,6 +108,37 @@ void FlushUnneededASanShadowMemory(uptr p, uptr size) {
107108
ReleaseMemoryPagesToOS(MemToShadow(p), MemToShadow(p + size));
108109
}
109110

111+
void ReExecWithoutASLR() {
112+
// ASLR personality check.
113+
// Caution: 'personality' is sometimes forbidden by sandboxes, so only call
114+
// this function as a last resort (when the memory mapping is incompatible
115+
// and ASan would fail anyway).
116+
int old_personality = personality(0xffffffff);
117+
if (old_personality == -1) {
118+
VReport(1, "WARNING: unable to run personality check.\n");
119+
return;
120+
}
121+
122+
bool aslr_on = (old_personality & ADDR_NO_RANDOMIZE) == 0;
123+
124+
if (aslr_on) {
125+
// Disable ASLR if the memory layout was incompatible.
126+
// Alternatively, we could just keep re-execing until we get lucky
127+
// with a compatible randomized layout, but the risk is that if it's
128+
// not an ASLR-related issue, we will be stuck in an infinite loop of
129+
// re-execing (unless we change ReExec to pass a parameter of the
130+
// number of retries allowed.)
131+
VReport(1,
132+
"WARNING: AddressSanitizer: memory layout is incompatible, "
133+
"possibly due to high-entropy ASLR.\n"
134+
"Re-execing with fixed virtual address space.\n"
135+
"N.B. reducing ASLR entropy is preferable.\n");
136+
CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);
137+
138+
ReExec();
139+
}
140+
}
141+
110142
# if SANITIZER_ANDROID
111143
// FIXME: should we do anything for Android?
112144
void AsanCheckDynamicRTPrereqs() {}

compiler-rt/lib/asan/asan_mac.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ uptr FindDynamicShadowStart() {
5555
GetMmapGranularity());
5656
}
5757

58+
// Not used.
59+
void TryReExecWithoutASLR() {}
60+
5861
// No-op. Mac does not support static linkage anyway.
5962
void AsanCheckDynamicRTPrereqs() {}
6063

compiler-rt/lib/asan/asan_shadow_setup.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@ void InitializeShadowMemory() {
109109
ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);
110110
ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1);
111111
} else {
112+
// The shadow mappings can shadow the entire user address space. However,
113+
// on 32-bit systems, the maximum ASLR entropy (currently up to 16-bits
114+
// == 256MB) is a significant chunk of the address space; reclaiming it by
115+
// disabling ASLR might allow chonky binaries to run.
116+
if (sizeof(uptr) == 32)
117+
TryReExecWithoutASLR();
118+
112119
Report(
113120
"Shadow memory range interleaves with an existing memory mapping. "
114121
"ASan cannot proceed correctly. ABORTING.\n");

compiler-rt/lib/asan/asan_win.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ uptr FindDynamicShadowStart() {
279279
GetMmapGranularity());
280280
}
281281

282+
// Not used
283+
void TryReExecWithoutASLR() {}
284+
282285
void AsanCheckDynamicRTPrereqs() {}
283286

284287
void AsanCheckIncompatibleRT() {}

0 commit comments

Comments
 (0)