[llvm-branch-commits] [compiler-rt] release/18.x: [msan] Re-exec with no ASLR if memory layout is incompatible on Linux (#85142) (PR #86201)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Mar 21 14:40:06 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-compiler-rt-sanitizer
Author: None (llvmbot)
<details>
<summary>Changes</summary>
Backport af2bf86a372cacf5f536bae06e2f2d3886eefb7b 58f7251820b14c93168726a24816d8a094599be5
Requested by: @<!-- -->thurstond
---
Full diff: https://github.com/llvm/llvm-project/pull/86201.diff
4 Files Affected:
- (modified) compiler-rt/lib/msan/msan.cpp (+1-1)
- (modified) compiler-rt/lib/msan/msan.h (+24-11)
- (modified) compiler-rt/lib/msan/msan_allocator.cpp (+3)
- (modified) compiler-rt/lib/msan/msan_linux.cpp (+54-13)
``````````diff
diff --git a/compiler-rt/lib/msan/msan.cpp b/compiler-rt/lib/msan/msan.cpp
index 3cdf10c149902c..a2fc27de1901b4 100644
--- a/compiler-rt/lib/msan/msan.cpp
+++ b/compiler-rt/lib/msan/msan.cpp
@@ -467,7 +467,7 @@ void __msan_init() {
__msan_clear_on_return();
if (__msan_get_track_origins())
VPrintf(1, "msan_track_origins\n");
- if (!InitShadow(__msan_get_track_origins())) {
+ if (!InitShadowWithReExec(__msan_get_track_origins())) {
Printf("FATAL: MemorySanitizer can not mmap the shadow memory.\n");
Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
Printf("FATAL: Disabling ASLR is known to cause this error.\n");
diff --git a/compiler-rt/lib/msan/msan.h b/compiler-rt/lib/msan/msan.h
index 710447a3e1a357..7fb58be67a02cd 100644
--- a/compiler-rt/lib/msan/msan.h
+++ b/compiler-rt/lib/msan/msan.h
@@ -33,12 +33,18 @@ struct MappingDesc {
uptr start;
uptr end;
enum Type {
- INVALID, APP, SHADOW, ORIGIN
+ INVALID = 1,
+ ALLOCATOR = 2,
+ APP = 4,
+ SHADOW = 8,
+ ORIGIN = 16,
} type;
const char *name;
};
-
+// Note: MappingDesc::ALLOCATOR entries are only used to check for memory
+// layout compatibility. The actual allocation settings are in
+// msan_allocator.cpp, which need to be kept in sync.
#if SANITIZER_LINUX && defined(__mips64)
// MIPS64 maps:
@@ -84,7 +90,8 @@ const MappingDesc kMemoryLayout[] = {
{0X0B00000000000, 0X0C00000000000, MappingDesc::SHADOW, "shadow-10-13"},
{0X0C00000000000, 0X0D00000000000, MappingDesc::INVALID, "invalid"},
{0X0D00000000000, 0X0E00000000000, MappingDesc::ORIGIN, "origin-10-13"},
- {0X0E00000000000, 0X1000000000000, MappingDesc::APP, "app-15"},
+ {0x0E00000000000, 0x0E40000000000, MappingDesc::ALLOCATOR, "allocator"},
+ {0X0E40000000000, 0X1000000000000, MappingDesc::APP, "app-15"},
};
# define MEM_TO_SHADOW(mem) ((uptr)mem ^ 0xB00000000000ULL)
# define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x200000000000ULL)
@@ -106,7 +113,8 @@ const MappingDesc kMemoryLayout[] = {
{0x510000000000ULL, 0x600000000000ULL, MappingDesc::APP, "app-2"},
{0x600000000000ULL, 0x610000000000ULL, MappingDesc::ORIGIN, "origin-1"},
{0x610000000000ULL, 0x700000000000ULL, MappingDesc::INVALID, "invalid"},
- {0x700000000000ULL, 0x800000000000ULL, MappingDesc::APP, "app-3"}};
+ {0x700000000000ULL, 0x740000000000ULL, MappingDesc::ALLOCATOR, "allocator"},
+ {0x740000000000ULL, 0x800000000000ULL, MappingDesc::APP, "app-3"}};
# define MEM_TO_SHADOW(mem) (((uptr)(mem)) ^ 0x500000000000ULL)
# define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x100000000000ULL)
@@ -118,7 +126,8 @@ const MappingDesc kMemoryLayout[] = {
{0x180200000000ULL, 0x1C0000000000ULL, MappingDesc::INVALID, "invalid"},
{0x1C0000000000ULL, 0x2C0200000000ULL, MappingDesc::ORIGIN, "origin"},
{0x2C0200000000ULL, 0x300000000000ULL, MappingDesc::INVALID, "invalid"},
- {0x300000000000ULL, 0x800000000000ULL, MappingDesc::APP, "high memory"}};
+ {0x300000000000ULL, 0x320000000000ULL, MappingDesc::ALLOCATOR, "allocator"},
+ {0x320000000000ULL, 0x800000000000ULL, MappingDesc::APP, "high memory"}};
// Various kernels use different low end ranges but we can combine them into one
// big range. They also use different high end ranges but we can map them all to
@@ -141,7 +150,8 @@ const MappingDesc kMemoryLayout[] = {
{0x180000000000ULL, 0x1C0000000000ULL, MappingDesc::INVALID, "invalid"},
{0x1C0000000000ULL, 0x2C0000000000ULL, MappingDesc::ORIGIN, "origin"},
{0x2C0000000000ULL, 0x440000000000ULL, MappingDesc::INVALID, "invalid"},
- {0x440000000000ULL, 0x500000000000ULL, MappingDesc::APP, "high memory"}};
+ {0x440000000000ULL, 0x460000000000ULL, MappingDesc::ALLOCATOR, "allocator"},
+ {0x460000000000ULL, 0x500000000000ULL, MappingDesc::APP, "high memory"}};
#define MEM_TO_SHADOW(mem) \
((((uptr)(mem)) & ~0xC00000000000ULL) + 0x080000000000ULL)
@@ -208,7 +218,8 @@ const MappingDesc kMemoryLayout[] = {
{0x510000000000ULL, 0x600000000000ULL, MappingDesc::APP, "app-2"},
{0x600000000000ULL, 0x610000000000ULL, MappingDesc::ORIGIN, "origin-1"},
{0x610000000000ULL, 0x700000000000ULL, MappingDesc::INVALID, "invalid"},
- {0x700000000000ULL, 0x800000000000ULL, MappingDesc::APP, "app-3"}};
+ {0x700000000000ULL, 0x740000000000ULL, MappingDesc::ALLOCATOR, "allocator"},
+ {0x740000000000ULL, 0x800000000000ULL, MappingDesc::APP, "app-3"}};
#define MEM_TO_SHADOW(mem) (((uptr)(mem)) ^ 0x500000000000ULL)
#define SHADOW_TO_ORIGIN(mem) (((uptr)(mem)) + 0x100000000000ULL)
@@ -223,20 +234,22 @@ const uptr kMemoryLayoutSize = sizeof(kMemoryLayout) / sizeof(kMemoryLayout[0]);
#ifndef __clang__
__attribute__((optimize("unroll-loops")))
#endif
-inline bool addr_is_type(uptr addr, MappingDesc::Type mapping_type) {
+inline bool
+addr_is_type(uptr addr, int mapping_types) {
// It is critical for performance that this loop is unrolled (because then it is
// simplified into just a few constant comparisons).
#ifdef __clang__
#pragma unroll
#endif
for (unsigned i = 0; i < kMemoryLayoutSize; ++i)
- if (kMemoryLayout[i].type == mapping_type &&
+ if ((kMemoryLayout[i].type & mapping_types) &&
addr >= kMemoryLayout[i].start && addr < kMemoryLayout[i].end)
return true;
return false;
}
-#define MEM_IS_APP(mem) addr_is_type((uptr)(mem), MappingDesc::APP)
+#define MEM_IS_APP(mem) \
+ (addr_is_type((uptr)(mem), MappingDesc::APP | MappingDesc::ALLOCATOR))
#define MEM_IS_SHADOW(mem) addr_is_type((uptr)(mem), MappingDesc::SHADOW)
#define MEM_IS_ORIGIN(mem) addr_is_type((uptr)(mem), MappingDesc::ORIGIN)
@@ -250,7 +263,7 @@ extern bool msan_init_is_running;
extern int msan_report_count;
bool ProtectRange(uptr beg, uptr end);
-bool InitShadow(bool init_origins);
+bool InitShadowWithReExec(bool init_origins);
char *GetProcSelfMaps();
void InitializeInterceptors();
diff --git a/compiler-rt/lib/msan/msan_allocator.cpp b/compiler-rt/lib/msan/msan_allocator.cpp
index 0b2dd2b2f1883d..b1bc5b9390f75b 100644
--- a/compiler-rt/lib/msan/msan_allocator.cpp
+++ b/compiler-rt/lib/msan/msan_allocator.cpp
@@ -48,6 +48,9 @@ struct MsanMapUnmapCallback {
}
};
+// Note: to ensure that the allocator is compatible with the application memory
+// layout (especially with high-entropy ASLR), kSpaceBeg and kSpaceSize must be
+// duplicated as MappingDesc::ALLOCATOR in msan.h.
#if defined(__mips64)
static const uptr kMaxAllowedMallocSize = 2UL << 30;
diff --git a/compiler-rt/lib/msan/msan_linux.cpp b/compiler-rt/lib/msan/msan_linux.cpp
index c7ecb7cad56661..cd2d9f5c720c57 100644
--- a/compiler-rt/lib/msan/msan_linux.cpp
+++ b/compiler-rt/lib/msan/msan_linux.cpp
@@ -20,6 +20,9 @@
# include <signal.h>
# include <stdio.h>
# include <stdlib.h>
+# if SANITIZER_LINUX
+# include <sys/personality.h>
+# endif
# include <sys/resource.h>
# include <sys/time.h>
# include <unistd.h>
@@ -43,11 +46,13 @@ void ReportMapRange(const char *descr, uptr beg, uptr size) {
}
}
-static bool CheckMemoryRangeAvailability(uptr beg, uptr size) {
+static bool CheckMemoryRangeAvailability(uptr beg, uptr size, bool verbose) {
if (size > 0) {
uptr end = beg + size - 1;
if (!MemoryRangeIsAvailable(beg, end)) {
- Printf("FATAL: Memory range 0x%zx - 0x%zx is not available.\n", beg, end);
+ if (verbose)
+ Printf("FATAL: Memory range 0x%zx - 0x%zx is not available.\n", beg,
+ end);
return false;
}
}
@@ -86,7 +91,7 @@ static void CheckMemoryLayoutSanity() {
CHECK(addr_is_type(start, type));
CHECK(addr_is_type((start + end) / 2, type));
CHECK(addr_is_type(end - 1, type));
- if (type == MappingDesc::APP) {
+ if (type == MappingDesc::APP || type == MappingDesc::ALLOCATOR) {
uptr addr = start;
CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr)));
CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr)));
@@ -106,7 +111,7 @@ static void CheckMemoryLayoutSanity() {
}
}
-bool InitShadow(bool init_origins) {
+static bool InitShadow(bool init_origins, bool dry_run) {
// Let user know mapping parameters first.
VPrintf(1, "__msan_init %p\n", reinterpret_cast<void *>(&__msan_init));
for (unsigned i = 0; i < kMemoryLayoutSize; ++i)
@@ -116,8 +121,9 @@ bool InitShadow(bool init_origins) {
CheckMemoryLayoutSanity();
if (!MEM_IS_APP(&__msan_init)) {
- Printf("FATAL: Code %p is out of application range. Non-PIE build?\n",
- reinterpret_cast<void *>(&__msan_init));
+ if (!dry_run)
+ Printf("FATAL: Code %p is out of application range. Non-PIE build?\n",
+ reinterpret_cast<void *>(&__msan_init));
return false;
}
@@ -138,20 +144,26 @@ bool InitShadow(bool init_origins) {
bool protect = type == MappingDesc::INVALID ||
(!init_origins && type == MappingDesc::ORIGIN);
CHECK(!(map && protect));
- if (!map && !protect)
- CHECK(type == MappingDesc::APP);
+ if (!map && !protect) {
+ CHECK(type == MappingDesc::APP || type == MappingDesc::ALLOCATOR);
+
+ if (dry_run && type == MappingDesc::ALLOCATOR &&
+ !CheckMemoryRangeAvailability(start, size, !dry_run))
+ return false;
+ }
if (map) {
- if (!CheckMemoryRangeAvailability(start, size))
+ if (dry_run && !CheckMemoryRangeAvailability(start, size, !dry_run))
return false;
- if (!MmapFixedSuperNoReserve(start, size, kMemoryLayout[i].name))
+ if (!dry_run &&
+ !MmapFixedSuperNoReserve(start, size, kMemoryLayout[i].name))
return false;
- if (common_flags()->use_madv_dontdump)
+ if (!dry_run && common_flags()->use_madv_dontdump)
DontDumpShadowMemory(start, size);
}
if (protect) {
- if (!CheckMemoryRangeAvailability(start, size))
+ if (dry_run && !CheckMemoryRangeAvailability(start, size, !dry_run))
return false;
- if (!ProtectMemoryRange(start, size, kMemoryLayout[i].name))
+ if (!dry_run && !ProtectMemoryRange(start, size, kMemoryLayout[i].name))
return false;
}
}
@@ -159,6 +171,35 @@ bool InitShadow(bool init_origins) {
return true;
}
+bool InitShadowWithReExec(bool init_origins) {
+ // Start with dry run: check layout is ok, but don't print warnings because
+ // warning messages will cause tests to fail (even if we successfully re-exec
+ // after the warning).
+ bool success = InitShadow(__msan_get_track_origins(), true);
+ if (!success) {
+# if SANITIZER_LINUX
+ // Perhaps ASLR entropy is too high. If ASLR is enabled, re-exec without it.
+ int old_personality = personality(0xffffffff);
+ bool aslr_on =
+ (old_personality != -1) && ((old_personality & ADDR_NO_RANDOMIZE) == 0);
+
+ if (aslr_on) {
+ VReport(1,
+ "WARNING: MemorySanitizer: memory layout is incompatible, "
+ "possibly due to high-entropy ASLR.\n"
+ "Re-execing with fixed virtual address space.\n"
+ "N.B. reducing ASLR entropy is preferable.\n");
+ CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);
+ ReExec();
+ }
+# endif
+ }
+
+ // The earlier dry run didn't actually map or protect anything. Run again in
+ // non-dry run mode.
+ return success && InitShadow(__msan_get_track_origins(), false);
+}
+
static void MsanAtExit(void) {
if (flags()->print_stats && (flags()->atexit || msan_report_count > 0))
ReportStats();
``````````
</details>
https://github.com/llvm/llvm-project/pull/86201
More information about the llvm-branch-commits
mailing list