Skip to content

Commit

Permalink
hwasan: Use bits [3..11) of the ring buffer entry address as the base…
Browse files Browse the repository at this point in the history
… stack tag.

This saves roughly 32 bytes of instructions per function with stack objects
and causes us to preserve enough information that we can recover the original
tags of all stack variables.

Now that stack tags are deterministic, we no longer need to pass
-hwasan-generate-tags-with-calls during check-hwasan. This also means that
the new stack tag generation mechanism is exercised by check-hwasan.

Differential Revision: https://reviews.llvm.org/D63360

llvm-svn: 363636
  • Loading branch information
pcc committed Jun 17, 2019
1 parent fb9ce10 commit d57f7cc
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 37 deletions.
5 changes: 5 additions & 0 deletions compiler-rt/lib/hwasan/hwasan_thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ static u32 RandomSeed() {

void Thread::InitRandomState() {
random_state_ = flags()->random_tags ? RandomSeed() : unique_id_;

// Push a random number of zeros onto the ring buffer so that the first stack
// tag base will be random.
for (tag_t i = 0, e = GenerateRandomTag(); i != e; ++i)
stack_allocations_->push(0);
}

void Thread::Init(uptr stack_buffer_start, uptr stack_buffer_size) {
Expand Down
22 changes: 11 additions & 11 deletions compiler-rt/test/hwasan/TestCases/random-align-right.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Tests malloc_align_right=1 and 8 (randomly aligning right).
// RUN: %clang_hwasan %s -o %t
//
// RUN: %run %t
// RUN: %env_hwasan_opts=malloc_align_right=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1
// RUN: %env_hwasan_opts=malloc_align_right=8 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK8
// RUN: %run %t 20
// RUN: %run %t 30
// RUN: %env_hwasan_opts=malloc_align_right=1 not %run %t 20 2>&1 | FileCheck %s --check-prefix=CHECK20
// RUN: %env_hwasan_opts=malloc_align_right=1 not %run %t 30 2>&1 | FileCheck %s --check-prefix=CHECK30
// RUN: %env_hwasan_opts=malloc_align_right=8 not %run %t 30 2>&1 | FileCheck %s --check-prefix=CHECK30

// REQUIRES: stable-runtime

Expand All @@ -15,21 +17,19 @@ static volatile void *sink;

int main(int argc, char **argv) {
__hwasan_enable_allocator_tagging();
int index = atoi(argv[1]);

// Perform 1000 buffer overflows within the 16-byte granule,
// so that random right-alignment has a very high chance of
// catching at least one of them.
for (int i = 0; i < 1000; i++) {
char *p = (char*)malloc(20);
sink = p;
fprintf(stderr, "[%d] p: %p; accessing p[20]:\n", i, p);
p[20 * argc] = 0; // requires malloc_align_right=1 to catch
fprintf(stderr, "[%d] p: %p; accessing p[30]:\n", i, p);
p[30 * argc] = 0; // requires malloc_align_right={1,8} to catch
// CHECK1: accessing p[20]
// CHECK1-NEXT: HWAddressSanitizer: tag-mismatch
// CHECK8: accessing p[30]:
// CHECK8-NEXT: HWAddressSanitizer: tag-mismatch
p[index] = 0;
// index=20 requires malloc_align_right=1 to catch
// CHECK20: HWAddressSanitizer: tag-mismatch
// index=30 requires malloc_align_right={1,8} to catch
// CHECK30: HWAddressSanitizer: tag-mismatch
}
}

5 changes: 4 additions & 1 deletion compiler-rt/test/hwasan/TestCases/stack-history-length.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %clang_hwasan -O1 %s -o %t
// RUN: %env_hwasan_opts=stack_history_size=2048 not %run %t 2046 2>&1 | FileCheck %s --check-prefix=YES
// RUN: %env_hwasan_opts=stack_history_size=2048 not %run %t 2045 2>&1 | FileCheck %s --check-prefix=YES
// RUN: %env_hwasan_opts=stack_history_size=2048 not %run %t 2047 2>&1 | FileCheck %s --check-prefix=NO

// REQUIRES: stable-runtime
Expand All @@ -22,6 +22,9 @@ int main(int argc, char **argv) {
FUNC0();
for (int i = 0; i < X; ++i)
FUNC();
// Make at least one call to OOB where base tag != 0 so that the bug is caught
// at least once.
OOB();
OOB();
}

Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/test/hwasan/lit.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ config.test_source_root = os.path.dirname(__file__)
# Setup default compiler flags used with -fsanitize=memory option.
clang_cflags = [config.target_cflags] + config.debug_info_flags
clang_cxxflags = config.cxx_mode_flags + clang_cflags
clang_hwasan_cflags = ["-fsanitize=hwaddress", "-mllvm", "-hwasan-generate-tags-with-calls"] + clang_cflags
clang_hwasan_cflags = ["-fsanitize=hwaddress"] + clang_cflags
clang_hwasan_cxxflags = config.cxx_mode_flags + clang_hwasan_cflags

def build_invocation(compile_flags):
Expand Down
50 changes: 32 additions & 18 deletions llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ class HWAddressSanitizer {
Value *getUARTag(IRBuilder<> &IRB, Value *StackTag);

Value *getHwasanThreadSlotPtr(IRBuilder<> &IRB, Type *Ty);
Value *emitPrologue(IRBuilder<> &IRB, bool WithFrameRecord);
void emitPrologue(IRBuilder<> &IRB, bool WithFrameRecord);

private:
LLVMContext *C;
Expand Down Expand Up @@ -284,6 +284,7 @@ class HWAddressSanitizer {
Constant *ShadowGlobal;

Value *LocalDynamicShadow = nullptr;
Value *StackBaseTag = nullptr;
GlobalValue *ThreadPtrGlobal = nullptr;
};

Expand Down Expand Up @@ -750,10 +751,16 @@ static unsigned RetagMask(unsigned AllocaNo) {
// x = x ^ (mask << 56) can be encoded as a single armv8 instruction for these
// masks.
// The list does not include the value 255, which is used for UAR.
static unsigned FastMasks[] = {
0, 1, 2, 3, 4, 6, 7, 8, 12, 14, 15, 16, 24,
28, 30, 31, 32, 48, 56, 60, 62, 63, 64, 96, 112, 120,
124, 126, 127, 128, 192, 224, 240, 248, 252, 254};
//
// Because we are more likely to use earlier elements of this list than later
// ones, it is sorted in increasing order of probability of collision with a
// mask allocated (temporally) nearby. The program that generated this list
// can be found at:
// https://github.com/google/sanitizers/blob/master/hwaddress-sanitizer/sort_masks.py
static unsigned FastMasks[] = {0, 128, 64, 192, 32, 96, 224, 112, 240,
48, 16, 120, 248, 56, 24, 8, 124, 252,
60, 28, 12, 4, 126, 254, 62, 30, 14,
6, 2, 127, 63, 31, 15, 7, 3, 1};
return FastMasks[AllocaNo % (sizeof(FastMasks) / sizeof(FastMasks[0]))];
}

Expand All @@ -764,6 +771,8 @@ Value *HWAddressSanitizer::getNextTagWithCall(IRBuilder<> &IRB) {
Value *HWAddressSanitizer::getStackBaseTag(IRBuilder<> &IRB) {
if (ClGenerateTagsWithCalls)
return getNextTagWithCall(IRB);
if (StackBaseTag)
return StackBaseTag;
// FIXME: use addressofreturnaddress (but implement it in aarch64 backend
// first).
Module *M = IRB.GetInsertBlock()->getParent()->getParent();
Expand Down Expand Up @@ -881,13 +890,16 @@ void HWAddressSanitizer::createFrameGlobal(Function &F,
GV->setComdat(Comdat);
}

Value *HWAddressSanitizer::emitPrologue(IRBuilder<> &IRB,
bool WithFrameRecord) {
if (!Mapping.InTls)
return getDynamicShadowNonTls(IRB);
void HWAddressSanitizer::emitPrologue(IRBuilder<> &IRB, bool WithFrameRecord) {
if (!Mapping.InTls) {
LocalDynamicShadow = getDynamicShadowNonTls(IRB);
return;
}

if (!WithFrameRecord && TargetTriple.isAndroid())
return getDynamicShadowIfunc(IRB);
if (!WithFrameRecord && TargetTriple.isAndroid()) {
LocalDynamicShadow = getDynamicShadowIfunc(IRB);
return;
}

Value *SlotPtr = getHwasanThreadSlotPtr(IRB, IntptrTy);
assert(SlotPtr);
Expand Down Expand Up @@ -920,6 +932,8 @@ Value *HWAddressSanitizer::emitPrologue(IRBuilder<> &IRB,
TargetTriple.isAArch64() ? ThreadLong : untagPointer(IRB, ThreadLong);

if (WithFrameRecord) {
StackBaseTag = IRB.CreateAShr(ThreadLong, 3);

// Prepare ring buffer data.
auto PC = IRB.CreatePtrToInt(F, IntptrTy);
auto GetStackPointerFn =
Expand All @@ -928,7 +942,7 @@ Value *HWAddressSanitizer::emitPrologue(IRBuilder<> &IRB,
IRB.CreateCall(GetStackPointerFn,
{Constant::getNullValue(IRB.getInt32Ty())}),
IntptrTy);
// Mix SP and PC. TODO: also add the tag to the mix.
// Mix SP and PC.
// Assumptions:
// PC is 0x0000PPPPPPPPPPPP (48 bits are meaningful, others are zero)
// SP is 0xsssssssssssSSSS0 (4 lower bits are zero)
Expand Down Expand Up @@ -959,13 +973,12 @@ Value *HWAddressSanitizer::emitPrologue(IRBuilder<> &IRB,
// Get shadow base address by aligning RecordPtr up.
// Note: this is not correct if the pointer is already aligned.
// Runtime library will make sure this never happens.
Value *ShadowBase = IRB.CreateAdd(
LocalDynamicShadow = IRB.CreateAdd(
IRB.CreateOr(
ThreadLongMaybeUntagged,
ConstantInt::get(IntptrTy, (1ULL << kShadowBaseAlignment) - 1)),
ConstantInt::get(IntptrTy, 1), "hwasan.shadow");
ShadowBase = IRB.CreateIntToPtr(ShadowBase, Int8PtrTy);
return ShadowBase;
LocalDynamicShadow = IRB.CreateIntToPtr(LocalDynamicShadow, Int8PtrTy);
}

bool HWAddressSanitizer::instrumentLandingPads(
Expand Down Expand Up @@ -1115,9 +1128,9 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F) {

Instruction *InsertPt = &*F.getEntryBlock().begin();
IRBuilder<> EntryIRB(InsertPt);
LocalDynamicShadow = emitPrologue(EntryIRB,
/*WithFrameRecord*/ ClRecordStackHistory &&
!AllocasToInstrument.empty());
emitPrologue(EntryIRB,
/*WithFrameRecord*/ ClRecordStackHistory &&
!AllocasToInstrument.empty());

bool Changed = false;
if (!AllocasToInstrument.empty()) {
Expand Down Expand Up @@ -1146,6 +1159,7 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F) {
Changed |= instrumentMemAccess(Inst);

LocalDynamicShadow = nullptr;
StackBaseTag = nullptr;

return Changed;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ entry:
%nodebug3 = alloca i8*
%a = alloca i8*
%b = alloca i8*
; CHECK: @llvm.dbg.declare{{.*}} !DIExpression(DW_OP_LLVM_tag_offset, 4)
; CHECK: @llvm.dbg.declare{{.*}} !DIExpression(DW_OP_LLVM_tag_offset, 32)
call void @llvm.dbg.declare(metadata i8** %a, metadata !12, metadata !DIExpression()), !dbg !14
; CHECK: @llvm.dbg.declare{{.*}} !DIExpression(DW_OP_LLVM_tag_offset, 4)
; CHECK: @llvm.dbg.declare{{.*}} !DIExpression(DW_OP_LLVM_tag_offset, 32)
call void @llvm.dbg.declare(metadata i8** %a, metadata !12, metadata !DIExpression()), !dbg !14
; CHECK: @llvm.dbg.declare{{.*}} !DIExpression(DW_OP_LLVM_tag_offset, 6)
; CHECK: @llvm.dbg.declare{{.*}} !DIExpression(DW_OP_LLVM_tag_offset, 96)
call void @llvm.dbg.declare(metadata i8** %b, metadata !13, metadata !DIExpression()), !dbg !14
; CHECK: @llvm.dbg.declare{{.*}} !DIExpression(DW_OP_LLVM_tag_offset, 6)
; CHECK: @llvm.dbg.declare{{.*}} !DIExpression(DW_OP_LLVM_tag_offset, 96)
call void @llvm.dbg.declare(metadata i8** %b, metadata !13, metadata !DIExpression()), !dbg !14
call void @g(i8** %nodebug0, i8** %nodebug1, i8** %nodebug2, i8** %nodebug3, i8** %a, i8** %b)
ret void, !dbg !15
Expand Down
7 changes: 5 additions & 2 deletions llvm/test/Instrumentation/HWAddressSanitizer/prologue.ll
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ define void @test_alloca() sanitize_hwaddress {
; CHECK-TLS: %[[B:[^ ]*]] = getelementptr i8, i8* %[[A]], i32 48
; CHECK-TLS: %[[C:[^ ]*]] = bitcast i8* %[[B]] to i64*
; CHECK-TLS: %[[D:[^ ]*]] = load i64, i64* %[[C]]
; CHECK-TLS: %[[E:[^ ]*]] = ashr i64 %[[D]], 3

; CHECK-NOHISTORY-NOT: store i64

Expand All @@ -68,8 +69,10 @@ define void @test_alloca() sanitize_hwaddress {
; CHECK-HISTORY: %[[D5:[^ ]*]] = and i64 %[[D4]], %[[D3]]
; CHECK-HISTORY: store i64 %[[D5]], i64* %[[C]]

; CHECK-TLS: %[[E:[^ ]*]] = or i64 %[[D]], 4294967295
; CHECK-TLS: = add i64 %[[E]], 1
; CHECK-TLS: %[[F:[^ ]*]] = or i64 %[[D]], 4294967295
; CHECK-TLS: = add i64 %[[F]], 1

; CHECK-HISTORY: = xor i64 %[[E]], 0

; CHECK-NOHISTORY-NOT: store i64

Expand Down

0 comments on commit d57f7cc

Please sign in to comment.