[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