Skip to content

[hwasan] Omit tag check for null pointers #122206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() ==
Expand Down
20 changes: 16 additions & 4 deletions llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -1701,8 +1712,9 @@ void HWAddressSanitizer::sanitizeFunction(Function &F,
PostDominatorTree *PDT = FAM.getCachedResult<PostDominatorTreeAnalysis>(F);
LoopInfo *LI = FAM.getCachedResult<LoopAnalysis>(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()) {
Expand Down
12 changes: 7 additions & 5 deletions llvm/test/CodeGen/AArch64/hwasan-zero-ptr.ll
Original file line number Diff line number Diff line change
@@ -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 = '<stdin>'
source_filename = "<stdin>"
Expand All @@ -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]
Expand Down
4 changes: 1 addition & 3 deletions llvm/test/Instrumentation/HWAddressSanitizer/zero-ptr.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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
;
Expand All @@ -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
;
Expand Down
Loading