Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions compiler-rt/lib/asan/asan_errors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

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());
Expand Down
66 changes: 66 additions & 0 deletions compiler-rt/lib/asan/asan_errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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) \
Expand Down
115 changes: 97 additions & 18 deletions compiler-rt/lib/asan/asan_interceptors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -47,6 +47,12 @@
# define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.2"
# endif

#if !defined(MAP_FAILED)
# define MAP_FAILED ((void *)-1)
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).

#endif

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

namespace __asan {

#define ASAN_READ_STRING_OF_LEN(ctx, s, len, n) \
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -157,45 +183,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 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<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;
}
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
Expand Down
30 changes: 30 additions & 0 deletions compiler-rt/lib/asan/asan_report.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions compiler-rt/lib/asan/asan_report.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
17 changes: 17 additions & 0 deletions compiler-rt/test/asan/TestCases/asan_mmap_oob_aix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s

Copy link
Contributor

Choose a reason for hiding this comment

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

This test needs something like // REQUIRES: system-aix, otherwise it will fail on non-AIX systems.

#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
Loading