diff --git a/compiler-rt/lib/hwasan/hwasan_allocator.cpp b/compiler-rt/lib/hwasan/hwasan_allocator.cpp --- a/compiler-rt/lib/hwasan/hwasan_allocator.cpp +++ b/compiler-rt/lib/hwasan/hwasan_allocator.cpp @@ -201,21 +201,35 @@ return PossiblyShortTagMatches(mem_tag, tagged_uptr, 1); } +static bool CheckInvalidFree(StackTrace *stack, void *untagged_ptr, + void *tagged_ptr) { + // This function can return true if halt_on_error is false. + if (!allocator.PointerIsMine(untagged_ptr) || + !PointerAndMemoryTagsMatch(tagged_ptr)) { + ReportInvalidFree(stack, reinterpret_cast(tagged_ptr)); + return true; + } + return false; +} + static void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) { CHECK(tagged_ptr); HWASAN_FREE_HOOK(tagged_ptr); - - if (!PointerAndMemoryTagsMatch(tagged_ptr)) - ReportInvalidFree(stack, reinterpret_cast(tagged_ptr)); - void *untagged_ptr = InTaggableRegion(reinterpret_cast(tagged_ptr)) ? UntagPtr(tagged_ptr) : tagged_ptr; + if (CheckInvalidFree(stack, untagged_ptr, tagged_ptr)) + return; + void *aligned_ptr = reinterpret_cast( RoundDownTo(reinterpret_cast(untagged_ptr), kShadowAlignment)); tag_t pointer_tag = GetTagFromPointer(reinterpret_cast(tagged_ptr)); Metadata *meta = reinterpret_cast(allocator.GetMetaData(aligned_ptr)); + if (!meta) { + ReportInvalidFree(stack, reinterpret_cast(tagged_ptr)); + return; + } uptr orig_size = meta->get_requested_size(); u32 free_context_id = StackDepotPut(*stack); u32 alloc_context_id = meta->alloc_context_id; @@ -278,13 +292,15 @@ static void *HwasanReallocate(StackTrace *stack, void *tagged_ptr_old, uptr new_size, uptr alignment) { - if (!PointerAndMemoryTagsMatch(tagged_ptr_old)) - ReportInvalidFree(stack, reinterpret_cast(tagged_ptr_old)); - + void *untagged_ptr_old = + InTaggableRegion(reinterpret_cast(tagged_ptr_old)) + ? UntagPtr(tagged_ptr_old) + : tagged_ptr_old; + if (CheckInvalidFree(stack, untagged_ptr_old, tagged_ptr_old)) + return nullptr; void *tagged_ptr_new = HwasanAllocate(stack, new_size, alignment, false /*zeroise*/); if (tagged_ptr_old && tagged_ptr_new) { - void *untagged_ptr_old = UntagPtr(tagged_ptr_old); Metadata *meta = reinterpret_cast(allocator.GetMetaData(untagged_ptr_old)); internal_memcpy( @@ -305,6 +321,8 @@ } HwasanChunkView FindHeapChunkByAddress(uptr address) { + if (!allocator.PointerIsMine(reinterpret_cast(address))) + return HwasanChunkView(); void *block = allocator.GetBlockBegin(reinterpret_cast(address)); if (!block) return HwasanChunkView(); diff --git a/compiler-rt/lib/hwasan/hwasan_report.cpp b/compiler-rt/lib/hwasan/hwasan_report.cpp --- a/compiler-rt/lib/hwasan/hwasan_report.cpp +++ b/compiler-rt/lib/hwasan/hwasan_report.cpp @@ -372,6 +372,12 @@ int num_descriptions_printed = 0; uptr untagged_addr = UntagAddr(tagged_addr); + if (MemIsShadow(untagged_addr)) { + Printf("%s%p is HWAsan shadow memory.\n%s", d.Location(), untagged_addr, + d.Default()); + return; + } + // Print some very basic information about the address, if it's a heap. HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr); if (uptr beg = chunk.Beg()) { @@ -559,8 +565,15 @@ uptr untagged_addr = UntagAddr(tagged_addr); tag_t ptr_tag = GetTagFromPointer(tagged_addr); - tag_t *tag_ptr = reinterpret_cast(MemToShadow(untagged_addr)); - tag_t mem_tag = *tag_ptr; + tag_t *tag_ptr = nullptr; + tag_t mem_tag = 0; + if (MemIsApp(untagged_addr)) { + tag_ptr = reinterpret_cast(MemToShadow(untagged_addr)); + if (MemIsShadow(reinterpret_cast(tag_ptr))) + mem_tag = *tag_ptr; + else + tag_ptr = nullptr; + } Decorator d; Printf("%s", d.Error()); uptr pc = GetTopPc(stack); @@ -574,14 +587,16 @@ SanitizerToolName, bug_type, untagged_addr, pc); } Printf("%s", d.Access()); - Printf("tags: %02x/%02x (ptr/mem)\n", ptr_tag, mem_tag); + if (tag_ptr) + Printf("tags: %02x/%02x (ptr/mem)\n", ptr_tag, mem_tag); Printf("%s", d.Default()); stack->Print(); PrintAddressDescription(tagged_addr, 0, nullptr); - PrintTagsAroundAddr(tag_ptr); + if (tag_ptr) + PrintTagsAroundAddr(tag_ptr); ReportErrorSummary(bug_type, stack); } diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_primary64.h b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_primary64.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_allocator_primary64.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_allocator_primary64.h @@ -282,6 +282,8 @@ CHECK(kMetadataSize); uptr class_id = GetSizeClass(p); uptr size = ClassIdToSize(class_id); + if (!size) + return nullptr; uptr chunk_idx = GetChunkIdx(reinterpret_cast(p), size); uptr region_beg = GetRegionBeginBySizeClass(class_id); return reinterpret_cast(GetMetadataEnd(region_beg) - diff --git a/compiler-rt/test/hwasan/TestCases/wild-free-close.c b/compiler-rt/test/hwasan/TestCases/wild-free-close.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/hwasan/TestCases/wild-free-close.c @@ -0,0 +1,21 @@ +// RUN: %clang_hwasan %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include +#include + +#include + +int main() { + __hwasan_enable_allocator_tagging(); + char *p = (char *)malloc(1); + fprintf(stderr, "ALLOC %p\n", __hwasan_tag_pointer(p, 0)); + // CHECK: ALLOC {{[0x]+}}[[ADDR:.*]] + free(p - 8); + // CHECK: ERROR: HWAddressSanitizer: invalid-free on address {{.*}} at pc {{[0x]+}}[[PC:.*]] on thread T{{[0-9]+}} + // CHECK: #0 {{[0x]+}}{{.*}}[[PC]] in free + // CHECK: #1 {{.*}} in main {{.*}}wild-free-close.c:[[@LINE-3]] + // CHECK: is located 8 bytes to the left of 1-byte region [{{[0x]+}}{{.*}}[[ADDR]] + // CHECK-NOT: Segmentation fault + // CHECK-NOT: SIGSEGV + return 0; +} diff --git a/compiler-rt/test/hwasan/TestCases/wild-free-realloc.c b/compiler-rt/test/hwasan/TestCases/wild-free-realloc.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/hwasan/TestCases/wild-free-realloc.c @@ -0,0 +1,14 @@ +// RUN: %clang_hwasan %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include + +int main() { + char *p = (char *)malloc(1); + realloc(p + 0x10000000000, 2); + // CHECK: ERROR: HWAddressSanitizer: invalid-free on address {{.*}} at pc {{[0x]+}}[[PC:.*]] on thread T{{[0-9]+}} + // CHECK: #0 {{[0x]+}}{{.*}}[[PC]] in realloc + // CHECK: #1 {{.*}} in main {{.*}}wild-free-realloc.c:[[@LINE-3]] + // CHECK-NOT: Segmentation fault + // CHECK-NOT: SIGSEGV + return 0; +} diff --git a/compiler-rt/test/hwasan/TestCases/wild-free-shadow.c b/compiler-rt/test/hwasan/TestCases/wild-free-shadow.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/hwasan/TestCases/wild-free-shadow.c @@ -0,0 +1,17 @@ +// RUN: %clang_hwasan %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include + +extern void *__hwasan_shadow_memory_dynamic_address; + +int main() { + char *p = (char *)malloc(1); + free(__hwasan_shadow_memory_dynamic_address); + // CHECK: ERROR: HWAddressSanitizer: invalid-free on address {{[0x]+}}[[PTR:.*]] at pc {{[0x]+}}[[PC:.*]] on thread T{{[0-9]+}} + // CHECK: #0 {{[0x]+}}{{.*}}[[PC]] in free + // CHECK: #1 {{.*}} in main {{.*}}wild-free-shadow.c:[[@LINE-3]] + // CHECK: {{[0x]+}}{{.*}}[[PTR]] is HWAsan shadow memory. + // CHECK-NOT: Segmentation fault + // CHECK-NOT: SIGSEGV + return 0; +} diff --git a/compiler-rt/test/hwasan/TestCases/wild-free.c b/compiler-rt/test/hwasan/TestCases/wild-free.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/hwasan/TestCases/wild-free.c @@ -0,0 +1,14 @@ +// RUN: %clang_hwasan %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include + +int main() { + char *p = (char *)malloc(1); + free(p + 0x10000000000); + // CHECK: ERROR: HWAddressSanitizer: invalid-free on address {{.*}} at pc {{[0x]+}}[[PC:.*]] on thread T{{[0-9]+}} + // CHECK: #0 {{[0x]+}}{{.*}}[[PC]] in free + // CHECK: #1 {{.*}} in main {{.*}}wild-free.c:[[@LINE-3]] + // CHECK-NOT: Segmentation fault + // CHECK-NOT: SIGSEGV + return 0; +}