Skip to content

Commit 8b2e5e9

Browse files
committed
[asan] Re-exec without ASLR if needed on 32-bit Linux
High-entropy ASLR allows up to 16-bits of entropy (256MB), which is a significant chunk of the 32-bit address space (4GB, less if running with a 32-bit kernel). 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 will re-exec without ASLR if it cannot map the shadow, thus reclaiming the 256MB of address space. Alternatives considered: 1) We don't attempt to 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 on 32-bit Linux relies on the constant offset to optimize its instrumentation and compiler-rt. This is loosely inspired by llvm#78351, llvm#85142, and llvm#85674, though those were required because there were no static mappings that could fully shadow the range of user mappings; this is not the case for ASan.
1 parent fd0e20a commit 8b2e5e9

File tree

5 files changed

+45
-0
lines changed

5 files changed

+45
-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: 28 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,33 @@ 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+
bool aslr_on =
118+
(old_personality != -1) && ((old_personality & ADDR_NO_RANDOMIZE) == 0);
119+
120+
if (aslr_on) {
121+
// Disable ASLR if the memory layout was incompatible.
122+
// Alternatively, we could just keep re-execing until we get lucky
123+
// with a compatible randomized layout, but the risk is that if it's
124+
// not an ASLR-related issue, we will be stuck in an infinite loop of
125+
// re-execing (unless we change ReExec to pass a parameter of the
126+
// number of retries allowed.)
127+
VReport(1,
128+
"WARNING: AddressSanitizer: memory layout is incompatible, "
129+
"possibly due to high-entropy ASLR.\n"
130+
"Re-execing with fixed virtual address space.\n"
131+
"N.B. reducing ASLR entropy is preferable.\n");
132+
CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);
133+
134+
ReExec();
135+
}
136+
}
137+
110138
# if SANITIZER_ANDROID
111139
// FIXME: should we do anything for Android?
112140
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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ void InitializeShadowMemory() {
109109
ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);
110110
ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1);
111111
} else {
112+
# if SANITIZER_LINUX
113+
// The shadow mappings can shadow the entire user address space. However,
114+
// on 32-bit systems, the maximum ASLR entropy (currently up to 16-bits
115+
// == 256MB) is a significant chunk of the address space; reclaiming it by
116+
// disabling ASLR might allow chonky binaries to run.
117+
if (sizeof(uptr) == 32)
118+
TryReExecWithoutASLR();
119+
# endif
120+
112121
Report(
113122
"Shadow memory range interleaves with an existing memory mapping. "
114123
"ASan cannot proceed correctly. ABORTING.\n");

compiler-rt/lib/asan/asan_win.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ uptr __asan_get_shadow_memory_dynamic_address() {
4343
__asan_init();
4444
return __asan_shadow_memory_dynamic_address;
4545
}
46+
4647
} // extern "C"
4748

4849
// ---------------------- Windows-specific interceptors ---------------- {{{
@@ -279,6 +280,9 @@ uptr FindDynamicShadowStart() {
279280
GetMmapGranularity());
280281
}
281282

283+
// Not used
284+
void TryReExecWithoutASLR() {}
285+
282286
void AsanCheckDynamicRTPrereqs() {}
283287

284288
void AsanCheckIncompatibleRT() {}

0 commit comments

Comments
 (0)