diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp index 9bec782ca8ce9..9d9d9889b3858 100644 --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -605,6 +605,15 @@ void AArch64AsmPrinter::LowerKCFI_CHECK(const MachineInstr &MI) { void AArch64AsmPrinter::LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI) { Register Reg = MI.getOperand(0).getReg(); + + // The HWASan pass won't emit a CHECK_MEMACCESS intrinsic with a pointer + // statically known to be zero. However, conceivably, the HWASan pass may + // encounter a "cannot currently statically prove to be null" pointer (and is + // therefore unable to omit the intrinsic) that later optimization passes + // convert into a statically known-null pointer. + if (Reg == AArch64::XZR) + return; + bool IsShort = ((MI.getOpcode() == AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES) || (MI.getOpcode() == diff --git a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp index 2031728c2f33d..75a19357ea1bb 100644 --- a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -348,7 +348,7 @@ class HWAddressSanitizer { bool ignoreMemIntrinsic(OptimizationRemarkEmitter &ORE, MemIntrinsic *MI); void instrumentMemIntrinsic(MemIntrinsic *MI); bool instrumentMemAccess(InterestingMemoryOperand &O, DomTreeUpdater &DTU, - LoopInfo *LI); + LoopInfo *LI, const DataLayout &DL); bool ignoreAccessWithoutRemark(Instruction *Inst, Value *Ptr); bool ignoreAccess(OptimizationRemarkEmitter &ORE, Instruction *Inst, Value *Ptr); @@ -1163,12 +1163,23 @@ void HWAddressSanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) { } bool HWAddressSanitizer::instrumentMemAccess(InterestingMemoryOperand &O, - DomTreeUpdater &DTU, - LoopInfo *LI) { + DomTreeUpdater &DTU, LoopInfo *LI, + const DataLayout &DL) { Value *Addr = O.getPtr(); LLVM_DEBUG(dbgs() << "Instrumenting: " << O.getInsn() << "\n"); + // If the pointer is statically known to be zero, the tag check will pass + // since: + // 1) it has a zero tag + // 2) the shadow memory corresponding to address 0 is initialized to zero and + // never updated. + // We can therefore elide the tag check. + llvm::KnownBits Known(DL.getPointerTypeSizeInBits(Addr->getType())); + llvm::computeKnownBits(Addr, Known, DL); + if (Known.isZero()) + return false; + if (O.MaybeMask) return false; // FIXME @@ -1701,8 +1712,9 @@ void HWAddressSanitizer::sanitizeFunction(Function &F, PostDominatorTree *PDT = FAM.getCachedResult(F); LoopInfo *LI = FAM.getCachedResult(F); DomTreeUpdater DTU(DT, PDT, DomTreeUpdater::UpdateStrategy::Lazy); + const DataLayout &DL = F.getDataLayout(); for (auto &Operand : OperandsToInstrument) - instrumentMemAccess(Operand, DTU, LI); + instrumentMemAccess(Operand, DTU, LI, DL); DTU.flush(); if (ClInstrumentMemIntrinsics && !IntrinToInstrument.empty()) { diff --git a/llvm/test/CodeGen/AArch64/hwasan-zero-ptr.ll b/llvm/test/CodeGen/AArch64/hwasan-zero-ptr.ll index dca39fe03fb10..b2eaa31007c35 100644 --- a/llvm/test/CodeGen/AArch64/hwasan-zero-ptr.ll +++ b/llvm/test/CodeGen/AArch64/hwasan-zero-ptr.ll @@ -1,11 +1,14 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 ; RUN: llc -filetype asm -o - %s | FileCheck %s -; This shows that when dereferencing a null pointer, HWASan will call -; __hwasan_check_x4294967071_19_fixed_0_short_v2 -; (N.B. 4294967071 == 2**32 - 239 + 14 == 2**32 - X0 + XZR +; This shows that CodeGen for AArch64 will elide the tag check when lowering +; the HWASan memaccess intrinsic for null pointers. ; -; The source was generated from llvm/test/Instrumentation/HWAddressSanitizer/zero-ptr.ll. +; N.B. The HWASan pass will normally omit the memaccess intrinsic if the +; pointer is already statically known to be null. +; +; The source was generated from llvm/test/Instrumentation/HWAddressSanitizer/zero-ptr.ll +; with the memaccess deliberately retained. ; ModuleID = '' source_filename = "" @@ -25,7 +28,6 @@ define void @test_store_to_zeroptr() #0 { ; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill ; CHECK-NEXT: .cfi_def_cfa_offset 16 ; CHECK-NEXT: .cfi_offset w30, -16 -; CHECK-NEXT: bl __hwasan_check_x4294967071_19_fixed_0_short_v2 ; CHECK-NEXT: mov x8, xzr ; CHECK-NEXT: mov w9, #42 // =0x2a ; CHECK-NEXT: str x9, [x8] diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/zero-ptr.ll b/llvm/test/Instrumentation/HWAddressSanitizer/zero-ptr.ll index a201174df995b..95cf6f1544df0 100644 --- a/llvm/test/Instrumentation/HWAddressSanitizer/zero-ptr.ll +++ b/llvm/test/Instrumentation/HWAddressSanitizer/zero-ptr.ll @@ -2,7 +2,7 @@ ; RUN: opt < %s -passes=hwasan -S | FileCheck %s ; RUN: opt < %s -passes=hwasan -hwasan-recover=0 -hwasan-mapping-offset=0 -S | FileCheck %s --check-prefixes=ABORT-ZERO-BASED-SHADOW -; This shows that HWASan will emit a memaccess check when dereferencing a null +; This shows that HWASan omits the memaccess check when dereferencing a null ; pointer. ; The output is used as the source for llvm/test/CodeGen/AArch64/hwasan-zero-ptr.ll. @@ -15,7 +15,6 @@ define void @test_store_to_zeroptr() sanitize_hwaddress { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[DOTHWASAN_SHADOW:%.*]] = call ptr asm "", "=r,0"(ptr @__hwasan_shadow) ; CHECK-NEXT: [[B:%.*]] = inttoptr i64 0 to ptr -; CHECK-NEXT: call void @llvm.hwasan.check.memaccess.shortgranules(ptr [[DOTHWASAN_SHADOW]], ptr [[B]], i32 19) ; CHECK-NEXT: store i64 42, ptr [[B]], align 8 ; CHECK-NEXT: ret void ; @@ -24,7 +23,6 @@ define void @test_store_to_zeroptr() sanitize_hwaddress { ; ABORT-ZERO-BASED-SHADOW-NEXT: entry: ; ABORT-ZERO-BASED-SHADOW-NEXT: [[DOTHWASAN_SHADOW:%.*]] = call ptr asm "", "=r,0"(ptr null) ; ABORT-ZERO-BASED-SHADOW-NEXT: [[B:%.*]] = inttoptr i64 0 to ptr -; ABORT-ZERO-BASED-SHADOW-NEXT: call void @llvm.hwasan.check.memaccess.shortgranules.fixedshadow(ptr [[B]], i32 19, i64 0) ; ABORT-ZERO-BASED-SHADOW-NEXT: store i64 42, ptr [[B]], align 8 ; ABORT-ZERO-BASED-SHADOW-NEXT: ret void ;