Skip to content

Conversation

@honeygoyal
Copy link

If the address range is not covered by shadow memory, make interceptors like mmap fail earlier.

@github-actions
Copy link

github-actions bot commented Dec 9, 2025

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented Dec 9, 2025

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: Honey Goyal (honeygoyal)

Changes

If the address range is not covered by shadow memory, make interceptors like mmap fail earlier.


Full diff: https://github.com/llvm/llvm-project/pull/171295.diff

6 Files Affected:

  • (modified) compiler-rt/lib/asan/asan_errors.cpp (+54)
  • (modified) compiler-rt/lib/asan/asan_errors.h (+66)
  • (modified) compiler-rt/lib/asan/asan_interceptors.cpp (+95-21)
  • (modified) compiler-rt/lib/asan/asan_report.cpp (+30)
  • (modified) compiler-rt/lib/asan/asan_report.h (+5)
  • (added) compiler-rt/test/asan/TestCases/asan_mmap_oob_aix.cpp (+17)
diff --git a/compiler-rt/lib/asan/asan_errors.cpp b/compiler-rt/lib/asan/asan_errors.cpp
index 99d6bdac3d720..6f37e8a995865 100644
--- a/compiler-rt/lib/asan/asan_errors.cpp
+++ b/compiler-rt/lib/asan/asan_errors.cpp
@@ -265,6 +265,60 @@ void ErrorAllocationSizeTooBig::Print() {
   PrintHintAllocatorCannotReturnNull();
   ReportErrorSummary(scariness.GetDescription(), stack);
 }
+void ErrorMmapAddrOverflow::Print() {
+  Decorator d;
+  Printf("%s", d.Error());
+  Report("ERROR: AddressSanitizer: mmap requested memory range (0x%zx + 0x%zx) "
+         "causes address overflow\n", start, length);
+  Printf("%s", d.Default());
+  stack->Print();
+  PrintHintAllocatorCannotReturnNull();
+  ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorMmapShadowOverlap::Print() {
+  Decorator d;
+  Printf("%s", d.Error());
+  Report("ERROR: AddressSanitizer: mmap requested memory range 0x%zx-0x%zx "
+         "overlaps with ASan shadow memory\n", start, end);
+  Printf("%s", d.Default());
+  stack->Print();
+  PrintHintAllocatorCannotReturnNull();
+  ReportErrorSummary(scariness.GetDescription(), stack); 
+}
+
+void ErrorMmapOutsideRange::Print() {
+  Decorator d;
+  Printf("%s", d.Error());
+  Report("ERROR: AddressSanitizer: mmap requested memory range 0x%zx-0x%zx is "
+         "outside ASan's instrumentable application memory range\n", start, end);
+  Printf("%s", d.Default());
+  stack->Print();
+  PrintHintAllocatorCannotReturnNull();
+  ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorMunmapShadowOverlap::Print() {
+  Decorator d;
+  Printf("%s", d.Error());
+  Report("ERROR: AddressSanitizer: munmap requested memory range 0x%zx-0x%zx "
+         "overlaps with ASan shadow memory\n", start, end);
+  Printf("%s", d.Default());
+  stack->Print();
+  PrintHintAllocatorCannotReturnNull();
+  ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorMunmapOutsideRange::Print() {
+  Decorator d;
+  Printf("%s", d.Error());
+  Report("ERROR: AddressSanitizer: munmap requested memory range 0x%zx-0x%zx is "
+         "outside ASan's instrumentable application memory range\n", start, end);
+  Printf("%s", d.Default());
+  stack->Print();
+  PrintHintAllocatorCannotReturnNull();
+  ReportErrorSummary(scariness.GetDescription(), stack);
+}
 
 void ErrorRssLimitExceeded::Print() {
   Decorator d;
diff --git a/compiler-rt/lib/asan/asan_errors.h b/compiler-rt/lib/asan/asan_errors.h
index f339b35d2a764..13e66865a6eb9 100644
--- a/compiler-rt/lib/asan/asan_errors.h
+++ b/compiler-rt/lib/asan/asan_errors.h
@@ -249,6 +249,67 @@ struct ErrorAllocationSizeTooBig : ErrorBase {
   void Print();
 };
 
+struct ErrorMmapAddrOverflow : ErrorBase {
+  const BufferedStackTrace *stack;
+  uptr start;
+  uptr length;
+  ErrorMmapAddrOverflow() = default;
+  ErrorMmapAddrOverflow(u32 tid, BufferedStackTrace *stack_, uptr start_, uptr len)
+      : ErrorBase(tid, 10, "bad-mmap-overflow"), 
+        stack(stack_), 
+        start(start_), 
+        length(len) {}
+  void Print();
+};
+
+struct ErrorMmapShadowOverlap : ErrorBase {
+  const BufferedStackTrace *stack;
+  uptr start, end;
+  ErrorMmapShadowOverlap() = default;
+  ErrorMmapShadowOverlap(u32 tid, BufferedStackTrace *stack_, uptr start_, uptr end_)
+      : ErrorBase(tid, 10, "bad-mmap-overlap"), 
+        stack(stack_), 
+        start(start_), 
+        end(end_) {}
+  void Print();
+};
+
+struct ErrorMmapOutsideRange : ErrorBase {
+  const BufferedStackTrace *stack;
+  uptr start, end;
+  ErrorMmapOutsideRange() = default;
+  ErrorMmapOutsideRange(u32 tid, BufferedStackTrace *stack_, uptr start_, uptr end_)
+      : ErrorBase(tid, 10, "bad-mmap-out-of-range"), 
+        stack(stack_), 
+        start(start_), 
+        end(end_) {}
+  void Print();
+};
+
+struct ErrorMunmapShadowOverlap : ErrorBase {
+  const BufferedStackTrace *stack;
+  uptr start, end;
+  ErrorMunmapShadowOverlap() = default;
+  ErrorMunmapShadowOverlap(u32 tid, BufferedStackTrace *stack_, uptr start_, uptr end_)
+      : ErrorBase(tid, 10, "bad-munmap-overlap"), 
+        stack(stack_), 
+        start(start_), 
+        end(end_) {}
+  void Print();
+};
+
+struct ErrorMunmapOutsideRange : ErrorBase {
+  const BufferedStackTrace *stack;
+  uptr start, end;
+  ErrorMunmapOutsideRange() = default;
+  ErrorMunmapOutsideRange(u32 tid, BufferedStackTrace *stack_, uptr start_, uptr end_)
+      : ErrorBase(tid, 10, "bad-munmap-out-of-range"), 
+        stack(stack_), 
+        start(start_), 
+        end(end_) {}
+  void Print();
+};
+
 struct ErrorRssLimitExceeded : ErrorBase {
   const BufferedStackTrace *stack;
 
@@ -433,6 +494,11 @@ struct ErrorGeneric : ErrorBase {
   macro(InvalidAlignedAllocAlignment)                      \
   macro(InvalidPosixMemalignAlignment)                     \
   macro(AllocationSizeTooBig)                              \
+  macro(MmapAddrOverflow)                                  \
+  macro(MmapShadowOverlap)                                 \
+  macro(MmapOutsideRange)                                  \
+  macro(MunmapShadowOverlap)                               \
+  macro(MunmapOutsideRange)                                \
   macro(RssLimitExceeded)                                  \
   macro(OutOfMemory)                                       \
   macro(StringFunctionMemoryRangesOverlap)                 \
diff --git a/compiler-rt/lib/asan/asan_interceptors.cpp b/compiler-rt/lib/asan/asan_interceptors.cpp
index 8643271e89d70..5b2b135c41123 100644
--- a/compiler-rt/lib/asan/asan_interceptors.cpp
+++ b/compiler-rt/lib/asan/asan_interceptors.cpp
@@ -23,6 +23,7 @@
 #include "asan_suppressions.h"
 #include "asan_thread.h"
 #include "lsan/lsan_common.h"
+#include "sanitizer_common/sanitizer_common.h"
 #include "sanitizer_common/sanitizer_errno.h"
 #include "sanitizer_common/sanitizer_internal_defs.h"
 #include "sanitizer_common/sanitizer_libc.h"
@@ -47,6 +48,8 @@
 #    define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.2"
 #  endif
 
+#define MAP_FIXED 0x0010 /* [MF|SHM] interpret addr exactly */
+
 namespace __asan {
 
 #define ASAN_READ_STRING_OF_LEN(ctx, s, len, n)                 \
@@ -86,7 +89,25 @@ int OnExit() {
   // FIXME: ask frontend whether we need to return failure.
   return 0;
 }
+static inline bool RangeOverlaps(uptr beg, uptr end_excl, uptr seg_beg, uptr seg_end_incl) {
+  if (!seg_beg && !seg_end_incl) return false;
+  uptr seg_end_excl = seg_end_incl + 1;
+  return beg < seg_end_excl && end_excl > seg_beg;
+}
+
+static inline bool IntersectsShadowOrGap(uptr beg, uptr end_excl) {
+  // Check shadow regions
+  if (RangeOverlaps(beg, end_excl, kLowShadowBeg, kLowShadowEnd)) return true;
+  if (kMidShadowBeg && RangeOverlaps(beg, end_excl, kMidShadowBeg, kMidShadowEnd)) return true;
+  if (RangeOverlaps(beg, end_excl, kHighShadowBeg, kHighShadowEnd)) return true;
 
+  // Check shadow gaps
+  if (RangeOverlaps(beg, end_excl, kShadowGapBeg, kShadowGapEnd)) return true;
+  if (kShadowGap2Beg && RangeOverlaps(beg, end_excl, kShadowGap2Beg, kShadowGap2End)) return true;
+  if (kShadowGap3Beg && RangeOverlaps(beg, end_excl, kShadowGap3Beg, kShadowGap3End)) return true;
+
+  return false;
+}
 } // namespace __asan
 
 // ---------------------- Wrappers ---------------- {{{1
@@ -157,45 +178,98 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
     }
 
 template <class Mmap>
-static void* mmap_interceptor(Mmap real_mmap, void *addr, SIZE_T length,
+static void *mmap_interceptor(Mmap real_mmap, void *addr, SIZE_T length,
                               int prot, int flags, int fd, OFF64_T offset) {
+  if (length == 0)
+    return real_mmap(addr, length, prot, flags, fd, offset);
+
+  uptr start = reinterpret_cast<uptr>(addr);
+  uptr end_excl;
+  if (UNLIKELY(__builtin_add_overflow(start, (uptr)length, &end_excl))) {
+    GET_STACK_TRACE_FATAL_HERE;
+    ReportMmapAddrOverflow(start, length, &stack);
+    return (void *)-1;
+  }
+
+  if (flags & MAP_FIXED) {
+    if (__asan::IntersectsShadowOrGap(start, end_excl)) {
+      errno = errno_EINVAL;
+      GET_STACK_TRACE_FATAL_HERE;
+      ReportMmapShadowOverlap(start, end_excl, &stack);      
+      if (common_flags()->abort_on_error) {
+        Abort();
+      }
+      return (void *)-1;
+    }
+    if (!AddrIsInMem(start) || !AddrIsInMem(end_excl - 1)) {
+      errno = errno_ENOMEM;
+      GET_STACK_TRACE_FATAL_HERE;
+      ReportMmapOutsideRange(start, end_excl, &stack);
+      return (void *)-1;
+    }
+  } else {
+    if (addr && __asan::IntersectsShadowOrGap(start, start + 1))
+      addr = nullptr;
+  }
+
   void *res = real_mmap(addr, length, prot, flags, fd, offset);
-  if (length && res != (void *)-1) {
+  if (res != (void *)-1) {
     const uptr beg = reinterpret_cast<uptr>(res);
-    DCHECK(IsAligned(beg, GetPageSize()));
-    SIZE_T rounded_length = RoundUpTo(length, GetPageSize());
-    // Only unpoison shadow if it's an ASAN managed address.
-    if (AddrIsInMem(beg) && AddrIsInMem(beg + rounded_length - 1))
-      PoisonShadow(beg, RoundUpTo(length, GetPageSize()), 0);
+    const uptr page = GetPageSize();
+    const uptr sz = RoundUpTo(length, page);
+    if (AddrIsInMem(beg) && AddrIsInMem(beg + sz - 1)) {
+      PoisonShadow(beg, sz, 0);
+    }
   }
   return res;
 }
 
 template <class Munmap>
 static int munmap_interceptor(Munmap real_munmap, void *addr, SIZE_T length) {
-  // We should not tag if munmap fail, but it's to late to tag after
-  // real_munmap, as the pages could be mmaped by another thread.
+  if (length == 0)
+    return real_munmap(addr, length);
+
   const uptr beg = reinterpret_cast<uptr>(addr);
-  if (length && IsAligned(beg, GetPageSize())) {
-    SIZE_T rounded_length = RoundUpTo(length, GetPageSize());
-    // Protect from unmapping the shadow.
-    if (AddrIsInMem(beg) && AddrIsInMem(beg + rounded_length - 1))
-      PoisonShadow(beg, rounded_length, 0);
+  uptr end_excl;
+  if (UNLIKELY(__builtin_add_overflow(beg, (uptr)length, &end_excl))) {
+    errno = errno_EINVAL;
+    GET_STACK_TRACE_FATAL_HERE;
+    ReportMunmapShadowOverlap(beg, end_excl, &stack);
+    return -1;
+  }
+
+  if ((AddrIsInMem(beg) || AddrIsInMem(end_excl - 1)) &&
+      (!AddrIsInMem(beg) || !AddrIsInMem(end_excl - 1))) {
+    errno = errno_EINVAL;
+    GET_STACK_TRACE_FATAL_HERE;
+    ReportMunmapOutsideRange(beg, end_excl, &stack);
+    return -1;
+  }
+
+  int res = real_munmap(addr, length);
+
+  if (res == 0) {
+    const uptr page = GetPageSize();
+    const uptr aligned_beg = RoundDownTo(beg, page);
+    const uptr aligned_end = RoundUpTo(end_excl, page);
+    if (AddrIsInMem(aligned_beg) && AddrIsInMem(aligned_end - 1)) {
+      PoisonShadow(aligned_beg, aligned_end - aligned_beg, 0);
+    }
   }
-  return real_munmap(addr, length);
+  return res;
 }
 
-#  define COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, length, prot, flags,   \
-                                     fd, offset)                               \
-  do {                                                                         \
-    (void)(ctx);                                                               \
-    return mmap_interceptor(REAL(mmap), addr, sz, prot, flags, fd, off);       \
+#  define COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, length, prot, flags,    \
+                                     fd, offset)                                \
+  do {                                                                          \
+    (void)(ctx);                                                                \
+    return mmap_interceptor(REAL(mmap), addr, length, prot, flags, fd, offset); \
   } while (false)
 
 #  define COMMON_INTERCEPTOR_MUNMAP_IMPL(ctx, addr, length)                    \
   do {                                                                         \
     (void)(ctx);                                                               \
-    return munmap_interceptor(REAL(munmap), addr, sz);                         \
+    return munmap_interceptor(REAL(munmap), addr, length);                     \
   } while (false)
 
 #if CAN_SANITIZE_LEAKS
diff --git a/compiler-rt/lib/asan/asan_report.cpp b/compiler-rt/lib/asan/asan_report.cpp
index e049a21e4e16d..fbefc63b4dace 100644
--- a/compiler-rt/lib/asan/asan_report.cpp
+++ b/compiler-rt/lib/asan/asan_report.cpp
@@ -343,6 +343,36 @@ void ReportAllocationSizeTooBig(uptr user_size, uptr total_size, uptr max_size,
   in_report.ReportError(error);
 }
 
+void ReportMmapAddrOverflow(uptr start, uptr length, BufferedStackTrace *stack) {
+  ScopedInErrorReport in_report(/*fatal*/ true);
+  ErrorMmapAddrOverflow error(GetCurrentTidOrInvalid(), stack, start, length);
+  in_report.ReportError(error);
+}
+
+void ReportMmapShadowOverlap(uptr start, uptr end, BufferedStackTrace *stack) {
+  ScopedInErrorReport in_report(/*fatal*/ true);
+  ErrorMmapShadowOverlap error(GetCurrentTidOrInvalid(), stack, start, end);
+  in_report.ReportError(error);
+}
+
+void ReportMmapOutsideRange(uptr start, uptr end, BufferedStackTrace *stack) {
+  ScopedInErrorReport in_report(/*fatal*/ true);
+  ErrorMmapOutsideRange error(GetCurrentTidOrInvalid(), stack, start, end);
+  in_report.ReportError(error);
+}
+
+void ReportMunmapShadowOverlap(uptr start, uptr end, BufferedStackTrace *stack) {
+  ScopedInErrorReport in_report(/*fatal*/ true);
+  ErrorMunmapShadowOverlap error(GetCurrentTidOrInvalid(), stack, start, end);
+  in_report.ReportError(error);
+}
+
+void ReportMunmapOutsideRange(uptr start, uptr end, BufferedStackTrace *stack) {
+  ScopedInErrorReport in_report(/*fatal*/ true);
+  ErrorMunmapOutsideRange error(GetCurrentTidOrInvalid(), stack, start, end);
+  in_report.ReportError(error);
+}
+
 void ReportRssLimitExceeded(BufferedStackTrace *stack) {
   ScopedInErrorReport in_report(/*fatal*/ true);
   ErrorRssLimitExceeded error(GetCurrentTidOrInvalid(), stack);
diff --git a/compiler-rt/lib/asan/asan_report.h b/compiler-rt/lib/asan/asan_report.h
index 3143d83abe390..e7f653cf6c3f6 100644
--- a/compiler-rt/lib/asan/asan_report.h
+++ b/compiler-rt/lib/asan/asan_report.h
@@ -73,6 +73,11 @@ void ReportInvalidPosixMemalignAlignment(uptr alignment,
                                          BufferedStackTrace *stack);
 void ReportAllocationSizeTooBig(uptr user_size, uptr total_size, uptr max_size,
                                 BufferedStackTrace *stack);
+void ReportMmapAddrOverflow(uptr start, uptr length, BufferedStackTrace *stack);
+void ReportMmapShadowOverlap(uptr start, uptr end, BufferedStackTrace *stack);
+void ReportMmapOutsideRange(uptr start, uptr end, BufferedStackTrace *stack);
+void ReportMunmapShadowOverlap(uptr start, uptr end, BufferedStackTrace *stack);
+void ReportMunmapOutsideRange(uptr start, uptr end, BufferedStackTrace *stack);                                
 void ReportRssLimitExceeded(BufferedStackTrace *stack);
 void ReportOutOfMemory(uptr requested_size, BufferedStackTrace *stack);
 void ReportStringFunctionMemoryRangesOverlap(const char *function,
diff --git a/compiler-rt/test/asan/TestCases/asan_mmap_oob_aix.cpp b/compiler-rt/test/asan/TestCases/asan_mmap_oob_aix.cpp
new file mode 100644
index 0000000000000..e23e18e5e005d
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/asan_mmap_oob_aix.cpp
@@ -0,0 +1,17 @@
+// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// REQUIRES: system-aix
+#include <sys/mman.h>
+
+#define ASAN_AIX_SHADOW_OFFSET 0x0a01000000000000ULL
+
+int main() {
+    size_t map_size = 4096;
+    void* addr = (void*)ASAN_AIX_SHADOW_OFFSET;
+    void* ptr = mmap(addr, map_size, PROT_READ | PROT_WRITE,
+                     MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+    if (ptr != MAP_FAILED) munmap(ptr, map_size);
+    return 0;
+}
+
+// CHECK: ERROR: AddressSanitizer: mmap requested memory range
+// CHECK: overlaps with ASan shadow memory
\ No newline at end of file

Copy link
Contributor

@thurstond thurstond left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#169775 had a better pull request title (it mentions mmap-munmap, which is the focus of this change)

Same overall question: Is the error-reporting necessary? If ASan can't honor a fixed memory mapping for whatever reason, it suffices to return MAP_FAILED (this is what HWASan's interceptor does); the shadow is an implementation detail that the user doesn't need to know about.

To elaborate: a sanitizer report usually means the sanitizer found a bug in the user's code. But if the mmap interceptor fails here because the user tried to map over the shadow, that's not really a bug in the user's code. It's bad luck, or arguably a bug in the sanitizer.

# define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.2"
# endif

#define MAP_FIXED 0x0010 /* [MF|SHM] interpret addr exactly */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

map_fixed (in lowercase) is defined in compiler-rt/lib/sanitizer_common/sanitizer_platform_limits_posix.cpp for this purpose (example usage: compiler-rt/lib/hwasan/hwasan_interceptors.cpp).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants