diff --git a/compiler-rt/lib/asan/asan_errors.cpp b/compiler-rt/lib/asan/asan_errors.cpp index 99d6bdac3d720..2a949a14b2967 100644 --- a/compiler-rt/lib/asan/asan_errors.cpp +++ b/compiler-rt/lib/asan/asan_errors.cpp @@ -266,6 +266,61 @@ void ErrorAllocationSizeTooBig::Print() { 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; Printf("%s", d.Error()); 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..5cd586305157e 100644 --- a/compiler-rt/lib/asan/asan_interceptors.cpp +++ b/compiler-rt/lib/asan/asan_interceptors.cpp @@ -12,7 +12,7 @@ //===----------------------------------------------------------------------===// #include "asan_interceptors.h" - +#include "sanitizer_common/sanitizer_common.h" #include "asan_allocator.h" #include "asan_internal.h" #include "asan_mapping.h" @@ -47,6 +47,12 @@ # define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.2" # endif +#if !defined(MAP_FAILED) +# define MAP_FAILED ((void *)-1) +#endif + +#define MAP_FIXED 0x0010 /* [MF|SHM] interpret addr exactly */ + namespace __asan { #define ASAN_READ_STRING_OF_LEN(ctx, s, len, n) \ @@ -87,6 +93,26 @@ int OnExit() { 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 +183,98 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) } template -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(addr); + uptr end_excl; + if (UNLIKELY(__builtin_add_overflow(start, (uptr)length, &end_excl))) { + GET_STACK_TRACE_FATAL_HERE; + ReportMmapAddrOverflow(start, length, &stack); + return MAP_FAILED; + } + + 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 MAP_FAILED; + } + if (!AddrIsInMem(start) || !AddrIsInMem(end_excl - 1)) { + errno = errno_ENOMEM; + GET_STACK_TRACE_FATAL_HERE; + ReportMmapOutsideRange(start, end_excl, &stack); + return MAP_FAILED; + } + } 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 != MAP_FAILED) { const uptr beg = reinterpret_cast(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 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(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; } - return real_munmap(addr, length); + + 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 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); \ + 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..5833159f09ab2 --- /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 + +#include + +#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