mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 14:06:10 +00:00
hwasan: Use bits [3..11) of the ring buffer entry address as the base 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
This commit is contained in:
parent
fb9ce100d1
commit
d57f7cc15e
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
@ -15,6 +17,7 @@ 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
|
||||
@ -22,14 +25,11 @@ int main(int argc, char **argv) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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):
|
||||
|
@ -218,7 +218,7 @@ public:
|
||||
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;
|
||||
@ -284,6 +284,7 @@ private:
|
||||
Constant *ShadowGlobal;
|
||||
|
||||
Value *LocalDynamicShadow = nullptr;
|
||||
Value *StackBaseTag = nullptr;
|
||||
GlobalValue *ThreadPtrGlobal = nullptr;
|
||||
};
|
||||
|
||||
@ -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]))];
|
||||
}
|
||||
|
||||
@ -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();
|
||||
@ -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);
|
||||
@ -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 =
|
||||
@ -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)
|
||||
@ -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(
|
||||
@ -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()) {
|
||||
@ -1146,6 +1159,7 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F) {
|
||||
Changed |= instrumentMemAccess(Inst);
|
||||
|
||||
LocalDynamicShadow = nullptr;
|
||||
StackBaseTag = nullptr;
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user