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 4 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
8 changes: 8 additions & 0 deletions llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,14 @@ 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 has a
// "maybe non-zero" pointer but later optimization passes convert it into
// a null pointer. As a last line of defense, we perform elision here too.
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.getMinValue() == 0 && Known.getMaxValue() == 0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be just

if (Known.isZero())

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a nice simplification! Done.

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
11 changes: 6 additions & 5 deletions llvm/test/CodeGen/AArch64/hwasan-zero-ptr.ll
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
; 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 HWASan will elide the tag check when lowering the memaccess
; intrinsic for null pointers.
; 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.
; 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 +27,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