From 3aacfafaad4a958bdcc0043b4492f686a3e38d06 Mon Sep 17 00:00:00 2001 From: Kostya Serebryany Date: Tue, 4 Oct 2016 23:39:58 +0000 Subject: [PATCH] [asan] When protect_shadow_gap=0, set up the shadow for the shadow gap. This is needed to support NVIDIA CUDA drivers. Unfortunately, I don't know how to test it properly with CUDA on a public build bot, so adding a test that emulates the CUDA behavior. llvm-svn: 283270 --- compiler-rt/lib/asan/asan_mapping.h | 33 +++++++++---------- compiler-rt/lib/asan/asan_rtl.cc | 15 ++++++++- compiler-rt/test/asan/TestCases/Linux/cuda_test.cc | 37 ++++++++++++++++++++++ 3 files changed, 68 insertions(+), 17 deletions(-) create mode 100644 compiler-rt/test/asan/TestCases/Linux/cuda_test.cc diff --git a/compiler-rt/lib/asan/asan_mapping.h b/compiler-rt/lib/asan/asan_mapping.h index 5cbdd34..d8e60a4 100644 --- a/compiler-rt/lib/asan/asan_mapping.h +++ b/compiler-rt/lib/asan/asan_mapping.h @@ -269,9 +269,25 @@ static inline bool AddrIsInMidMem(uptr a) { return kMidMemBeg && a >= kMidMemBeg && a <= kMidMemEnd; } +static inline bool AddrIsInShadowGap(uptr a) { + PROFILE_ASAN_MAPPING(); + if (kMidMemBeg) { + if (a <= kShadowGapEnd) + return SHADOW_OFFSET == 0 || a >= kShadowGapBeg; + return (a >= kShadowGap2Beg && a <= kShadowGap2End) || + (a >= kShadowGap3Beg && a <= kShadowGap3End); + } + // In zero-based shadow mode we treat addresses near zero as addresses + // in shadow gap as well. + if (SHADOW_OFFSET == 0) + return a <= kShadowGapEnd; + return a >= kShadowGapBeg && a <= kShadowGapEnd; +} + static inline bool AddrIsInMem(uptr a) { PROFILE_ASAN_MAPPING(); - return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a); + return AddrIsInLowMem(a) || AddrIsInMidMem(a) || AddrIsInHighMem(a) || + (flags()->protect_shadow_gap == 0 && AddrIsInShadowGap(a)); } static inline uptr MemToShadow(uptr p) { @@ -295,21 +311,6 @@ static inline bool AddrIsInShadow(uptr a) { return AddrIsInLowShadow(a) || AddrIsInMidShadow(a) || AddrIsInHighShadow(a); } -static inline bool AddrIsInShadowGap(uptr a) { - PROFILE_ASAN_MAPPING(); - if (kMidMemBeg) { - if (a <= kShadowGapEnd) - return SHADOW_OFFSET == 0 || a >= kShadowGapBeg; - return (a >= kShadowGap2Beg && a <= kShadowGap2End) || - (a >= kShadowGap3Beg && a <= kShadowGap3End); - } - // In zero-based shadow mode we treat addresses near zero as addresses - // in shadow gap as well. - if (SHADOW_OFFSET == 0) - return a <= kShadowGapEnd; - return a >= kShadowGapBeg && a <= kShadowGapEnd; -} - static inline bool AddrIsAlignedByGranularity(uptr a) { PROFILE_ASAN_MAPPING(); return (a & (SHADOW_GRANULARITY - 1)) == 0; diff --git a/compiler-rt/lib/asan/asan_rtl.cc b/compiler-rt/lib/asan/asan_rtl.cc index 462867b..97ce9f8 100644 --- a/compiler-rt/lib/asan/asan_rtl.cc +++ b/compiler-rt/lib/asan/asan_rtl.cc @@ -335,8 +335,21 @@ static void InitializeHighMemEnd() { } static void ProtectGap(uptr addr, uptr size) { - if (!flags()->protect_shadow_gap) + if (!flags()->protect_shadow_gap) { + // The shadow gap is unprotected, so there is a chance that someone + // is actually using this memory. Which means it needs a shadow... + uptr GapShadowBeg = RoundDownTo(MEM_TO_SHADOW(addr), GetPageSizeCached()); + uptr GapShadowEnd = + RoundUpTo(MEM_TO_SHADOW(addr + size), GetPageSizeCached()) - 1; + if (Verbosity()) + Printf("protect_shadow_gap=0:" + " not protecting shadow gap, allocating gap's shadow\n" + "|| `[%p, %p]` || ShadowGap's shadow ||\n", GapShadowBeg, + GapShadowEnd); + ReserveShadowMemoryRange(GapShadowBeg, GapShadowEnd, + "unprotected gap shadow"); return; + } void *res = MmapFixedNoAccess(addr, size, "shadow gap"); if (addr == (uptr)res) return; diff --git a/compiler-rt/test/asan/TestCases/Linux/cuda_test.cc b/compiler-rt/test/asan/TestCases/Linux/cuda_test.cc new file mode 100644 index 0000000..e87f56b --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Linux/cuda_test.cc @@ -0,0 +1,37 @@ +// Emulate the behavior of the NVIDIA CUDA driver +// that mmaps memory inside the asan's shadow gap. +// +// REQUIRES: x86_64-target-arch +// +// RUN: %clangxx_asan %s -o %t +// RUN: not %env_asan_opts=protect_shadow_gap=1 %t 2>&1 | FileCheck %s --check-prefix=CHECK-PROTECT1 +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK-PROTECT1 +// RUN: not %env_asan_opts=protect_shadow_gap=0 %t 2>&1 | FileCheck %s --check-prefix=CHECK-PROTECT0 +#include +#include +#include +#include + +#include "sanitizer/asan_interface.h" + +int main(void) { + uintptr_t Base = 0x200000000; + uintptr_t Size = 0x1100000000; + void *addr = + mmap((void *)Base, Size, PROT_READ | PROT_WRITE, + MAP_NORESERVE | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0); + assert(addr == (void*)Base); + // Make sure we can access memory in shadow gap. + // W/o protect_shadow_gap=0 we should fail here. + for (uintptr_t Addr = Base; Addr < Base + Size; Addr += Size / 100) + *(char*)Addr = 1; + // CHECK-PROTECT1: AddressSanitizer: SEGV on unknown address 0x0000bfff8000 + + // Poison a part of gap's shadow: + __asan_poison_memory_region((void*)Base, 4096); + // Now we should fail with use-after-poison. + *(char*)(Base + 1234) = 1; + // CHECK-PROTECT0: AddressSanitizer: use-after-poison on address 0x0002000004d2 +} + + -- 2.7.4