Skip to content

Commit 757caa2

Browse files
thurstondchencha3
authored andcommitted
[dfsan] Re-exec with no ASLR if memory layout is incompatible on Linux (llvm#85674)
DFSan's shadow mappings are incompatible with 32 bits of ASLR entropy ('sudo sysctl vm.mmap_rnd_bits=32; ninja check-dfsan') and it is difficult to fix this via increasing the size of the shadow mappings, due to the overhead of shadow memory. This patch works around the issue by detecting if the memory layout is incompatible, and if so, re-exec'ing without ASLR. DFSan and MSan share copy-pasted shadow memory code, hence this workaround is ported from MSan: - "[msan] Re-exec with no ASLR if memory layout is incompatible on Linux" (llvm@58f7251) - "[msan] Add 'MappingDesc::ALLOCATOR' type and check it is available" (llvm@af2bf86) (which in turn are inspired by TSan: "Re-exec TSan with no ASLR if memory layout is incompatible on Linux" (llvm@0784b1e )) aeubanks had remarked in llvm#85142 (comment) that this issue occurs in Chromium: https://ci.chromium.org/ui/p/chromium/builders/try/linux_upload_clang/5066/overview
1 parent a76fff8 commit 757caa2

File tree

3 files changed

+78
-19
lines changed

3 files changed

+78
-19
lines changed

compiler-rt/lib/dfsan/dfsan.cpp

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
#include "sanitizer_common/sanitizer_libc.h"
3434
#include "sanitizer_common/sanitizer_report_decorator.h"
3535
#include "sanitizer_common/sanitizer_stacktrace.h"
36+
#if SANITIZER_LINUX
37+
# include <sys/personality.h>
38+
#endif
3639

3740
using namespace __dfsan;
3841

@@ -1127,11 +1130,12 @@ static void CheckMemoryLayoutSanity() {
11271130

11281131
// TODO: CheckMemoryRangeAvailability is based on msan.
11291132
// Consider refactoring these into a shared implementation.
1130-
static bool CheckMemoryRangeAvailability(uptr beg, uptr size) {
1133+
static bool CheckMemoryRangeAvailability(uptr beg, uptr size, bool verbose) {
11311134
if (size > 0) {
11321135
uptr end = beg + size - 1;
11331136
if (!MemoryRangeIsAvailable(beg, end)) {
1134-
Printf("FATAL: Memory range %p - %p is not available.\n", beg, end);
1137+
if (verbose)
1138+
Printf("FATAL: Memory range %p - %p is not available.\n", beg, end);
11351139
return false;
11361140
}
11371141
}
@@ -1163,7 +1167,7 @@ static bool ProtectMemoryRange(uptr beg, uptr size, const char *name) {
11631167

11641168
// TODO: InitShadow is based on msan.
11651169
// Consider refactoring these into a shared implementation.
1166-
bool InitShadow(bool init_origins) {
1170+
bool InitShadow(bool init_origins, bool dry_run) {
11671171
// Let user know mapping parameters first.
11681172
VPrintf(1, "dfsan_init %p\n", (void *)&__dfsan::dfsan_init);
11691173
for (unsigned i = 0; i < kMemoryLayoutSize; ++i)
@@ -1173,8 +1177,9 @@ bool InitShadow(bool init_origins) {
11731177
CheckMemoryLayoutSanity();
11741178

11751179
if (!MEM_IS_APP(&__dfsan::dfsan_init)) {
1176-
Printf("FATAL: Code %p is out of application range. Non-PIE build?\n",
1177-
(uptr)&__dfsan::dfsan_init);
1180+
if (!dry_run)
1181+
Printf("FATAL: Code %p is out of application range. Non-PIE build?\n",
1182+
(uptr)&__dfsan::dfsan_init);
11781183
return false;
11791184
}
11801185

@@ -1195,27 +1200,62 @@ bool InitShadow(bool init_origins) {
11951200
bool protect = type == MappingDesc::INVALID ||
11961201
(!init_origins && type == MappingDesc::ORIGIN);
11971202
CHECK(!(map && protect));
1198-
if (!map && !protect)
1199-
CHECK(type == MappingDesc::APP);
1203+
if (!map && !protect) {
1204+
CHECK(type == MappingDesc::APP || type == MappingDesc::ALLOCATOR);
1205+
1206+
if (dry_run && type == MappingDesc::ALLOCATOR &&
1207+
!CheckMemoryRangeAvailability(start, size, !dry_run))
1208+
return false;
1209+
}
12001210
if (map) {
1201-
if (!CheckMemoryRangeAvailability(start, size))
1211+
if (dry_run && !CheckMemoryRangeAvailability(start, size, !dry_run))
12021212
return false;
1203-
if (!MmapFixedSuperNoReserve(start, size, kMemoryLayout[i].name))
1213+
if (!dry_run &&
1214+
!MmapFixedSuperNoReserve(start, size, kMemoryLayout[i].name))
12041215
return false;
1205-
if (common_flags()->use_madv_dontdump)
1216+
if (!dry_run && common_flags()->use_madv_dontdump)
12061217
DontDumpShadowMemory(start, size);
12071218
}
12081219
if (protect) {
1209-
if (!CheckMemoryRangeAvailability(start, size))
1220+
if (dry_run && !CheckMemoryRangeAvailability(start, size, !dry_run))
12101221
return false;
1211-
if (!ProtectMemoryRange(start, size, kMemoryLayout[i].name))
1222+
if (!dry_run && !ProtectMemoryRange(start, size, kMemoryLayout[i].name))
12121223
return false;
12131224
}
12141225
}
12151226

12161227
return true;
12171228
}
12181229

1230+
bool InitShadowWithReExec(bool init_origins) {
1231+
// Start with dry run: check layout is ok, but don't print warnings because
1232+
// warning messages will cause tests to fail (even if we successfully re-exec
1233+
// after the warning).
1234+
bool success = InitShadow(init_origins, true);
1235+
if (!success) {
1236+
#if SANITIZER_LINUX
1237+
// Perhaps ASLR entropy is too high. If ASLR is enabled, re-exec without it.
1238+
int old_personality = personality(0xffffffff);
1239+
bool aslr_on =
1240+
(old_personality != -1) && ((old_personality & ADDR_NO_RANDOMIZE) == 0);
1241+
1242+
if (aslr_on) {
1243+
VReport(1,
1244+
"WARNING: DataflowSanitizer: memory layout is incompatible, "
1245+
"possibly due to high-entropy ASLR.\n"
1246+
"Re-execing with fixed virtual address space.\n"
1247+
"N.B. reducing ASLR entropy is preferable.\n");
1248+
CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);
1249+
ReExec();
1250+
}
1251+
#endif
1252+
}
1253+
1254+
// The earlier dry run didn't actually map or protect anything. Run again in
1255+
// non-dry run mode.
1256+
return success && InitShadow(init_origins, false);
1257+
}
1258+
12191259
static void DFsanInit(int argc, char **argv, char **envp) {
12201260
CHECK(!dfsan_init_is_running);
12211261
if (dfsan_inited)
@@ -1229,7 +1269,11 @@ static void DFsanInit(int argc, char **argv, char **envp) {
12291269

12301270
CheckASLR();
12311271

1232-
InitShadow(dfsan_get_track_origins());
1272+
if (!InitShadowWithReExec(dfsan_get_track_origins())) {
1273+
Printf("FATAL: DataflowSanitizer can not mmap the shadow memory.\n");
1274+
DumpProcessMap();
1275+
Die();
1276+
}
12331277

12341278
initialize_interceptors();
12351279

compiler-rt/lib/dfsan/dfsan_allocator.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ struct DFsanMapUnmapCallback {
3737
void OnUnmap(uptr p, uptr size) const { dfsan_set_label(0, (void *)p, size); }
3838
};
3939

40+
// Note: to ensure that the allocator is compatible with the application memory
41+
// layout (especially with high-entropy ASLR), kSpaceBeg and kSpaceSize must be
42+
// duplicated as MappingDesc::ALLOCATOR in dfsan_platform.h.
4043
#if defined(__aarch64__)
4144
const uptr kAllocatorSpace = 0xE00000000000ULL;
4245
#else

compiler-rt/lib/dfsan/dfsan_platform.h

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,19 @@ using __sanitizer::uptr;
2727
struct MappingDesc {
2828
uptr start;
2929
uptr end;
30-
enum Type { INVALID, APP, SHADOW, ORIGIN } type;
30+
enum Type {
31+
INVALID = 1,
32+
ALLOCATOR = 2,
33+
APP = 4,
34+
SHADOW = 8,
35+
ORIGIN = 16,
36+
} type;
3137
const char *name;
3238
};
3339

40+
// Note: MappingDesc::ALLOCATOR entries are only used to check for memory
41+
// layout compatibility. The actual allocation settings are in
42+
// dfsan_allocator.cpp, which need to be kept in sync.
3443
#if SANITIZER_LINUX && SANITIZER_WORDSIZE == 64
3544

3645
# if defined(__aarch64__)
@@ -53,7 +62,8 @@ const MappingDesc kMemoryLayout[] = {
5362
{0X0B00000000000, 0X0C00000000000, MappingDesc::SHADOW, "shadow-10-13"},
5463
{0X0C00000000000, 0X0D00000000000, MappingDesc::INVALID, "invalid"},
5564
{0X0D00000000000, 0X0E00000000000, MappingDesc::ORIGIN, "origin-10-13"},
56-
{0X0E00000000000, 0X1000000000000, MappingDesc::APP, "app-15"},
65+
{0X0E00000000000, 0X0E40000000000, MappingDesc::ALLOCATOR, "allocator"},
66+
{0X0E40000000000, 0X1000000000000, MappingDesc::APP, "app-15"},
5767
};
5868
# define MEM_TO_SHADOW(mem) ((uptr)mem ^ 0xB00000000000ULL)
5969
# define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x200000000000ULL)
@@ -76,7 +86,8 @@ const MappingDesc kMemoryLayout[] = {
7686
{0x510000000000ULL, 0x600000000000ULL, MappingDesc::APP, "app-2"},
7787
{0x600000000000ULL, 0x610000000000ULL, MappingDesc::ORIGIN, "origin-1"},
7888
{0x610000000000ULL, 0x700000000000ULL, MappingDesc::INVALID, "invalid"},
79-
{0x700000000000ULL, 0x800000000000ULL, MappingDesc::APP, "app-3"}};
89+
{0x700000000000ULL, 0x740000000000ULL, MappingDesc::ALLOCATOR, "allocator"},
90+
{0x740000000000ULL, 0x800000000000ULL, MappingDesc::APP, "app-3"}};
8091
# define MEM_TO_SHADOW(mem) (((uptr)(mem)) ^ 0x500000000000ULL)
8192
# define SHADOW_TO_ORIGIN(mem) (((uptr)(mem)) + 0x100000000000ULL)
8293
# endif
@@ -93,20 +104,21 @@ const uptr kMemoryLayoutSize = sizeof(kMemoryLayout) / sizeof(kMemoryLayout[0]);
93104
__attribute__((optimize("unroll-loops")))
94105
#endif
95106
inline bool
96-
addr_is_type(uptr addr, MappingDesc::Type mapping_type) {
107+
addr_is_type(uptr addr, int mapping_types) {
97108
// It is critical for performance that this loop is unrolled (because then it is
98109
// simplified into just a few constant comparisons).
99110
#ifdef __clang__
100111
# pragma unroll
101112
#endif
102113
for (unsigned i = 0; i < kMemoryLayoutSize; ++i)
103-
if (kMemoryLayout[i].type == mapping_type &&
114+
if ((kMemoryLayout[i].type & mapping_types) &&
104115
addr >= kMemoryLayout[i].start && addr < kMemoryLayout[i].end)
105116
return true;
106117
return false;
107118
}
108119

109-
#define MEM_IS_APP(mem) addr_is_type((uptr)(mem), MappingDesc::APP)
120+
#define MEM_IS_APP(mem) \
121+
(addr_is_type((uptr)(mem), MappingDesc::APP | MappingDesc::ALLOCATOR))
110122
#define MEM_IS_SHADOW(mem) addr_is_type((uptr)(mem), MappingDesc::SHADOW)
111123
#define MEM_IS_ORIGIN(mem) addr_is_type((uptr)(mem), MappingDesc::ORIGIN)
112124

0 commit comments

Comments
 (0)