Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -90,6 +90,11 @@ option(SANITIZER_CAN_USE_CXXABI "Sanitizers can use cxxabi" ${use_cxxabi_default}) pythonize_bool(SANITIZER_CAN_USE_CXXABI) +option(SANITIZER_USE_SOFTWARE_MEMORY_MANAGER + "Experimental: Use software memory manager instead of mmap() in sanitizers." OFF) +mark_as_advanced(SANITIZER_USE_SOFTWARE_MEMORY_MANAGER) +pythonize_bool(SANITIZER_USE_SOFTWARE_MEMORY_MANAGER) + #================================ # Setup Compiler Flags #================================ @@ -168,6 +173,8 @@ endif() append_list_if(COMPILER_RT_DEBUG -DSANITIZER_DEBUG=1 SANITIZER_COMMON_CFLAGS) +append_list_if(SANITIZER_USE_SOFTWARE_MEMORY_MANAGER + -DSANITIZER_USE_SOFTWARE_MEMORY_MANAGER=1 SANITIZER_COMMON_CFLAGS) # Build with optimization, unless we're in debug mode. If we're using MSVC, # always respect the optimization flags set by CMAKE_BUILD_TYPE instead. Index: lib/asan/asan_allocator.cc =================================================================== --- lib/asan/asan_allocator.cc +++ lib/asan/asan_allocator.cc @@ -414,7 +414,7 @@ if (!allocated) return allocator.ReturnNullOrDieOnOOM(); - if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) { + if (*shadow_ptr(allocated) == 0 && CanPoisonMemory()) { // Heap poisoning is enabled, but the allocator provides an unpoisoned // chunk. This is possible if CanPoisonMemory() was false for some // time, for example, due to flags()->start_disabled. @@ -466,9 +466,8 @@ PoisonShadow(user_beg, size_rounded_down_to_granularity, 0); // Deal with the end of the region if size is not aligned to granularity. if (size != size_rounded_down_to_granularity && CanPoisonMemory()) { - u8 *shadow = - (u8 *)MemToShadow(user_beg + size_rounded_down_to_granularity); - *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0; + u8 sval = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0; + *checked_shadow_ptr(user_beg + size_rounded_down_to_granularity) = sval; } AsanStats &thread_stats = GetCurrentThreadStats(); Index: lib/asan/asan_fake_stack.cc =================================================================== --- lib/asan/asan_fake_stack.cc +++ lib/asan/asan_fake_stack.cc @@ -29,10 +29,10 @@ // For small size classes inline PoisonShadow for better performance. ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) { CHECK_EQ(SHADOW_SCALE, 3); // This code expects SHADOW_SCALE=3. - u64 *shadow = reinterpret_cast(MemToShadow(ptr)); + checked_shadow_ptr shadow(ptr); if (class_id <= 6) { for (uptr i = 0; i < (((uptr)1) << class_id); i++) { - shadow[i] = magic; + shadow.at(i) = magic; // Make sure this does not become memset. SanitizerBreakOptimization(nullptr); } Index: lib/asan/asan_mapping.h =================================================================== --- lib/asan/asan_mapping.h +++ lib/asan/asan_mapping.h @@ -235,6 +235,74 @@ namespace __asan { +#if SANITIZER_USE_SOFTWARE_MEMORY_MANAGER +static const unsigned page_size_scale = 16; // 64K bytes. +static const unsigned page_size = 1u << page_size_scale; +static const unsigned page_size_mask = page_size - 1; +static const uptr non_existent_vpage_no = ~static_cast(0); + +// Return offset within shadow page. +#define PAGE_OFFSET(vp) (static_cast((vp)) & page_size_mask) + +// Return virtual page number by given virtual address. +#define VPTR_TO_VPAGE(vp) (static_cast((vp)) >> page_size_scale) +#endif // SANITIZER_USE_SOFTWARE_MEMORY_MANAGER + +// Pointers of this class provide access to physical shadow memory. +class shadow_ptr { + public: + explicit shadow_ptr(uptr mem) + : value(MEM_TO_SHADOW(mem)) +#if SANITIZER_USE_SOFTWARE_MEMORY_MANAGER + , last_vpage_hit(non_existent_vpage_no), page(nullptr) +#endif + {} + + explicit shadow_ptr(void *mem) + : shadow_ptr(reinterpret_cast(mem)) + {} + + u8 &access(uptr p) { +#if SANITIZER_USE_SOFTWARE_MEMORY_MANAGER + uptr vpage = VPTR_TO_VPAGE(p); + if (vpage != last_vpage_hit) { + // TODO(ikosarev): Get a pointer to the physical page here. + page = reinterpret_cast(p) - PAGE_OFFSET(p); + last_vpage_hit = vpage; + } + return page[PAGE_OFFSET(p)]; +#else + return *reinterpret_cast(p); +#endif + } + + u8 &operator * () { return access(value); } + + template + cell_type &ref() { + return reinterpret_cast(access(value)); + } + + template + cell_type &at(uptr index) { + uptr p = value + sizeof(cell_type) * index; + return reinterpret_cast(access(p)); + } + + shadow_ptr &operator ++() { + ++value; + return *this; + } + + private: + uptr value; + +#if SANITIZER_USE_SOFTWARE_MEMORY_MANAGER + uptr last_vpage_hit; + u8 *page; +#endif +}; + extern uptr AsanMappingProfile[]; #if ASAN_FIXED_MAPPING @@ -295,6 +363,14 @@ return MEM_TO_SHADOW(p); } +class checked_shadow_ptr : public shadow_ptr { + public: + explicit checked_shadow_ptr(uptr mem) : shadow_ptr(mem) { + PROFILE_ASAN_MAPPING(); + CHECK(AddrIsInMem(mem)); + } +}; + static inline bool AddrIsInHighShadow(uptr a) { PROFILE_ASAN_MAPPING(); return a >= kHighShadowBeg && a <= kHighMemEnd; @@ -318,8 +394,7 @@ static inline bool AddressIsPoisoned(uptr a) { PROFILE_ASAN_MAPPING(); const uptr kAccessSize = 1; - u8 *shadow_address = (u8*)MEM_TO_SHADOW(a); - s8 shadow_value = *shadow_address; + s8 shadow_value = *shadow_ptr(a); if (shadow_value) { u8 last_accessed_byte = (a & (SHADOW_GRANULARITY - 1)) + kAccessSize - 1; Index: lib/asan/asan_poisoning.h =================================================================== --- lib/asan/asan_poisoning.h +++ lib/asan/asan_poisoning.h @@ -73,8 +73,8 @@ uptr aligned_addr, uptr size, uptr redzone_size, u8 value) { DCHECK(CanPoisonMemory()); bool poison_partial = flags()->poison_partial; - u8 *shadow = (u8*)MEM_TO_SHADOW(aligned_addr); - for (uptr i = 0; i < redzone_size; i += SHADOW_GRANULARITY, shadow++) { + shadow_ptr shadow(aligned_addr); + for (uptr i = 0; i < redzone_size; i += SHADOW_GRANULARITY, ++shadow) { if (i + SHADOW_GRANULARITY <= size) { *shadow = 0; // fully addressable } else if (i >= size) { Index: lib/asan/asan_poisoning.cc =================================================================== --- lib/asan/asan_poisoning.cc +++ lib/asan/asan_poisoning.cc @@ -81,13 +81,13 @@ CHECK_LE(size, 4096); CHECK(IsAligned(end, SHADOW_GRANULARITY)); if (!IsAligned(ptr, SHADOW_GRANULARITY)) { - *(u8 *)MemToShadow(ptr) = - poison ? static_cast(ptr % SHADOW_GRANULARITY) : 0; + u8 sval = poison ? static_cast(ptr % SHADOW_GRANULARITY) : 0; + *checked_shadow_ptr(ptr) = sval; ptr |= SHADOW_GRANULARITY - 1; ptr++; } for (; ptr < end; ptr += SHADOW_GRANULARITY) - *(u8*)MemToShadow(ptr) = poison ? kAsanIntraObjectRedzone : 0; + *checked_shadow_ptr(ptr) = poison ? kAsanIntraObjectRedzone : 0; } } // namespace __asan @@ -260,16 +260,14 @@ void __asan_poison_cxx_array_cookie(uptr p) { if (SANITIZER_WORDSIZE != 64) return; if (!flags()->poison_array_cookie) return; - uptr s = MEM_TO_SHADOW(p); - *reinterpret_cast(s) = kAsanArrayCookieMagic; + *shadow_ptr(p) = kAsanArrayCookieMagic; } extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_load_cxx_array_cookie(uptr *p) { if (SANITIZER_WORDSIZE != 64) return *p; if (!flags()->poison_array_cookie) return *p; - uptr s = MEM_TO_SHADOW(reinterpret_cast(p)); - u8 sval = *reinterpret_cast(s); + u8 sval = *shadow_ptr(p); if (sval == kAsanArrayCookieMagic) return *p; // If sval is not kAsanArrayCookieMagic it can only be freed memory, // which means that we are going to get double-free. So, return 0 to avoid @@ -377,11 +375,11 @@ // FIXME: Two of these three checks are disabled until we fix // https://github.com/google/sanitizers/issues/258. // if (d1 != d2) - // CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1); + // CHECK_EQ(*checked_shadow_ptr(d1), old_mid - d1); if (a + granularity <= d1) - CHECK_EQ(*(u8*)MemToShadow(a), 0); + CHECK_EQ(*checked_shadow_ptr(a), 0); // if (d2 + granularity <= c && c <= end) - // CHECK_EQ(*(u8 *)MemToShadow(c - granularity), + // CHECK_EQ(*checked_shadow_ptr(c - granularity), // kAsanContiguousContainerOOBMagic); uptr b1 = RoundDownTo(new_mid, granularity); @@ -392,7 +390,7 @@ PoisonShadow(b2, c - b2, kAsanContiguousContainerOOBMagic); if (b1 != b2) { CHECK_EQ(b2 - b1, granularity); - *(u8*)MemToShadow(b1) = static_cast(new_mid - b1); + *checked_shadow_ptr(b1) = static_cast(new_mid - b1); } } Index: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -160,9 +160,8 @@ ASAN_REPORT_ERROR_N(store, true) #define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg, fatal) \ - uptr sp = MEM_TO_SHADOW(addr); \ - uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast(sp) \ - : *reinterpret_cast(sp); \ + shadow_ptr sp(addr); \ + uptr s = size <= SHADOW_GRANULARITY ? sp.ref() : sp.ref(); \ if (UNLIKELY(s)) { \ if (UNLIKELY(size >= SHADOW_GRANULARITY || \ ((s8)((addr & (SHADOW_GRANULARITY - 1)) + size - 1)) >= \ Index: lib/asan/tests/CMakeLists.txt =================================================================== --- lib/asan/tests/CMakeLists.txt +++ lib/asan/tests/CMakeLists.txt @@ -328,7 +328,10 @@ darwin_filter_host_archs(ASAN_SUPPORTED_ARCH ASAN_TEST_ARCH) endif() foreach(arch ${ASAN_TEST_ARCH}) - add_asan_tests_for_arch_and_kind(${arch} "-inline") + # The software memory manager requires out-of-line instrumentation. + if(NOT SANITIZER_USE_SOFTWARE_MEMORY_MANAGER) + add_asan_tests_for_arch_and_kind(${arch} "-inline") + endif() add_asan_tests_for_arch_and_kind(${arch} "-with-calls" -mllvm -asan-instrumentation-with-call-threshold=0) endforeach() Index: test/asan/lit.cfg =================================================================== --- test/asan/lit.cfg +++ test/asan/lit.cfg @@ -82,6 +82,10 @@ config.debug_info_flags + target_cflags) if config.target_arch == 's390x': clang_asan_static_cflags.append("-mbackchain") + +if config.use_software_memory_manager: + clang_asan_static_cflags.extend( + ["-mllvm", "-asan-instrumentation-with-call-threshold=0"]) clang_asan_static_cxxflags = config.cxx_mode_flags + clang_asan_static_cflags asan_dynamic_flags = [] Index: test/lit.common.configured.in =================================================================== --- test/lit.common.configured.in +++ test/lit.common.configured.in @@ -32,6 +32,8 @@ set_default("can_symbolize", @CAN_SYMBOLIZE@) set_default("use_lld", False) set_default("use_thinlto", False) +set_default("use_software_memory_manager", + "@SANITIZER_USE_SOFTWARE_MEMORY_MANAGER_PYBOOL@") config.available_features.add('target-is-%s' % config.target_arch) # LLVM tools dir can be passed in lit parameters, so try to Index: test/safestack/canary.c =================================================================== --- test/safestack/canary.c +++ test/safestack/canary.c @@ -2,7 +2,8 @@ // RUN: %run %t.nossp 2>&1 | FileCheck --check-prefix=NOSSP %s // RUN: %clang_safestack -fstack-protector-all -D_FORTIFY_SOURCE=0 -g %s -o %t.ssp -// RUN: not --crash %run %t.ssp 2>&1 | FileCheck -check-prefix=SSP %s +// RUN: env LIBC_FATAL_STDERR_=1 not --crash %run %t.ssp 2>&1 | \ +// RUN: FileCheck -check-prefix=SSP %s // Test stack canaries on the unsafe stack.