diff --git a/compiler-rt/lib/dfsan/dfsan.cpp b/compiler-rt/lib/dfsan/dfsan.cpp index 1b2d552f2d5cfe..6e9ad107e2233a 100644 --- a/compiler-rt/lib/dfsan/dfsan.cpp +++ b/compiler-rt/lib/dfsan/dfsan.cpp @@ -88,8 +88,72 @@ int __dfsan_get_track_origins() { // 45-46 are cleared to bring the address into the range // [0x100000008000,0x200000000000). See the function shadow_for below. // +// On Linux/MIPS64, memory is laid out as follows: // +// +--------------------+ 0x10000000000 (top of memory) +// | application memory | +// +--------------------+ 0xF000008000 (kAppAddr) +// | | +// | unused | +// | | +// +--------------------+ 0x2000000000 (kUnusedAddr) +// | shadow memory | +// +--------------------+ 0x1000008000 (kShadowAddr) +// | unused | +// +--------------------+ 0x0000010000 +// | reserved by kernel | +// +--------------------+ 0x0000000000 + +// On Linux/AArch64 (39-bit VMA), memory is laid out as follow: +// +// +--------------------+ 0x8000000000 (top of memory) +// | application memory | +// +--------------------+ 0x7000008000 (kAppAddr) +// | | +// | unused | +// | | +// +--------------------+ 0x1000000000 (kUnusedAddr) +// | shadow memory | +// +--------------------+ 0x0000010000 (kShadowAddr) +// | reserved by kernel | +// +--------------------+ 0x0000000000 +// On Linux/AArch64 (42-bit VMA), memory is laid out as follow: +// +// +--------------------+ 0x40000000000 (top of memory) +// | application memory | +// +--------------------+ 0x3ff00008000 (kAppAddr) +// | | +// | unused | +// | | +// +--------------------+ 0x8000000000 (kUnusedAddr) +// | shadow memory | +// +--------------------+ 0x0000010000 (kShadowAddr) +// | reserved by kernel | +// +--------------------+ 0x0000000000 + +// On Linux/AArch64 (48-bit VMA), memory is laid out as follow: +// +// +--------------------+ 0x1000000000000 (top of memory) +// | application memory | +// +--------------------+ 0xffff00008000 (kAppAddr) +// | unused | +// +--------------------+ 0xaaaab0000000 (top of PIE address) +// | application PIE | +// +--------------------+ 0xaaaaa0000000 (top of PIE address) +// | | +// | unused | +// | | +// +--------------------+ 0x8000000000 (kUnusedAddr) +// | shadow memory | +// +--------------------+ 0x0000010000 (kShadowAddr) +// | reserved by kernel | +// +--------------------+ 0x0000000000 + +#ifdef DFSAN_RUNTIME_VMA +// Runtime detected VMA size. +int __dfsan::vmaSize; +#endif extern "C" SANITIZER_INTERFACE_ATTRIBUTE dfsan_label __dfsan_union_load(const dfsan_label *ls, uptr n) { @@ -835,6 +899,22 @@ void dfsan_clear_thread_local_state() { } } +static void InitializePlatformEarly() { + AvoidCVE_2016_2143(); +#ifdef DFSAN_RUNTIME_VMA + __dfsan::vmaSize = + (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); + if (__dfsan::vmaSize == 39 || __dfsan::vmaSize == 42 || + __dfsan::vmaSize == 48) { + __dfsan_shadow_ptr_mask = ShadowMask(); + } else { + Printf("FATAL: DataFlowSanitizer: unsupported VMA range\n"); + Printf("FATAL: Found %d - Supported 39, 42, and 48\n", __dfsan::vmaSize); + Die(); + } +#endif +} + extern "C" void dfsan_flush() { if (!MmapFixedSuperNoReserve(ShadowAddr(), UnusedAddr() - ShadowAddr())) Die(); @@ -847,10 +927,10 @@ static void DFsanInit(int argc, char **argv, char **envp) { dfsan_init_is_running = true; SanitizerToolName = "DataflowSanitizer"; - AvoidCVE_2016_2143(); - InitializeFlags(); + ::InitializePlatformEarly(); + dfsan_flush(); if (common_flags()->use_madv_dontdump) DontDumpShadowMemory(ShadowAddr(), UnusedAddr() - ShadowAddr()); diff --git a/compiler-rt/lib/dfsan/dfsan_platform.h b/compiler-rt/lib/dfsan/dfsan_platform.h index 64a093ff97d1d9..8f64227c985933 100644 --- a/compiler-rt/lib/dfsan/dfsan_platform.h +++ b/compiler-rt/lib/dfsan/dfsan_platform.h @@ -20,6 +20,7 @@ namespace __dfsan { using __sanitizer::uptr; +#if defined(__x86_64__) struct Mapping { static const uptr kShadowAddr = 0x100000008000; static const uptr kOriginAddr = 0x200000008000; @@ -27,10 +28,46 @@ struct Mapping { static const uptr kAppAddr = 0x700000008000; static const uptr kShadowMask = ~0x600000000000; }; +#elif defined(__mips64) +struct Mapping { + static const uptr kShadowAddr = 0x1000008000; + static const uptr kUnusedAddr = 0x2000000000; + static const uptr kAppAddr = 0xF000008000; + static const uptr kShadowMask = ~0xE000000000; +}; +#elif defined(__aarch64__) +struct Mapping39 { + static const uptr kShadowAddr = 0x10000; + static const uptr kUnusedAddr = 0x1000000000; + static const uptr kAppAddr = 0x7000008000; + static const uptr kShadowMask = ~0x7800000000; +}; + +struct Mapping42 { + static const uptr kShadowAddr = 0x10000; + static const uptr kUnusedAddr = 0x8000000000; + static const uptr kAppAddr = 0x3ff00008000; + static const uptr kShadowMask = ~0x3c000000000; +}; + +struct Mapping48 { + static const uptr kShadowAddr = 0x10000; + static const uptr kUnusedAddr = 0x8000000000; + static const uptr kAppAddr = 0xffff00008000; + static const uptr kShadowMask = ~0xfffff0000000; +}; + +extern int vmaSize; +# define DFSAN_RUNTIME_VMA 1 +#else +# error "DFSan not supported for this platform!" +#endif enum MappingType { MAPPING_SHADOW_ADDR, +#if defined(__x86_64__) MAPPING_ORIGIN_ADDR, +#endif MAPPING_UNUSED_ADDR, MAPPING_APP_ADDR, MAPPING_SHADOW_MASK @@ -53,7 +90,17 @@ uptr MappingImpl(void) { template uptr MappingArchImpl(void) { +#ifdef __aarch64__ + switch (vmaSize) { + case 39: return MappingImpl(); + case 42: return MappingImpl(); + case 48: return MappingImpl(); + } + DCHECK(0); + return 0; +#else return MappingImpl(); +#endif } ALWAYS_INLINE @@ -63,7 +110,11 @@ uptr ShadowAddr() { ALWAYS_INLINE uptr OriginAddr() { +#if defined(__x86_64__) return MappingArchImpl(); +#else + return 0; +#endif } ALWAYS_INLINE diff --git a/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp index 708df2895a0d54..ae375314f994d9 100644 --- a/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp @@ -123,6 +123,11 @@ static const Align MinOriginAlignment = Align(4); static const unsigned ArgTLSSize = 800; static const unsigned RetvalTLSSize = 800; +// External symbol to be used when generating the shadow address for +// architectures with multiple VMAs. Instead of using a constant integer +// the runtime will set the external mask based on the VMA range. +const char DFSanExternShadowPtrMask[] = "__dfsan_shadow_ptr_mask"; + // The -dfsan-preserve-alignment flag controls whether this pass assumes that // alignment requirements provided by the input IR are correct. For example, // if the input IR contains a load with alignment 8, this flag will cause @@ -398,6 +403,7 @@ class DataFlowSanitizer { Constant *ArgOriginTLS; Constant *RetvalTLS; Constant *RetvalOriginTLS; + Constant *ExternalShadowMask; FunctionType *DFSanUnionLoadFnTy; FunctionType *DFSanLoadLabelAndOriginFnTy; FunctionType *DFSanUnimplementedFnTy; @@ -431,6 +437,7 @@ class DataFlowSanitizer { DFSanABIList ABIList; DenseMap UnwrappedFnMap; AttrBuilder ReadOnlyNoneAttrs; + bool DFSanRuntimeShadowMask = false; Value *getShadowOffset(Value *Addr, IRBuilder<> &IRB); Value *getShadowAddress(Value *Addr, Instruction *Pos); @@ -1006,11 +1013,6 @@ bool DataFlowSanitizer::init(Module &M) { Triple TargetTriple(M.getTargetTriple()); const DataLayout &DL = M.getDataLayout(); - if (TargetTriple.getOS() != Triple::Linux) - report_fatal_error("unsupported operating system"); - if (TargetTriple.getArch() != Triple::x86_64) - report_fatal_error("unsupported architecture"); - Mod = &M; Ctx = &M.getContext(); Int8Ptr = Type::getInt8PtrTy(*Ctx); @@ -1022,9 +1024,27 @@ bool DataFlowSanitizer::init(Module &M) { ZeroPrimitiveShadow = ConstantInt::getSigned(PrimitiveShadowTy, 0); ZeroOrigin = ConstantInt::getSigned(OriginTy, 0); + // TODO: these should be platform-specific and set in the switch-stmt below. ShadowBase = ConstantInt::get(IntptrTy, 0x100000008000LL); OriginBase = ConstantInt::get(IntptrTy, 0x200000008000LL); - ShadowPtrMask = ConstantInt::getSigned(IntptrTy, ~0x600000000000LL); + + switch (TargetTriple.getArch()) { + case Triple::x86_64: + ShadowPtrMask = ConstantInt::getSigned(IntptrTy, ~0x600000000000LL); + break; + case Triple::mips64: + case Triple::mips64el: + ShadowPtrMask = ConstantInt::getSigned(IntptrTy, ~0xE000000000LL); + break; + case Triple::aarch64: + case Triple::aarch64_be: + // AArch64 supports multiple VMAs and the shadow mask is set at runtime. + DFSanRuntimeShadowMask = true; + break; + default: + report_fatal_error("unsupported triple"); + } + Type *DFSanUnionLoadArgs[2] = {PrimitiveShadowPtrTy, IntptrTy}; DFSanUnionLoadFnTy = FunctionType::get(PrimitiveShadowTy, DFSanUnionLoadArgs, /*isVarArg=*/false); @@ -1370,6 +1390,9 @@ bool DataFlowSanitizer::runImpl(Module &M) { injectMetadataGlobals(M); + ExternalShadowMask = + Mod->getOrInsertGlobal(DFSanExternShadowPtrMask, IntptrTy); + initializeCallbackFunctions(M); initializeRuntimeFunctions(M); @@ -1718,8 +1741,13 @@ void DFSanFunction::setShadow(Instruction *I, Value *Shadow) { Value *DataFlowSanitizer::getShadowOffset(Value *Addr, IRBuilder<> &IRB) { // Returns Addr & shadow_mask assert(Addr != RetvalTLS && "Reinstrumenting?"); + Value *ShadowPtrMaskValue; + if (DFSanRuntimeShadowMask) + ShadowPtrMaskValue = IRB.CreateLoad(IntptrTy, ExternalShadowMask); + else + ShadowPtrMaskValue = ShadowPtrMask; return IRB.CreateAnd(IRB.CreatePtrToInt(Addr, IntptrTy), - IRB.CreatePtrToInt(ShadowPtrMask, IntptrTy)); + IRB.CreatePtrToInt(ShadowPtrMaskValue, IntptrTy)); } std::pair diff --git a/llvm/test/Instrumentation/DataFlowSanitizer/external_mask.ll b/llvm/test/Instrumentation/DataFlowSanitizer/external_mask.ll new file mode 100644 index 00000000000000..26491f50a00f2f --- /dev/null +++ b/llvm/test/Instrumentation/DataFlowSanitizer/external_mask.ll @@ -0,0 +1,13 @@ +; RUN: opt < %s -dfsan -S | FileCheck %s +target datalayout = "e-m:e-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-gnu" + +define i32 @test(i32 %a, i32* nocapture readonly %b) #0 { + ; CHECK: @"dfs$test" + ; CHECK: %[[RV:.*]] load{{.*}}__dfsan_shadow_ptr_mask + ; CHECK: ptrtoint i32* {{.*}} to i64 + ; CHECK: and {{.*}}%[[RV:.*]] + %1 = load i32, i32* %b, align 4 + %2 = add nsw i32 %1, %a + ret i32 %2 +}