[llvm-branch-commits] [compiler-rt] [ASan][Darwin] Support gapless shadow layout for iOS 27.0 (PR #217530)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Aug 19 22:14:26 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-compiler-rt-sanitizer
Author: Andrew Haberlandt (ndrewh)
<details>
<summary>Changes</summary>
This is the second PR in a series that upstreams support for iOS 27.0 (see #<!-- -->217527 for the first).
When the shadow can be placed entirely above app memory (as on iOS 27), there is no need to split shadow into low/high halves with a middle gap. This PR adopts a "gapless" layout in ASAN, in which all of application memory is in `[kLowMemBeg, kLowMemEnd]` and shadow is `[kLowShadowBeg, kLowShadowEnd]`. The "high" region is unused (because of the way it is defined, `kHighMemBeg > kHighMemEnd` in this new layout, which conveniently means that `AddrIsInHighMem(p)` is always false) -- this is a bit jank and unintuitive, but it keeps the diff between iOS and other platforms relatively small.
- Add kGaplessShadow (Darwin-only) to detect this configuration.
- Teach `InitializeShadowMemory` to reserve one contiguous shadow region and protect only the shadow-of-shadow when kGaplessShadow is true
- Update PrintAddressSpaceLayout to print the single-region layout.
rdar://167657399
---
Full diff: https://github.com/llvm/llvm-project/pull/217530.diff
3 Files Affected:
- (modified) compiler-rt/lib/asan/asan_mapping.h (+9)
- (modified) compiler-rt/lib/asan/asan_rtl.cpp (+41-35)
- (modified) compiler-rt/lib/asan/asan_shadow_setup.cpp (+39-1)
``````````diff
diff --git a/compiler-rt/lib/asan/asan_mapping.h b/compiler-rt/lib/asan/asan_mapping.h
index 406fcaba5692f..f514b6a4d207a 100644
--- a/compiler-rt/lib/asan/asan_mapping.h
+++ b/compiler-rt/lib/asan/asan_mapping.h
@@ -314,6 +314,15 @@ extern uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; // Initialized in __asan_init.
# define kMidShadowBeg MEM_TO_SHADOW(kMidMemBeg)
# define kMidShadowEnd MEM_TO_SHADOW(kMidMemEnd)
+// If the first byte of shadow can be placed after the last byte of app mem,
+// we don't need a gap since the shadow's shadow won't be in the middle
+// of app mem.
+# if SANITIZER_APPLE
+# define kGaplessShadow (kLowShadowBeg > kHighMemEnd)
+# else
+# define kGaplessShadow (false)
+# endif
+
// With the zero shadow base we can not actually map pages starting from 0.
// This constant is somewhat arbitrary.
# define kZeroBaseShadowStart 0
diff --git a/compiler-rt/lib/asan/asan_rtl.cpp b/compiler-rt/lib/asan/asan_rtl.cpp
index c036a13a11029..2467657274a73 100644
--- a/compiler-rt/lib/asan/asan_rtl.cpp
+++ b/compiler-rt/lib/asan/asan_rtl.cpp
@@ -334,43 +334,49 @@ static void InitializeHighMemEnd() {
}
void PrintAddressSpaceLayout() {
- if (kHighMemBeg) {
- Printf("|| `[%p, %p]` || HighMem ||\n",
- (void*)kHighMemBeg, (void*)kHighMemEnd);
- Printf("|| `[%p, %p]` || HighShadow ||\n",
- (void*)kHighShadowBeg, (void*)kHighShadowEnd);
- }
- if (kMidMemBeg) {
- Printf("|| `[%p, %p]` || ShadowGap3 ||\n",
- (void*)kShadowGap3Beg, (void*)kShadowGap3End);
- Printf("|| `[%p, %p]` || MidMem ||\n",
- (void*)kMidMemBeg, (void*)kMidMemEnd);
- Printf("|| `[%p, %p]` || ShadowGap2 ||\n",
- (void*)kShadowGap2Beg, (void*)kShadowGap2End);
- Printf("|| `[%p, %p]` || MidShadow ||\n",
- (void*)kMidShadowBeg, (void*)kMidShadowEnd);
- }
- Printf("|| `[%p, %p]` || ShadowGap ||\n",
- (void*)kShadowGapBeg, (void*)kShadowGapEnd);
- if (kLowShadowBeg) {
- Printf("|| `[%p, %p]` || LowShadow ||\n",
- (void*)kLowShadowBeg, (void*)kLowShadowEnd);
- Printf("|| `[%p, %p]` || LowMem ||\n",
- (void*)kLowMemBeg, (void*)kLowMemEnd);
- }
- Printf("MemToShadow(shadow): %p %p",
- (void*)MEM_TO_SHADOW(kLowShadowBeg),
- (void*)MEM_TO_SHADOW(kLowShadowEnd));
- if (kHighMemBeg) {
- Printf(" %p %p",
- (void*)MEM_TO_SHADOW(kHighShadowBeg),
+ if (!kGaplessShadow) {
+ if (kHighMemBeg) {
+ Printf("|| `[%p, %p]` || HighMem ||\n", (void*)kHighMemBeg,
+ (void*)kHighMemEnd);
+ Printf("|| `[%p, %p]` || HighShadow ||\n", (void*)kHighShadowBeg,
+ (void*)kHighShadowEnd);
+ }
+ if (kMidMemBeg) {
+ Printf("|| `[%p, %p]` || ShadowGap3 ||\n", (void*)kShadowGap3Beg,
+ (void*)kShadowGap3End);
+ Printf("|| `[%p, %p]` || MidMem ||\n", (void*)kMidMemBeg,
+ (void*)kMidMemEnd);
+ Printf("|| `[%p, %p]` || ShadowGap2 ||\n", (void*)kShadowGap2Beg,
+ (void*)kShadowGap2End);
+ Printf("|| `[%p, %p]` || MidShadow ||\n", (void*)kMidShadowBeg,
+ (void*)kMidShadowEnd);
+ }
+ Printf("|| `[%p, %p]` || ShadowGap ||\n", (void*)kShadowGapBeg,
+ (void*)kShadowGapEnd);
+ if (kLowShadowBeg) {
+ Printf("|| `[%p, %p]` || LowShadow ||\n", (void*)kLowShadowBeg,
+ (void*)kLowShadowEnd);
+ Printf("|| `[%p, %p]` || LowMem ||\n", (void*)kLowMemBeg,
+ (void*)kLowMemEnd);
+ }
+ Printf("MemToShadow(shadow): %p %p", (void*)MEM_TO_SHADOW(kLowShadowBeg),
+ (void*)MEM_TO_SHADOW(kLowShadowEnd));
+ if (kHighMemBeg) {
+ Printf(" %p %p", (void*)MEM_TO_SHADOW(kHighShadowBeg),
+ (void*)MEM_TO_SHADOW(kHighShadowEnd));
+ }
+ if (kMidMemBeg) {
+ Printf(" %p %p", (void*)MEM_TO_SHADOW(kMidShadowBeg),
+ (void*)MEM_TO_SHADOW(kMidShadowEnd));
+ }
+ } else {
+ Printf("|| `[%p, %p]` || Shadow ||\n", (void*)kLowShadowBeg,
+ (void*)kHighShadowEnd);
+ Printf("|| `[%p, %p]` || Mem ||\n", (void*)kLowMemBeg,
+ (void*)kHighMemEnd);
+ Printf("MemToShadow(shadow): %p %p", (void*)MEM_TO_SHADOW(kLowShadowBeg),
(void*)MEM_TO_SHADOW(kHighShadowEnd));
}
- if (kMidMemBeg) {
- Printf(" %p %p",
- (void*)MEM_TO_SHADOW(kMidShadowBeg),
- (void*)MEM_TO_SHADOW(kMidShadowEnd));
- }
Printf("\n");
Printf("redzone=%zu\n", (uptr)flags()->redzone);
Printf("max_redzone=%zu\n", (uptr)flags()->max_redzone);
diff --git a/compiler-rt/lib/asan/asan_shadow_setup.cpp b/compiler-rt/lib/asan/asan_shadow_setup.cpp
index de3fb3910c56e..167da2c0251df 100644
--- a/compiler-rt/lib/asan/asan_shadow_setup.cpp
+++ b/compiler-rt/lib/asan/asan_shadow_setup.cpp
@@ -38,6 +38,7 @@ static void ProtectGap(uptr addr, uptr size) {
"unprotected gap shadow");
return;
}
+ VReport(2, "ProtectGap %p sz=%p\n", (void*)addr, (void*)size);
__sanitizer::ProtectGap(addr, size, kZeroBaseShadowStart,
kZeroBaseMaxShadowStart);
}
@@ -85,7 +86,44 @@ void InitializeShadowMemory() {
if (Verbosity()) PrintAddressSpaceLayout();
- if (full_shadow_is_available) {
+ if (full_shadow_is_available && kGaplessShadow) {
+ // Normally, the shadow memory overlaps with the memory mappable
+ // by the application, so we split shadow into "low" and "high"
+ // with a protected gap in the middle (the shadow of the shadow).
+ //
+ // However, on some platforms, we can map the shadow above
+ // the space normally addressable by the application. On these
+ // platforms, we do not need a gap.
+
+ // In the "gapless" configuration, there is only one shadow mapping
+ // which covers all app memory i.e. from kLowMemBeg to kHighMemEnd.
+ ReserveShadowMemoryRange(shadow_start, kHighShadowEnd, "shadow");
+
+ // kLowShadowEnd, kHighShadowBeg are defined assuming there is a gap,
+ // and this affects calls such as AddrIsInLowMem and AddrIsInHighMem.
+ //
+ // We want all of application memory to be in the "low mem" region and all
+ // of the shadow to be in the "low shadow" region. However, kLowMemEnd
+ // is defined differently in terms of the shadow base, which is always above
+ // the actual app mem max (i.e. >4TB, kHighMemEnd). This means
+ // (kLowMemBeg, kLowMemEnd) is a slight over-approximation of the low app
+ // memory. However, it's still good enough for us because it includes
+ // all app memory and no shadow memory, which we assert here.
+ CHECK_GE(kLowMemEnd, kHighMemEnd);
+ CHECK_LT(kLowMemEnd, kLowShadowBeg);
+ CHECK_GE(kLowShadowEnd, kHighShadowEnd);
+
+ // We don't use the "high mem" region, so we expect beg > end, to ensure
+ // that AddrIsInHighMem/AddrIsInHighShadow always fails.
+ CHECK_GT(kHighMemBeg, kHighMemEnd);
+ CHECK_GT(kHighShadowBeg, kHighShadowEnd);
+
+ // The shadow of the shadow may still technically be mappable by the
+ // sanitizers or other tools, so we protect it here just to be safe.
+ ProtectGap(
+ MEM_TO_SHADOW(kLowShadowBeg),
+ MEM_TO_SHADOW(kHighShadowEnd) - MEM_TO_SHADOW(kLowShadowBeg) + 1);
+ } else if (full_shadow_is_available) {
// mmap the low shadow plus at least one page at the left.
if (kLowShadowBeg)
ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
``````````
</details>
https://github.com/llvm/llvm-project/pull/217530
More information about the llvm-branch-commits
mailing list