diff --git a/compiler-rt/lib/asan/asan_mapping.h b/compiler-rt/lib/asan/asan_mapping.h index 91fe60db6329a..ecd196e2a021e 100644 --- a/compiler-rt/lib/asan/asan_mapping.h +++ b/compiler-rt/lib/asan/asan_mapping.h @@ -178,6 +178,8 @@ # define ASAN_SHADOW_OFFSET_CONST 0x30000000 # elif SANITIZER_IOS # define ASAN_SHADOW_OFFSET_DYNAMIC +# elif SANITIZER_AIX +# define ASAN_SHADOW_OFFSET_CONST 0x40000000 # else # define ASAN_SHADOW_OFFSET_CONST 0x20000000 # endif @@ -193,7 +195,11 @@ # elif defined(__aarch64__) # define ASAN_SHADOW_OFFSET_CONST 0x0000001000000000 # elif defined(__powerpc64__) -# define ASAN_SHADOW_OFFSET_CONST 0x0000100000000000 +# if SANITIZER_AIX +# define ASAN_SHADOW_OFFSET_CONST 0x0a01000000000000 +# else +# define ASAN_SHADOW_OFFSET_CONST 0x0000100000000000 +# endif # elif defined(__s390x__) # define ASAN_SHADOW_OFFSET_CONST 0x0010000000000000 # elif SANITIZER_FREEBSD @@ -272,6 +278,8 @@ extern uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; // Initialized in __asan_init. # if defined(__sparc__) && SANITIZER_WORDSIZE == 64 # include "asan_mapping_sparc64.h" +# elif SANITIZER_WORDSIZE == 64 && SANITIZER_AIX +# include "asan_mapping_aix64.h" # else # define MEM_TO_SHADOW(mem) \ (((mem) >> ASAN_SHADOW_SCALE) + (ASAN_SHADOW_OFFSET)) diff --git a/compiler-rt/lib/asan/asan_mapping_aix64.h b/compiler-rt/lib/asan/asan_mapping_aix64.h new file mode 100644 index 0000000000000..38b97482ec1f4 --- /dev/null +++ b/compiler-rt/lib/asan/asan_mapping_aix64.h @@ -0,0 +1,184 @@ +//===-- asan_mapping_aix64.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// AIX64-specific definitions for ASan memory mapping. +//===----------------------------------------------------------------------===// +#ifndef ASAN_MAPPING_AIX64_H +#define ASAN_MAPPING_AIX64_H + +// https://www.ibm.com/docs/en/aix/7.3?topic=concepts-system-memory-allocation-using-malloc-subsystem +// +// For 64-bit on AIX, +// - Data, heap, bss region is from 0x0000 0001 0000 0000 to +// 0x07ff ffff ffff ffff (1ULL << 59). +// - Shared library regions is from: +// 0x0900 0000 0000 0000 to 0x09ff ffff ffff ffff +// or 0x0800 0000 0000 0000 to 0x08ff ffff ffff ffff ((1ULL << 52) * 2) +// - mmap region is from 0x0a00 0000 0000 0000 to 0x0aff ffff ffff ffff +// (1ULL << 52). +// - Initial stack region is from 0x0f00 0000 0000 0000 to +// 0x0fff ffff ffff ffff (1ULL << 56). +// +// All above ranges are too big. And after verifying on AIX,(these datas are +// from experiments on AIX72, AIX OS may change this layout in future) +// - the biggest heap size is 1ULL << 47. +// - the biggest global variable size is 1ULL << 29. (Which may be put in shared +// library data regions because global variables may be compiled to shared +// libraries.) +// the related address range for shared library data regions is: +// 0x0900 1000 0000 0000 to 0x0900 1001 0000 0000 +// or 0x0800 1000 0000 0000 to 0x0800 1001 0000 0000 (when above range is +// used by system libraries.) +// - the biggest mmap size is 1ULL << 46. +// - the biggest stack size is 1ULL << 32. +// +// We don't need so big heap and mmap, calling mmap for shadow memory for such +// big heap and mmap is quite slow on AIX, so to balance runtime and examinable +// memory size, we use 1ULL << 39(512GB) as size for each region except mmap +// region. For mmap region, aix system mmap function may return a big range +// address, we allocate 1ULL << 41(2TB). +// +// So the reasonable user space region size is: +// - Data, heap, bss is from 0x0 to 0x0000 007f ffff ffff +// - Shared library data is from: +// 0x0900 1000 0000 0000 to 0x0900 107f ffff ffff +// or 0x0800 1000 0000 0000 to 0x0800 107f ffff ffff +// - mmap is from 0x0a00 0000 0000 0000 to 0x0a00 01ff ffff ffff +// - Stack is from 0x0fff ff80 0000 0000 to 0x0fff ffff ffff ffff +// +// AIX64 set ASAN_SHADOW_OFFSET_CONST at 0x0a01000000000000 because mmap +// memory starts at 0x0a00000000000000 and shadow memory should be allocated +// there. And we keep 0x0a00000000000000 to 0x0a01000000000000 For user mmap +// usage. + +// NOTE: Users are not expected to use `mmap` specifying fixed address which is +// inside the shadow memory ranges. + +// Default AIX64 mapping: +// || `[0x0fffff8000000000, 0x0fffffffffffffff]` || HighMem || +// || `[0x0a80fff000000000, 0x0a80ffffffffffff]` || HighShadow || +// || `[0x0a41000000000000, 0x0a41003fffffffff]` || MidShadow || +// || `[0x0a21020000000000, 0x0a21020fffffffff]` || Mid2Shadow || +// || `[0x0a01020000000000, 0x0a01020fffffffff]` || Mid3Shadow || +// || `[0x0a01000000000000, 0x0a01000fffffffff]` || LowShadow || +// || `[0x0a00000000000000, 0x0a0001ffffffffff]` || MidMem || +// || `[0x0900100000000000, 0x0900107fffffffff]` || Mid2Mem || +// || `[0x0800100000000000, 0x0800107fffffffff]` || Mid3Mem || +// || `[0x0000000000000000, 0x0000007fffffffff]` || LowMem || + +#define VMA_BITS 58 +#define HIGH_BITS (64 - VMA_BITS) + +#define MEM_TO_SHADOW(mem) \ + ((((mem) << HIGH_BITS) >> (HIGH_BITS + (ASAN_SHADOW_SCALE))) + \ + ASAN_SHADOW_OFFSET) + +#define SHADOW_TO_MEM(ptr) (__asan::ShadowToMemAIX64(ptr)) + +#define kLowMemBeg 0ULL +#define kLowMemEnd 0x0000007fffffffffULL + +#define kLowShadowBeg ASAN_SHADOW_OFFSET +#define kLowShadowEnd MEM_TO_SHADOW(kLowMemEnd) + +#define kHighMemBeg 0x0fffff8000000000ULL + +#define kHighShadowBeg MEM_TO_SHADOW(kHighMemBeg) +#define kHighShadowEnd MEM_TO_SHADOW(kHighMemEnd) + +#define kMidMemBeg 0x0a00000000000000ULL +#define kMidMemEnd 0x0a0001ffffffffffULL + +#define kMidShadowBeg MEM_TO_SHADOW(kMidMemBeg) +#define kMidShadowEnd MEM_TO_SHADOW(kMidMemEnd) + +#define kMid2MemBeg 0x0900100000000000ULL +#define kMid2MemEnd 0x0900107fffffffffULL + +#define kMid2ShadowBeg MEM_TO_SHADOW(kMid2MemBeg) +#define kMid2ShadowEnd MEM_TO_SHADOW(kMid2MemEnd) + +#define kMid3MemBeg 0x0800100000000000ULL +#define kMid3MemEnd 0x0800107fffffffffULL + +#define kMid3ShadowBeg MEM_TO_SHADOW(kMid3MemBeg) +#define kMid3ShadowEnd MEM_TO_SHADOW(kMid3MemEnd) + +// AIX does not care about the gaps. +#define kZeroBaseShadowStart 0 +#define kZeroBaseMaxShadowStart 0 + +#define kShadowGapBeg 0 +#define kShadowGapEnd 0 + +#define kShadowGap2Beg 0 +#define kShadowGap2End 0 + +#define kShadowGap3Beg 0 +#define kShadowGap3End 0 + +#define kShadowGap4Beg 0 +#define kShadowGap4End 0 + +namespace __asan { + +static inline bool AddrIsInLowMem(uptr a) { + PROFILE_ASAN_MAPPING(); + return a <= kLowMemEnd; +} + +static inline bool AddrIsInLowShadow(uptr a) { + PROFILE_ASAN_MAPPING(); + return a >= kLowShadowBeg && a <= kLowShadowEnd; +} + +static inline bool AddrIsInMidMem(uptr a) { + PROFILE_ASAN_MAPPING(); + return (a >= kMidMemBeg && a <= kMidMemEnd) || + (a >= kMid2MemBeg && a <= kMid2MemEnd) || + (a >= kMid3MemBeg && a <= kMid3MemEnd); +} + +static inline bool AddrIsInMidShadow(uptr a) { + PROFILE_ASAN_MAPPING(); + return (a >= kMidShadowBeg && a <= kMidShadowEnd) || + (a >= kMid2ShadowBeg && a <= kMid2ShadowEnd) || + (a >= kMid3ShadowBeg && a <= kMid3ShadowEnd); +} + +static inline bool AddrIsInHighMem(uptr a) { + PROFILE_ASAN_MAPPING(); + return kHighMemBeg && a >= kHighMemBeg && a <= kHighMemEnd; +} + +static inline bool AddrIsInHighShadow(uptr a) { + PROFILE_ASAN_MAPPING(); + return kHighMemBeg && a >= kHighShadowBeg && a <= kHighShadowEnd; +} + +static inline bool AddrIsInShadowGap(uptr a) { return false; } + +static inline constexpr uptr ShadowToMemAIX64(uptr p) { + PROFILE_ASAN_MAPPING(); + p -= ASAN_SHADOW_OFFSET; + p <<= ASAN_SHADOW_SCALE; + if (p >= 0x3ffff8000000000ULL) { + // HighMem + p |= (0x03ULL << VMA_BITS); + } else if (p >= 0x100000000000ULL) { + // MidShadow/Mid2Shadow/Mid2Shadow + p |= (0x02ULL << VMA_BITS); + } + return p; +} + +} // namespace __asan + +#endif // ASAN_MAPPING_AIX64_H diff --git a/compiler-rt/lib/asan/asan_poisoning.h b/compiler-rt/lib/asan/asan_poisoning.h index b68af1086e17d..d53bca9d34f29 100644 --- a/compiler-rt/lib/asan/asan_poisoning.h +++ b/compiler-rt/lib/asan/asan_poisoning.h @@ -53,7 +53,9 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, // for mapping shadow and zeroing out pages doesn't "just work", so we should // probably provide higher-level interface for these operations. // For now, just memset on Windows. - if (value || SANITIZER_WINDOWS == 1 || + // On AIX, calling ReserveShadowMemoryRange() is not allowed to remap the + // memory, so just memset the memory. + if (value || SANITIZER_WINDOWS || SANITIZER_AIX || shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) { REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); } else { diff --git a/compiler-rt/lib/asan/asan_rtl.cpp b/compiler-rt/lib/asan/asan_rtl.cpp index b3f6677a99cfb..90072cfbfa260 100644 --- a/compiler-rt/lib/asan/asan_rtl.cpp +++ b/compiler-rt/lib/asan/asan_rtl.cpp @@ -55,13 +55,23 @@ static void AsanDie() { WaitForDebugger(flags()->sleep_before_dying, "before dying"); if (flags()->unmap_shadow_on_exit) { +#if SANITIZER_AIX && SANITIZER_WORDSIZE == 64 + UnmapOrDie((void *)kHighShadowBeg, kHighShadowEnd - kHighShadowBeg); + UnmapOrDie((void *)kMidShadowBeg, kMidShadowEnd - kMidShadowBeg); + + UnmapOrDie((void *)kMid2ShadowBeg, kMid2ShadowEnd - kMid2ShadowBeg); + UnmapOrDie((void *)kMid3ShadowBeg, kMid3ShadowEnd - kMid3ShadowBeg); + + UnmapOrDie((void *)kLowShadowBeg, kLowShadowEnd - kLowShadowBeg); +#else if (kMidMemBeg) { - UnmapOrDie((void*)kLowShadowBeg, kMidMemBeg - kLowShadowBeg); - UnmapOrDie((void*)kMidMemEnd, kHighShadowEnd - kMidMemEnd); + UnmapOrDie((void *)kLowShadowBeg, kMidMemBeg - kLowShadowBeg); + UnmapOrDie((void *)kMidMemEnd, kHighShadowEnd - kMidMemEnd); } else { if (kHighShadowEnd) - UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg); + UnmapOrDie((void *)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg); } +#endif } } @@ -85,7 +95,11 @@ bool AsanInited() { bool replace_intrin_cached; #if !ASAN_FIXED_MAPPING +# if !(SANITIZER_AIX && __powerpc64__) uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; +# else +uptr kHighMemEnd; +# endif #endif // -------------------------- Misc ---------------- {{{1 @@ -341,17 +355,29 @@ void PrintAddressSpaceLayout() { (void*)kHighShadowBeg, (void*)kHighShadowEnd); } if (kMidMemBeg) { + // AIX shadowgap is always set to 0 for 64-bit. +#if !SANITIZER_AIX || SANITIZER_WORDSIZE != 64 Printf("|| `[%p, %p]` || ShadowGap3 ||\n", (void*)kShadowGap3Beg, (void*)kShadowGap3End); +#endif Printf("|| `[%p, %p]` || MidMem ||\n", (void*)kMidMemBeg, (void*)kMidMemEnd); +#if !SANITIZER_AIX || SANITIZER_WORDSIZE != 64 Printf("|| `[%p, %p]` || ShadowGap2 ||\n", (void*)kShadowGap2Beg, (void*)kShadowGap2End); +#endif Printf("|| `[%p, %p]` || MidShadow ||\n", (void*)kMidShadowBeg, (void*)kMidShadowEnd); } +#if SANITIZER_AIX && SANITIZER_WORDSIZE == 64 + Printf("|| `[%p, %p]` || Mid2Shadow ||\n", (void *)kMid2ShadowBeg, + (void *)kMid2ShadowEnd); + Printf("|| `[%p, %p]` || Mid3Shadow ||\n", (void *)kMid3ShadowBeg, + (void *)kMid3ShadowEnd); +#else Printf("|| `[%p, %p]` || ShadowGap ||\n", (void*)kShadowGapBeg, (void*)kShadowGapEnd); +#endif if (kLowShadowBeg) { Printf("|| `[%p, %p]` || LowShadow ||\n", (void*)kLowShadowBeg, (void*)kLowShadowEnd); @@ -371,6 +397,13 @@ void PrintAddressSpaceLayout() { (void*)MEM_TO_SHADOW(kMidShadowBeg), (void*)MEM_TO_SHADOW(kMidShadowEnd)); } +// On AIX, for 64-bit, there are totally 3 mid memory regions. +#if SANITIZER_AIX == 1 && SANITIZER_WORDSIZE == 64 + Printf(" %p %p", (void *)MEM_TO_SHADOW(kMid2ShadowBeg), + (void *)MEM_TO_SHADOW(kMid2ShadowEnd)); + Printf(" %p %p", (void *)MEM_TO_SHADOW(kMid3ShadowBeg), + (void *)MEM_TO_SHADOW(kMid3ShadowEnd)); +#endif Printf("\n"); Printf("redzone=%zu\n", (uptr)flags()->redzone); Printf("max_redzone=%zu\n", (uptr)flags()->max_redzone); @@ -386,7 +419,10 @@ void PrintAddressSpaceLayout() { CHECK(ASAN_SHADOW_SCALE >= 3 && ASAN_SHADOW_SCALE <= 7); if (kMidMemBeg) CHECK(kMidShadowBeg > kLowShadowEnd && +// On AIX 64-bit, we have a highly customized memory layout. +#if !SANITIZER_AIX || SANITIZER_WORDSIZE != 64 kMidMemBeg > kMidShadowEnd && +#endif kHighShadowBeg > kMidMemEnd); } diff --git a/compiler-rt/lib/asan/asan_shadow_setup.cpp b/compiler-rt/lib/asan/asan_shadow_setup.cpp index 5b3591da067bd..c7e72a325ecdd 100644 --- a/compiler-rt/lib/asan/asan_shadow_setup.cpp +++ b/compiler-rt/lib/asan/asan_shadow_setup.cpp @@ -42,6 +42,26 @@ static void ProtectGap(uptr addr, uptr size) { kZeroBaseMaxShadowStart); } +static void HandleShadowGap() { +# if SANITIZER_AIX == 1 && SANITIZER_WORDSIZE == 64 + // Fox 64-bit AIX, there is a very customized memory layout, we don't have + // the ability to protect all the shadow gaps. But we need to reserve + // shadow memory for middle memory. + if (kMidShadowBeg) + ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow"); + if (kMid2ShadowBeg) + ReserveShadowMemoryRange(kMid2ShadowBeg, kMid2ShadowEnd, "mid2 shadow"); + if (kMid3ShadowBeg) + ReserveShadowMemoryRange(kMid3ShadowBeg, kMid3ShadowEnd, "mid3 shadow"); +# else + if (kShadowGapBeg) { + // protect the gap. + ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); + CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1); + } +# endif +} + static void MaybeReportLinuxPIEBug() { #if SANITIZER_LINUX && \ (defined(__x86_64__) || defined(__aarch64__) || SANITIZER_RISCV64) @@ -91,9 +111,7 @@ void InitializeShadowMemory() { ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow"); // mmap the high shadow. ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow"); - // protect the gap. - ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); - CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1); + HandleShadowGap(); } else if (kMidMemBeg && MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) && MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) { diff --git a/compiler-rt/test/asan/TestCases/AIX/aix64_mapping.cpp b/compiler-rt/test/asan/TestCases/AIX/aix64_mapping.cpp new file mode 100644 index 0000000000000..932c47c30bf50 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/AIX/aix64_mapping.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_asan -O0 %s -o %t +// RUN: %env_asan_opts=verbosity=2 %run %t 2>&1 | FileCheck %s +// REQUIRES: powerpc64-target-arch + +#include + +int main() { + // CHECK: || `[0xfffff8000000000, 0xfffffffffffffff]` || HighMem || + // CHECK: || `[0xa80fff000000000, 0xa80ffffffffffff]` || HighShadow || + // CHECK: || `[0xa00000000000000, 0xa0001ffffffffff]` || MidMem || + // CHECK: || `[0xa41000000000000, 0xa41003fffffffff]` || MidShadow || + // CHECK: || `[0xa21020000000000, 0xa21020fffffffff]` || Mid2Shadow || + // CHECK: || `[0xa01020000000000, 0xa01020fffffffff]` || Mid3Shadow || + // CHECK: || `[0xa01000000000000, 0xa01000fffffffff]` || LowShadow || + // CHECK: || `[0x000000000000, 0x007fffffffff]` || LowMem || + + return 0; +} diff --git a/compiler-rt/test/asan/TestCases/AIX/lit.local.cfg.py b/compiler-rt/test/asan/TestCases/AIX/lit.local.cfg.py new file mode 100644 index 0000000000000..55462708e3b6c --- /dev/null +++ b/compiler-rt/test/asan/TestCases/AIX/lit.local.cfg.py @@ -0,0 +1,10 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + + +root = getRoot(config) + +if root.host_os not in ["AIX"]: + config.unsupported = True