[compiler-rt] Make interceptors fail earlier (PR #171295)

Honey Goyal via llvm-commits llvm-commits at lists.llvm.org
Tue Dec 9 00:28:51 PST 2025


https://github.com/honeygoyal created https://github.com/llvm/llvm-project/pull/171295

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

>From 7119e113a203d1b74a66c4d045443272df273f1f Mon Sep 17 00:00:00 2001
From: Honey Goyal <honey.goyal3 at ibm.com>
Date: Tue, 9 Dec 2025 08:34:04 +0000
Subject: [PATCH] Implement mmap fail earlier

---
 compiler-rt/lib/asan/asan_errors.cpp          |  54 ++++++++
 compiler-rt/lib/asan/asan_errors.h            |  66 ++++++++++
 compiler-rt/lib/asan/asan_interceptors.cpp    | 116 ++++++++++++++----
 compiler-rt/lib/asan/asan_report.cpp          |  30 +++++
 compiler-rt/lib/asan/asan_report.h            |   5 +
 .../test/asan/TestCases/asan_mmap_oob_aix.cpp |  17 +++
 6 files changed, 267 insertions(+), 21 deletions(-)
 create mode 100644 compiler-rt/test/asan/TestCases/asan_mmap_oob_aix.cpp

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



More information about the llvm-commits mailing list