122 changes: 78 additions & 44 deletions compiler-rt/lib/hwasan/hwasan_report.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,19 @@ static void PrintStackAllocations(StackAllocationsRingBuffer *sa,
}
}

// Returns true if tag == *tag_ptr, reading tags from short granules if
// necessary. This may return a false positive if tags 1-15 are used as a
// regular tag rather than a short granule marker.
static bool TagsEqual(tag_t tag, tag_t *tag_ptr) {
if (tag == *tag_ptr)
return true;
if (*tag_ptr == 0 || *tag_ptr > kShadowAlignment - 1)
return false;
uptr mem = ShadowToMem(reinterpret_cast<uptr>(tag_ptr));
tag_t inline_tag = *reinterpret_cast<tag_t *>(mem + kShadowAlignment - 1);
return tag == inline_tag;
}

void PrintAddressDescription(
uptr tagged_addr, uptr access_size,
StackAllocationsRingBuffer *current_stack_allocations) {
Expand Down Expand Up @@ -235,39 +248,36 @@ void PrintAddressDescription(
// check the allocator if it has a live chunk there.
tag_t addr_tag = GetTagFromPointer(tagged_addr);
tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
if (*tag_ptr != addr_tag) { // should be true usually.
tag_t *left = tag_ptr, *right = tag_ptr;
// scan left.
for (int i = 0; i < 1000 && *left == *tag_ptr; i++, left--){}
// scan right.
for (int i = 0; i < 1000 && *right == *tag_ptr; i++, right++){}
// Chose the object that has addr_tag and that is closer to addr.
tag_t *candidate = nullptr;
if (*right == addr_tag && *left == addr_tag)
candidate = right - tag_ptr < tag_ptr - left ? right : left;
else if (*right == addr_tag)
candidate = right;
else if (*left == addr_tag)
tag_t *candidate = nullptr, *left = tag_ptr, *right = tag_ptr;
for (int i = 0; i < 1000; i++) {
if (TagsEqual(addr_tag, left)) {
candidate = left;
break;
}
--left;
if (TagsEqual(addr_tag, right)) {
candidate = right;
break;
}
++right;
}

if (candidate) {
uptr mem = ShadowToMem(reinterpret_cast<uptr>(candidate));
HwasanChunkView chunk = FindHeapChunkByAddress(mem);
if (chunk.IsAllocated()) {
Printf("%s", d.Location());
Printf(
"%p is located %zd bytes to the %s of %zd-byte region [%p,%p)\n",
untagged_addr,
candidate == left ? untagged_addr - chunk.End()
: chunk.Beg() - untagged_addr,
candidate == right ? "left" : "right", chunk.UsedSize(),
chunk.Beg(), chunk.End());
Printf("%s", d.Allocation());
Printf("allocated here:\n");
Printf("%s", d.Default());
GetStackTraceFromId(chunk.GetAllocStackId()).Print();
num_descriptions_printed++;
}
if (candidate) {
uptr mem = ShadowToMem(reinterpret_cast<uptr>(candidate));
HwasanChunkView chunk = FindHeapChunkByAddress(mem);
if (chunk.IsAllocated()) {
Printf("%s", d.Location());
Printf("%p is located %zd bytes to the %s of %zd-byte region [%p,%p)\n",
untagged_addr,
candidate == left ? untagged_addr - chunk.End()
: chunk.Beg() - untagged_addr,
candidate == left ? "right" : "left", chunk.UsedSize(),
chunk.Beg(), chunk.End());
Printf("%s", d.Allocation());
Printf("allocated here:\n");
Printf("%s", d.Default());
GetStackTraceFromId(chunk.GetAllocStackId()).Print();
num_descriptions_printed++;
}
}

Expand Down Expand Up @@ -325,13 +335,10 @@ void PrintAddressDescription(

void ReportStats() {}

static void PrintTagsAroundAddr(tag_t *tag_ptr) {
Printf(
"Memory tags around the buggy address (one tag corresponds to %zd "
"bytes):\n", kShadowAlignment);

static void PrintTagInfoAroundAddr(tag_t *tag_ptr, uptr num_rows,
void (*print_tag)(InternalScopedString &s,
tag_t *tag)) {
const uptr row_len = 16; // better be power of two.
const uptr num_rows = 17;
tag_t *center_row_beg = reinterpret_cast<tag_t *>(
RoundDownTo(reinterpret_cast<uptr>(tag_ptr), row_len));
tag_t *beg_row = center_row_beg - row_len * (num_rows / 2);
Expand All @@ -341,14 +348,42 @@ static void PrintTagsAroundAddr(tag_t *tag_ptr) {
s.append("%s", row == center_row_beg ? "=>" : " ");
for (uptr i = 0; i < row_len; i++) {
s.append("%s", row + i == tag_ptr ? "[" : " ");
s.append("%02x", row[i]);
print_tag(s, &row[i]);
s.append("%s", row + i == tag_ptr ? "]" : " ");
}
s.append("%s\n", row == center_row_beg ? "<=" : " ");
}
Printf("%s", s.data());
}

static void PrintTagsAroundAddr(tag_t *tag_ptr) {
Printf(
"Memory tags around the buggy address (one tag corresponds to %zd "
"bytes):\n", kShadowAlignment);
PrintTagInfoAroundAddr(tag_ptr, 17, [](InternalScopedString &s, tag_t *tag) {
s.append("%02x", *tag);
});

Printf(
"Tags for short granules around the buggy address (one tag corresponds "
"to %zd bytes):\n",
kShadowAlignment);
PrintTagInfoAroundAddr(tag_ptr, 3, [](InternalScopedString &s, tag_t *tag) {
if (*tag >= 1 && *tag <= kShadowAlignment) {
uptr granule_addr = ShadowToMem(reinterpret_cast<uptr>(tag));
s.append("%02x",
*reinterpret_cast<u8 *>(granule_addr + kShadowAlignment - 1));
} else {
s.append("..");
}
});
Printf(
"See "
"https://clang.llvm.org/docs/"
"HardwareAssistedAddressSanitizerDesign.html#short-granules for a "
"description of short granule tags\n");
}

void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
ScopedReport R(flags()->halt_on_error);

Expand Down Expand Up @@ -376,7 +411,8 @@ void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
}

void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
uptr tail_size, const u8 *expected) {
const u8 *expected) {
uptr tail_size = kShadowAlignment - (orig_size % kShadowAlignment);
ScopedReport R(flags()->halt_on_error);
Decorator d;
uptr untagged_addr = UntagAddr(tagged_addr);
Expand Down Expand Up @@ -420,11 +456,9 @@ void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
"to the right of a heap object, but within the %zd-byte granule, e.g.\n"
" char *x = new char[20];\n"
" x[25] = 42;\n"
"By default %s does not detect such bugs at the time of write,\n"
"but can detect them at the time of free/delete.\n"
"To disable this feature set HWASAN_OPTIONS=free_checks_tail_magic=0;\n"
"To enable checking at the time of access, set "
"HWASAN_OPTIONS=malloc_align_right to non-zero\n\n",
"%s does not detect such bugs in uninstrumented code at the time of write,"
"\nbut can detect them at the time of free/delete.\n"
"To disable this feature set HWASAN_OPTIONS=free_checks_tail_magic=0\n",
kShadowAlignment, SanitizerToolName);
Printf("%s", s.data());
GetCurrentThread()->Announce();
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/hwasan/hwasan_report.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
bool is_store, bool fatal, uptr *registers_frame);
void ReportInvalidFree(StackTrace *stack, uptr addr);
void ReportTailOverwritten(StackTrace *stack, uptr addr, uptr orig_size,
uptr tail_size, const u8 *expected);
const u8 *expected);
void ReportRegisters(uptr *registers_frame, uptr pc);
void ReportAtExitStatistics();

Expand Down
37 changes: 14 additions & 23 deletions compiler-rt/test/hwasan/TestCases/heap-buffer-overflow.c
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
// RUN: %clang_hwasan %s -o %t
// RUN: not %run %t 40 2>&1 | FileCheck %s --check-prefix=CHECK40-LEFT
// RUN: %env_hwasan_opts=malloc_align_right=2 not %run %t 40 2>&1 | FileCheck %s --check-prefix=CHECK40-RIGHT
// RUN: not %run %t 80 2>&1 | FileCheck %s --check-prefix=CHECK80-LEFT
// RUN: %env_hwasan_opts=malloc_align_right=2 not %run %t 80 2>&1 | FileCheck %s --check-prefix=CHECK80-RIGHT
// RUN: not %run %t 40 2>&1 | FileCheck %s --check-prefix=CHECK40
// RUN: not %run %t 80 2>&1 | FileCheck %s --check-prefix=CHECK80
// RUN: not %run %t -30 2>&1 | FileCheck %s --check-prefix=CHECKm30
// RUN: not %run %t -30 1000000 2>&1 | FileCheck %s --check-prefix=CHECKMm30
// RUN: not %run %t 1000000 1000000 2>&1 | FileCheck %s --check-prefix=CHECKM

// Test OOB within the granule.
// Misses the bug when malloc is left-aligned, catches it otherwise.
// RUN: %run %t 31
// RUN: %env_hwasan_opts=malloc_align_right=2 not %run %t 31 2>&1 | FileCheck %s --check-prefix=CHECK31

// RUN: %run %t 30 20
// RUN: %env_hwasan_opts=malloc_align_right=9 not %run %t 30 20 2>&1 | FileCheck %s --check-prefix=CHECK20-RIGHT8

// RUN: %env_hwasan_opts=malloc_align_right=42 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-WRONG-FLAG
// RUN: not %run %t 31 2>&1 | FileCheck %s --check-prefix=CHECK31
// RUN: not %run %t 30 20 2>&1 | FileCheck %s --check-prefix=CHECK20

// REQUIRES: stable-runtime

Expand All @@ -33,15 +25,11 @@ int main(int argc, char **argv) {
fprintf(stderr, "base: %p access: %p\n", x, &x[offset]);
sink = x[offset];

// CHECK40-LEFT: allocated heap chunk; size: 32 offset: 8
// CHECK40-LEFT: is located 10 bytes to the right of 30-byte region
// CHECK40-RIGHT: allocated heap chunk; size: 32 offset:
// CHECK40-RIGHT: is located 10 bytes to the right of 30-byte region
// CHECK40: allocated heap chunk; size: 32 offset: 8
// CHECK40: is located 10 bytes to the right of 30-byte region
//
// CHECK80-LEFT: allocated heap chunk; size: 32 offset: 16
// CHECK80-LEFT: is located 50 bytes to the right of 30-byte region
// CHECK80-RIGHT: allocated heap chunk; size: 32 offset:
// CHECK80-RIGHT: is located 50 bytes to the right of 30-byte region
// CHECK80: allocated heap chunk; size: 32 offset: 16
// CHECK80: is located 50 bytes to the right of 30-byte region
//
// CHECKm30: is located 30 bytes to the left of 30-byte region
//
Expand All @@ -51,10 +39,13 @@ int main(int argc, char **argv) {
// CHECKM: is a large allocated heap chunk; size: 1003520 offset: 1000000
// CHECKM: is located 0 bytes to the right of 1000000-byte region
//
// CHECK31: tags: [[TAG:..]]/0e (ptr/mem)
// CHECK31: is located 1 bytes to the right of 30-byte region
// CHECK31: Memory tags around the buggy address
// CHECK31: [0e]
// CHECK31: Tags for short granules around the buggy address
// CHECK31: {{\[}}[[TAG]]]
//
// CHECK20-RIGHT8: is located 10 bytes to the right of 20-byte region [0x{{.*}}8,0x{{.*}}c)
//
// CHECK-WRONG-FLAG: ERROR: unsupported value of malloc_align_right flag: 42
// CHECK20: is located 10 bytes to the right of 20-byte region [0x{{.*}}0,0x{{.*}}4)
free(x);
}
35 changes: 0 additions & 35 deletions compiler-rt/test/hwasan/TestCases/random-align-right.c

This file was deleted.

3 changes: 2 additions & 1 deletion compiler-rt/test/hwasan/TestCases/stack-oob.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// RUN: %clang_hwasan -DSIZE=15 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clang_hwasan -DSIZE=16 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clang_hwasan -DSIZE=64 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clang_hwasan -DSIZE=0x1000 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
Expand All @@ -17,7 +18,7 @@ int f() {
int main() {
return f();
// CHECK: READ of size 1 at
// CHECK: #0 {{.*}} in f{{.*}}stack-oob.c:14
// CHECK: #0 {{.*}} in f{{.*}}stack-oob.c:15

// CHECK: is located in stack of threa

Expand Down
16 changes: 11 additions & 5 deletions compiler-rt/test/hwasan/TestCases/tail-magic.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,25 @@
#include <stdio.h>
#include <sanitizer/hwasan_interface.h>

static volatile void *sink;
static volatile char *sink;

// Overwrite the tail in a non-hwasan function so that we don't detect the
// stores as OOB.
__attribute__((no_sanitize("hwaddress"))) void overwrite_tail() {
sink[20] = 0x42;
sink[24] = 0x66;
}

int main(int argc, char **argv) {
__hwasan_enable_allocator_tagging();

char *p = (char*)malloc(20);
sink = p;
p[20] = 0x42;
p[24] = 0x66;
sink = (char *)((uintptr_t)(p) & 0xffffffffffffff);
overwrite_tail();
free(p);
// CHECK: ERROR: HWAddressSanitizer: alocation-tail-overwritten; heap object [{{.*}}) of size 20
// CHECK: in main {{.*}}tail-magic.c:[[@LINE-2]]
// CHECK: allocated here:
// CHECK: in main {{.*}}tail-magic.c:[[@LINE-8]]
// CHECK: in main {{.*}}tail-magic.c:[[@LINE-7]]
// CHECK: Tail contains: .. .. .. .. 42 {{.. .. ..}} 66
}
71 changes: 68 additions & 3 deletions llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,17 +304,82 @@ void AArch64AsmPrinter::EmitHwasanMemaccessSymbols(Module &M) {
.addReg(Reg)
.addImm(AArch64_AM::getShifterImm(AArch64_AM::LSR, 56)),
*STI);
MCSymbol *HandleMismatchSym = OutContext.createTempSymbol();
MCSymbol *HandlePartialSym = OutContext.createTempSymbol();
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::Bcc)
.addImm(AArch64CC::NE)
.addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)),
.addExpr(MCSymbolRefExpr::create(HandlePartialSym, OutContext)),
*STI);
MCSymbol *ReturnSym = OutContext.createTempSymbol();
OutStreamer->EmitLabel(ReturnSym);
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::RET).addReg(AArch64::LR), *STI);

OutStreamer->EmitLabel(HandleMismatchSym);
OutStreamer->EmitLabel(HandlePartialSym);
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::SUBSWri)
.addReg(AArch64::WZR)
.addReg(AArch64::W16)
.addImm(15)
.addImm(0),
*STI);
MCSymbol *HandleMismatchSym = OutContext.createTempSymbol();
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::Bcc)
.addImm(AArch64CC::HI)
.addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)),
*STI);

OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::ANDXri)
.addReg(AArch64::X17)
.addReg(Reg)
.addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)),
*STI);
size_t Size = 1 << (AccessInfo & 0xf);
if (Size != 1)
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::ADDXri)
.addReg(AArch64::X17)
.addReg(AArch64::X17)
.addImm(Size - 1)
.addImm(0),
*STI);
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::SUBSWrs)
.addReg(AArch64::WZR)
.addReg(AArch64::W16)
.addReg(AArch64::W17)
.addImm(0),
*STI);
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::Bcc)
.addImm(AArch64CC::LS)
.addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)),
*STI);

OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::ORRXri)
.addReg(AArch64::X16)
.addReg(Reg)
.addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)),
*STI);
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::LDRBBui)
.addReg(AArch64::W16)
.addReg(AArch64::X16)
.addImm(0),
*STI);
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::SUBSXrs)
.addReg(AArch64::XZR)
.addReg(AArch64::X16)
.addReg(Reg)
.addImm(AArch64_AM::getShifterImm(AArch64_AM::LSR, 56)),
*STI);
OutStreamer->EmitInstruction(
MCInstBuilder(AArch64::Bcc)
.addImm(AArch64CC::EQ)
.addExpr(MCSymbolRefExpr::create(ReturnSym, OutContext)),
*STI);

OutStreamer->EmitLabel(HandleMismatchSym);
OutStreamer->EmitInstruction(MCInstBuilder(AArch64::STPXpre)
.addReg(AArch64::SP)
.addReg(AArch64::X0)
Expand Down
98 changes: 84 additions & 14 deletions llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ class HWAddressSanitizer {
Value **MaybeMask);

bool isInterestingAlloca(const AllocaInst &AI);
bool tagAlloca(IRBuilder<> &IRB, AllocaInst *AI, Value *Tag);
bool tagAlloca(IRBuilder<> &IRB, AllocaInst *AI, Value *Tag, size_t Size);
Value *tagPointer(IRBuilder<> &IRB, Type *Ty, Value *PtrLong, Value *Tag);
Value *untagPointer(IRBuilder<> &IRB, Value *PtrLong);
bool instrumentStack(
Expand Down Expand Up @@ -574,10 +574,35 @@ void HWAddressSanitizer::instrumentMemAccessInline(Value *Ptr, bool IsWrite,
}

Instruction *CheckTerm =
SplitBlockAndInsertIfThen(TagMismatch, InsertBefore, !Recover,
SplitBlockAndInsertIfThen(TagMismatch, InsertBefore, false,
MDBuilder(*C).createBranchWeights(1, 100000));

IRB.SetInsertPoint(CheckTerm);
Value *OutOfShortGranuleTagRange =
IRB.CreateICmpUGT(MemTag, ConstantInt::get(Int8Ty, 15));
Instruction *CheckFailTerm =
SplitBlockAndInsertIfThen(OutOfShortGranuleTagRange, CheckTerm, !Recover,
MDBuilder(*C).createBranchWeights(1, 100000));

IRB.SetInsertPoint(CheckTerm);
Value *PtrLowBits = IRB.CreateTrunc(IRB.CreateAnd(PtrLong, 15), Int8Ty);
PtrLowBits = IRB.CreateAdd(
PtrLowBits, ConstantInt::get(Int8Ty, (1 << AccessSizeIndex) - 1));
Value *PtrLowBitsOOB = IRB.CreateICmpUGE(PtrLowBits, MemTag);
SplitBlockAndInsertIfThen(PtrLowBitsOOB, CheckTerm, false,
MDBuilder(*C).createBranchWeights(1, 100000),
nullptr, nullptr, CheckFailTerm->getParent());

IRB.SetInsertPoint(CheckTerm);
Value *InlineTagAddr = IRB.CreateOr(AddrLong, 15);
InlineTagAddr = IRB.CreateIntToPtr(InlineTagAddr, Int8PtrTy);
Value *InlineTag = IRB.CreateLoad(Int8Ty, InlineTagAddr);
Value *InlineTagMismatch = IRB.CreateICmpNE(PtrTag, InlineTag);
SplitBlockAndInsertIfThen(InlineTagMismatch, CheckTerm, false,
MDBuilder(*C).createBranchWeights(1, 100000),
nullptr, nullptr, CheckFailTerm->getParent());

IRB.SetInsertPoint(CheckFailTerm);
InlineAsm *Asm;
switch (TargetTriple.getArch()) {
case Triple::x86_64:
Expand All @@ -601,6 +626,8 @@ void HWAddressSanitizer::instrumentMemAccessInline(Value *Ptr, bool IsWrite,
report_fatal_error("unsupported architecture");
}
IRB.CreateCall(Asm, PtrLong);
if (Recover)
cast<BranchInst>(CheckFailTerm)->setSuccessor(0, CheckTerm->getParent());
}

void HWAddressSanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) {
Expand Down Expand Up @@ -677,15 +704,14 @@ static uint64_t getAllocaSizeInBytes(const AllocaInst &AI) {
}

bool HWAddressSanitizer::tagAlloca(IRBuilder<> &IRB, AllocaInst *AI,
Value *Tag) {
size_t Size = (getAllocaSizeInBytes(*AI) + Mapping.getAllocaAlignment() - 1) &
~(Mapping.getAllocaAlignment() - 1);
Value *Tag, size_t Size) {
size_t AlignedSize = alignTo(Size, Mapping.getAllocaAlignment());

Value *JustTag = IRB.CreateTrunc(Tag, IRB.getInt8Ty());
if (ClInstrumentWithCalls) {
IRB.CreateCall(HwasanTagMemoryFunc,
{IRB.CreatePointerCast(AI, Int8PtrTy), JustTag,
ConstantInt::get(IntptrTy, Size)});
ConstantInt::get(IntptrTy, AlignedSize)});
} else {
size_t ShadowSize = Size >> Mapping.Scale;
Value *ShadowPtr = memToShadow(IRB.CreatePointerCast(AI, IntptrTy), IRB);
Expand All @@ -695,7 +721,16 @@ bool HWAddressSanitizer::tagAlloca(IRBuilder<> &IRB, AllocaInst *AI,
// FIXME: the interceptor is not as fast as real memset. Consider lowering
// llvm.memset right here into either a sequence of stores, or a call to
// hwasan_tag_memory.
IRB.CreateMemSet(ShadowPtr, JustTag, ShadowSize, /*Align=*/1);
if (ShadowSize)
IRB.CreateMemSet(ShadowPtr, JustTag, ShadowSize, /*Align=*/1);
if (Size != AlignedSize) {
IRB.CreateStore(
ConstantInt::get(Int8Ty, Size % Mapping.getAllocaAlignment()),
IRB.CreateConstGEP1_32(Int8Ty, ShadowPtr, ShadowSize));
IRB.CreateStore(JustTag, IRB.CreateConstGEP1_32(
Int8Ty, IRB.CreateBitCast(AI, Int8PtrTy),
AlignedSize - 1));
}
}
return true;
}
Expand Down Expand Up @@ -964,14 +999,15 @@ bool HWAddressSanitizer::instrumentStack(
DDI->setArgOperand(2, MetadataAsValue::get(*C, NewExpr));
}

tagAlloca(IRB, AI, Tag);
size_t Size = getAllocaSizeInBytes(*AI);
tagAlloca(IRB, AI, Tag, Size);

for (auto RI : RetVec) {
IRB.SetInsertPoint(RI);

// Re-tag alloca memory with the special UAR tag.
Value *Tag = getUARTag(IRB, StackTag);
tagAlloca(IRB, AI, Tag);
tagAlloca(IRB, AI, Tag, alignTo(Size, Mapping.getAllocaAlignment()));
}
}

Expand Down Expand Up @@ -1012,11 +1048,6 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F) {
for (auto &Inst : BB) {
if (ClInstrumentStack)
if (AllocaInst *AI = dyn_cast<AllocaInst>(&Inst)) {
// Realign all allocas. We don't want small uninteresting allocas to
// hide in instrumented alloca's padding.
if (AI->getAlignment() < Mapping.getAllocaAlignment())
AI->setAlignment(Mapping.getAllocaAlignment());
// Instrument some of them.
if (isInterestingAlloca(*AI))
AllocasToInstrument.push_back(AI);
continue;
Expand Down Expand Up @@ -1068,6 +1099,45 @@ bool HWAddressSanitizer::sanitizeFunction(Function &F) {
StackTag);
}

// Pad and align each of the allocas that we instrumented to stop small
// uninteresting allocas from hiding in instrumented alloca's padding and so
// that we have enough space to store real tags for short granules.
DenseMap<AllocaInst *, AllocaInst *> AllocaToPaddedAllocaMap;
for (AllocaInst *AI : AllocasToInstrument) {
uint64_t Size = getAllocaSizeInBytes(*AI);
uint64_t AlignedSize = alignTo(Size, Mapping.getAllocaAlignment());
AI->setAlignment(std::max(AI->getAlignment(), 16u));
if (Size != AlignedSize) {
Type *TypeWithPadding = StructType::get(
AI->getAllocatedType(), ArrayType::get(Int8Ty, AlignedSize - Size));
auto *NewAI = new AllocaInst(
TypeWithPadding, AI->getType()->getAddressSpace(), nullptr, "", AI);
NewAI->takeName(AI);
NewAI->setAlignment(AI->getAlignment());
NewAI->setUsedWithInAlloca(AI->isUsedWithInAlloca());
NewAI->setSwiftError(AI->isSwiftError());
NewAI->copyMetadata(*AI);
Value *Zero = ConstantInt::get(Int32Ty, 0);
auto *GEP = GetElementPtrInst::Create(TypeWithPadding, NewAI,
{Zero, Zero}, "", AI);
AI->replaceAllUsesWith(GEP);
AllocaToPaddedAllocaMap[AI] = NewAI;
}
}

if (!AllocaToPaddedAllocaMap.empty()) {
for (auto &BB : F)
for (auto &Inst : BB)
if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(&Inst))
if (auto *AI =
dyn_cast_or_null<AllocaInst>(DVI->getVariableLocation()))
if (auto *NewAI = AllocaToPaddedAllocaMap.lookup(AI))
DVI->setArgOperand(
0, MetadataAsValue::get(*C, LocalAsMetadata::get(NewAI)));
for (auto &P : AllocaToPaddedAllocaMap)
P.first->eraseFromParent();
}

// If we split the entry block, move any allocas that were originally in the
// entry block back into the entry block so that they aren't treated as
// dynamic allocas.
Expand Down
28 changes: 26 additions & 2 deletions llvm/test/CodeGen/AArch64/hwasan-check-memaccess.ll
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,20 @@ declare void @llvm.hwasan.check.memaccess(i8*, i8*, i32)
; CHECK-NEXT: ldrb w16, [x9, x16]
; CHECK-NEXT: cmp x16, x0, lsr #56
; CHECK-NEXT: b.ne .Ltmp0
; CHECK-NEXT: .Ltmp1:
; CHECK-NEXT: ret
; CHECK-NEXT: .Ltmp0:
; CHECK-NEXT: cmp w16, #15
; CHECK-NEXT: b.hi .Ltmp2
; CHECK-NEXT: and x17, x0, #0xf
; CHECK-NEXT: add x17, x17, #255
; CHECK-NEXT: cmp w16, w17
; CHECK-NEXT: b.ls .Ltmp2
; CHECK-NEXT: orr x16, x0, #0xf
; CHECK-NEXT: ldrb w16, [x16]
; CHECK-NEXT: cmp x16, x0, lsr #56
; CHECK-NEXT: b.eq .Ltmp1
; CHECK-NEXT: .Ltmp2:
; CHECK-NEXT: stp x0, x1, [sp, #-256]!
; CHECK-NEXT: stp x29, x30, [sp, #232]
; CHECK-NEXT: mov x1, #456
Expand All @@ -58,9 +70,21 @@ declare void @llvm.hwasan.check.memaccess(i8*, i8*, i32)
; CHECK-NEXT: ubfx x16, x1, #4, #52
; CHECK-NEXT: ldrb w16, [x9, x16]
; CHECK-NEXT: cmp x16, x1, lsr #56
; CHECK-NEXT: b.ne .Ltmp1
; CHECK-NEXT: b.ne .Ltmp3
; CHECK-NEXT: .Ltmp4:
; CHECK-NEXT: ret
; CHECK-NEXT: .Ltmp1:
; CHECK-NEXT: .Ltmp3:
; CHECK-NEXT: cmp w16, #15
; CHECK-NEXT: b.hi .Ltmp5
; CHECK-NEXT: and x17, x1, #0xf
; CHECK-NEXT: add x17, x17, #2047
; CHECK-NEXT: cmp w16, w17
; CHECK-NEXT: b.ls .Ltmp5
; CHECK-NEXT: orr x16, x1, #0xf
; CHECK-NEXT: ldrb w16, [x16]
; CHECK-NEXT: cmp x16, x1, lsr #56
; CHECK-NEXT: b.eq .Ltmp4
; CHECK-NEXT: .Ltmp5:
; CHECK-NEXT: stp x0, x1, [sp, #-256]!
; CHECK-NEXT: stp x29, x30, [sp, #232]
; CHECK-NEXT: mov x0, x1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ declare void @use32(i32*)

define void @test_alloca() sanitize_hwaddress {
; CHECK-LABEL: @test_alloca(
; CHECK: %[[GEP:[^ ]*]] = getelementptr { i32, [12 x i8] }, { i32, [12 x i8] }* %x, i32 0, i32 0
; CHECK: %[[T1:[^ ]*]] = call i8 @__hwasan_generate_tag()
; CHECK: %[[A:[^ ]*]] = zext i8 %[[T1]] to i64
; CHECK: %[[B:[^ ]*]] = ptrtoint i32* %x to i64
; CHECK: %[[B:[^ ]*]] = ptrtoint i32* %[[GEP]] to i64
; CHECK: %[[C:[^ ]*]] = shl i64 %[[A]], 56
; CHECK: or i64 %[[B]], %[[C]]

Expand Down
15 changes: 10 additions & 5 deletions llvm/test/Instrumentation/HWAddressSanitizer/alloca.ll
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,29 @@ define void @test_alloca() sanitize_hwaddress {
; CHECK: %[[B:[^ ]*]] = lshr i64 %[[A]], 20
; CHECK: %[[BASE_TAG:[^ ]*]] = xor i64 %[[A]], %[[B]]

; CHECK: %[[X:[^ ]*]] = alloca i32, align 16
; CHECK: %[[X:[^ ]*]] = alloca { i32, [12 x i8] }, align 16
; CHECK: %[[X_GEP:[^ ]*]] = getelementptr { i32, [12 x i8] }, { i32, [12 x i8] }* %[[X]], i32 0, i32 0
; CHECK: %[[X_TAG:[^ ]*]] = xor i64 %[[BASE_TAG]], 0
; CHECK: %[[X1:[^ ]*]] = ptrtoint i32* %[[X]] to i64
; CHECK: %[[X1:[^ ]*]] = ptrtoint i32* %[[X_GEP]] to i64
; CHECK: %[[C:[^ ]*]] = shl i64 %[[X_TAG]], 56
; CHECK: %[[D:[^ ]*]] = or i64 %[[X1]], %[[C]]
; CHECK: %[[X_HWASAN:[^ ]*]] = inttoptr i64 %[[D]] to i32*

; CHECK: %[[X_TAG2:[^ ]*]] = trunc i64 %[[X_TAG]] to i8
; CHECK: %[[E:[^ ]*]] = ptrtoint i32* %[[X]] to i64
; CHECK: %[[E:[^ ]*]] = ptrtoint i32* %[[X_GEP]] to i64
; CHECK: %[[F:[^ ]*]] = lshr i64 %[[E]], 4
; DYNAMIC-SHADOW: %[[X_SHADOW:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %[[F]]
; ZERO-BASED-SHADOW: %[[X_SHADOW:[^ ]*]] = inttoptr i64 %[[F]] to i8*
; CHECK: call void @llvm.memset.p0i8.i64(i8* align 1 %[[X_SHADOW]], i8 %[[X_TAG2]], i64 1, i1 false)
; CHECK: %[[X_SHADOW_GEP:[^ ]*]] = getelementptr i8, i8* %[[X_SHADOW]], i32 0
; CHECK: store i8 4, i8* %[[X_SHADOW_GEP]]
; CHECK: %[[X_I8:[^ ]*]] = bitcast i32* %[[X_GEP]] to i8*
; CHECK: %[[X_I8_GEP:[^ ]*]] = getelementptr i8, i8* %[[X_I8]], i32 15
; CHECK: store i8 %[[X_TAG2]], i8* %[[X_I8_GEP]]
; CHECK: call void @use32(i32* nonnull %[[X_HWASAN]])

; UAR-TAGS: %[[BASE_TAG_COMPL:[^ ]*]] = xor i64 %[[BASE_TAG]], 255
; UAR-TAGS: %[[X_TAG_UAR:[^ ]*]] = trunc i64 %[[BASE_TAG_COMPL]] to i8
; CHECK: %[[E2:[^ ]*]] = ptrtoint i32* %[[X]] to i64
; CHECK: %[[E2:[^ ]*]] = ptrtoint i32* %[[X_GEP]] to i64
; CHECK: %[[F2:[^ ]*]] = lshr i64 %[[E2]], 4
; DYNAMIC-SHADOW: %[[X_SHADOW2:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %[[F2]]
; ZERO-BASED-SHADOW: %[[X_SHADOW2:[^ ]*]] = inttoptr i64 %[[F2]] to i8*
Expand Down
52 changes: 50 additions & 2 deletions llvm/test/Instrumentation/HWAddressSanitizer/basic.ll
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,35 @@ define i8 @test_load8(i8* %a) sanitize_hwaddress {
; RECOVER-ZERO-BASED-SHADOW: %[[E:[^ ]*]] = inttoptr i64 %[[D]] to i8*
; RECOVER: %[[MEMTAG:[^ ]*]] = load i8, i8* %[[E]]
; RECOVER: %[[F:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[MEMTAG]]
; RECOVER: br i1 %[[F]], label {{.*}}, label {{.*}}, !prof {{.*}}
; RECOVER: br i1 %[[F]], label %[[MISMATCH:[0-9]*]], label %[[CONT:[0-9]*]], !prof {{.*}}

; RECOVER: [[MISMATCH]]:
; RECOVER: %[[NOTSHORT:[^ ]*]] = icmp ugt i8 %[[MEMTAG]], 15
; RECOVER: br i1 %[[NOTSHORT]], label %[[FAIL:[0-9]*]], label %[[SHORT:[0-9]*]], !prof {{.*}}

; RECOVER: [[FAIL]]:
; RECOVER: call void asm sideeffect "brk #2336", "{x0}"(i64 %[[A]])
; RECOVER: br label

; RECOVER: [[SHORT]]:
; RECOVER: %[[LOWBITS:[^ ]*]] = and i64 %[[A]], 15
; RECOVER: %[[LOWBITS_I8:[^ ]*]] = trunc i64 %[[LOWBITS]] to i8
; RECOVER: %[[LAST:[^ ]*]] = add i8 %[[LOWBITS_I8]], 0
; RECOVER: %[[OOB:[^ ]*]] = icmp uge i8 %[[LAST]], %[[MEMTAG]]
; RECOVER: br i1 %[[OOB]], label %[[FAIL]], label %[[INBOUNDS:[0-9]*]], !prof {{.*}}

; RECOVER: [[INBOUNDS]]:
; RECOVER: %[[EOG_ADDR:[^ ]*]] = or i64 %[[C]], 15
; RECOVER: %[[EOG_PTR:[^ ]*]] = inttoptr i64 %[[EOG_ADDR]] to i8*
; RECOVER: %[[EOGTAG:[^ ]*]] = load i8, i8* %[[EOG_PTR]]
; RECOVER: %[[EOG_MISMATCH:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[EOGTAG]]
; RECOVER: br i1 %[[EOG_MISMATCH]], label %[[FAIL]], label %[[CONT1:[0-9]*]], !prof {{.*}}

; RECOVER: [[CONT1]]:
; RECOVER: br label %[[CONT]]

; RECOVER: [[CONT]]:

; ABORT-DYNAMIC-SHADOW: call void @llvm.hwasan.check.memaccess(i8* %.hwasan.shadow, i8* %a, i32 0)
; ABORT-ZERO-BASED-SHADOW: call void @llvm.hwasan.check.memaccess(i8* null, i8* %a, i32 0)

Expand All @@ -54,11 +78,35 @@ define i16 @test_load16(i16* %a) sanitize_hwaddress {
; RECOVER-ZERO-BASED-SHADOW: %[[E:[^ ]*]] = inttoptr i64 %[[D]] to i8*
; RECOVER: %[[MEMTAG:[^ ]*]] = load i8, i8* %[[E]]
; RECOVER: %[[F:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[MEMTAG]]
; RECOVER: br i1 %[[F]], label {{.*}}, label {{.*}}, !prof {{.*}}
; RECOVER: br i1 %[[F]], label %[[MISMATCH:[0-9]*]], label %[[CONT:[0-9]*]], !prof {{.*}}

; RECOVER: [[MISMATCH]]:
; RECOVER: %[[NOTSHORT:[^ ]*]] = icmp ugt i8 %[[MEMTAG]], 15
; RECOVER: br i1 %[[NOTSHORT]], label %[[FAIL:[0-9]*]], label %[[SHORT:[0-9]*]], !prof {{.*}}

; RECOVER: [[FAIL]]:
; RECOVER: call void asm sideeffect "brk #2337", "{x0}"(i64 %[[A]])
; RECOVER: br label

; RECOVER: [[SHORT]]:
; RECOVER: %[[LOWBITS:[^ ]*]] = and i64 %[[A]], 15
; RECOVER: %[[LOWBITS_I8:[^ ]*]] = trunc i64 %[[LOWBITS]] to i8
; RECOVER: %[[LAST:[^ ]*]] = add i8 %[[LOWBITS_I8]], 1
; RECOVER: %[[OOB:[^ ]*]] = icmp uge i8 %[[LAST]], %[[MEMTAG]]
; RECOVER: br i1 %[[OOB]], label %[[FAIL]], label %[[INBOUNDS:[0-9]*]], !prof {{.*}}

; RECOVER: [[INBOUNDS]]:
; RECOVER: %[[EOG_ADDR:[^ ]*]] = or i64 %[[C]], 15
; RECOVER: %[[EOG_PTR:[^ ]*]] = inttoptr i64 %[[EOG_ADDR]] to i8*
; RECOVER: %[[EOGTAG:[^ ]*]] = load i8, i8* %[[EOG_PTR]]
; RECOVER: %[[EOG_MISMATCH:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[EOGTAG]]
; RECOVER: br i1 %[[EOG_MISMATCH]], label %[[FAIL]], label %[[CONT1:[0-9]*]], !prof {{.*}}

; RECOVER: [[CONT1]]:
; RECOVER: br label %[[CONT]]

; RECOVER: [[CONT]]:

; ABORT: %[[A:[^ ]*]] = bitcast i16* %a to i8*
; ABORT-DYNAMIC-SHADOW: call void @llvm.hwasan.check.memaccess(i8* %.hwasan.shadow, i8* %[[A]], i32 1)
; ABORT-ZERO-BASED-SHADOW: call void @llvm.hwasan.check.memaccess(i8* null, i8* %[[A]], i32 1)
Expand Down
5 changes: 3 additions & 2 deletions llvm/test/Instrumentation/HWAddressSanitizer/kernel-alloca.ll
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ define void @test_alloca() sanitize_hwaddress {
; CHECK: %[[B:[^ ]*]] = lshr i64 %[[A]], 20
; CHECK: %[[BASE_TAG:[^ ]*]] = xor i64 %[[A]], %[[B]]

; CHECK: %[[X:[^ ]*]] = alloca i32, align 16
; CHECK: %[[X:[^ ]*]] = alloca { i32, [12 x i8] }, align 16
; CHECK: %[[X_GEP:[^ ]*]] = getelementptr { i32, [12 x i8] }, { i32, [12 x i8] }* %[[X]], i32 0, i32 0
; CHECK: %[[X_TAG:[^ ]*]] = xor i64 %[[BASE_TAG]], 0
; CHECK: %[[X1:[^ ]*]] = ptrtoint i32* %[[X]] to i64
; CHECK: %[[X1:[^ ]*]] = ptrtoint i32* %[[X_GEP]] to i64
; CHECK: %[[C:[^ ]*]] = shl i64 %[[X_TAG]], 56
; CHECK: %[[D:[^ ]*]] = or i64 %[[C]], 72057594037927935
; CHECK: %[[E:[^ ]*]] = and i64 %[[X1]], %[[D]]
Expand Down