diff --git a/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp b/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp --- a/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp +++ b/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp @@ -47,15 +47,12 @@ // appended to a log file automatically per Printf() call. constexpr size_t kDescriptionBufferLen = 128; char DescriptionBuffer[kDescriptionBufferLen] = ""; + + bool AccessWasInBounds = false; if (E != Error::UNKNOWN && Metadata != nullptr) { uintptr_t Address = __gwp_asan_get_allocation_address(Metadata); size_t Size = __gwp_asan_get_allocation_size(Metadata); - if (E == Error::USE_AFTER_FREE) { - snprintf(DescriptionBuffer, kDescriptionBufferLen, - "(%zu byte%s into a %zu-byte allocation at 0x%zx) ", - AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size, - Address); - } else if (AccessPtr < Address) { + if (AccessPtr < Address) { snprintf(DescriptionBuffer, kDescriptionBufferLen, "(%zu byte%s to the left of a %zu-byte allocation at 0x%zx) ", Address - AccessPtr, (Address - AccessPtr == 1) ? "" : "s", Size, @@ -65,9 +62,15 @@ "(%zu byte%s to the right of a %zu-byte allocation at 0x%zx) ", AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size, Address); - } else { + } else if (E == Error::DOUBLE_FREE) { snprintf(DescriptionBuffer, kDescriptionBufferLen, "(a %zu-byte allocation) ", Size); + } else { + AccessWasInBounds = true; + snprintf(DescriptionBuffer, kDescriptionBufferLen, + "(%zu byte%s into a %zu-byte allocation at 0x%zx) ", + AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size, + Address); } } @@ -81,8 +84,19 @@ else snprintf(ThreadBuffer, kThreadBufferLen, "%" PRIu64, ThreadID); - Printf("%s at 0x%zx %sby thread %s here:\n", gwp_asan::ErrorToString(E), - AccessPtr, DescriptionBuffer, ThreadBuffer); + const char *OutOfBoundsAndUseAfterFreeWarning = ""; + if (E == Error::USE_AFTER_FREE && !AccessWasInBounds) { + OutOfBoundsAndUseAfterFreeWarning = + " (warning: buffer overflow/underflow detected on a free()'d " + "allocation. This either means you have a buffer-overflow and a " + "use-after-free at the same time, or you have a long-lived " + "use-after-free bug where the allocation/deallocation metadata below " + "has already been overwritten and is likely bogus)"; + } + + Printf("%s%s at 0x%zx %sby thread %s here:\n", gwp_asan::ErrorToString(E), + OutOfBoundsAndUseAfterFreeWarning, AccessPtr, DescriptionBuffer, + ThreadBuffer); } void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State, diff --git a/compiler-rt/test/gwp_asan/free_then_overflow.cpp b/compiler-rt/test/gwp_asan/free_then_overflow.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/gwp_asan/free_then_overflow.cpp @@ -0,0 +1,17 @@ +// REQUIRES: gwp_asan +// RUN: %clangxx_gwp_asan %s -o %t +// RUN: %expect_crash %run %t 2>&1 | FileCheck %s + +// CHECK: GWP-ASan detected a memory error +// CHECK: Use After Free +// CHECK-SAME: warning: buffer overflow/underflow detected on a free()'d allocation +// CHECK-SAME: at 0x{{[a-f0-9]+}} (1 byte to the right of a 1-byte allocation + +#include + +int main() { + char *Ptr = reinterpret_cast(malloc(1)); + free(Ptr); + volatile char x = *(Ptr + 1); + return 0; +} diff --git a/compiler-rt/test/gwp_asan/free_then_underflow.cpp b/compiler-rt/test/gwp_asan/free_then_underflow.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/gwp_asan/free_then_underflow.cpp @@ -0,0 +1,17 @@ +// REQUIRES: gwp_asan +// RUN: %clangxx_gwp_asan %s -o %t +// RUN: %expect_crash %run %t 2>&1 | FileCheck %s + +// CHECK: GWP-ASan detected a memory error +// CHECK: Use After Free +// CHECK-SAME: warning: buffer overflow/underflow detected on a free()'d allocation +// CHECK-SAME: at 0x{{[a-f0-9]+}} (1 byte to the left of a 1-byte allocation + +#include + +int main() { + char *Ptr = reinterpret_cast(malloc(1)); + free(Ptr); + volatile char x = *(Ptr - 1); + return 0; +} diff --git a/compiler-rt/test/gwp_asan/heap_buffer_overflow.cpp b/compiler-rt/test/gwp_asan/heap_buffer_overflow.cpp --- a/compiler-rt/test/gwp_asan/heap_buffer_overflow.cpp +++ b/compiler-rt/test/gwp_asan/heap_buffer_overflow.cpp @@ -2,6 +2,9 @@ // RUN: %clangxx_gwp_asan %s -o %t // RUN: %expect_crash %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_gwp_asan %s -o %t -DALSO_FREE +// RUN: %expect_crash %run %t 2>&1 | FileCheck %s + // CHECK: GWP-ASan detected a memory error // CHECK: Buffer Overflow at 0x{{[a-f0-9]+}} ({{[1-9][0-9]*}} bytes to the right // CHECK-SAME: of a {{[1-9][0-9]*}}-byte allocation @@ -11,8 +14,10 @@ #include "page_size.h" int main() { - char *Ptr = - reinterpret_cast(malloc(pageSize())); + char *Ptr = reinterpret_cast(malloc(pageSize())); +#ifdef ALSO_FREE + free(Ptr); +#endif // ALSO_FREE volatile char x = *(Ptr + pageSize()); return 0; } diff --git a/compiler-rt/test/gwp_asan/heap_buffer_underflow.cpp b/compiler-rt/test/gwp_asan/heap_buffer_underflow.cpp --- a/compiler-rt/test/gwp_asan/heap_buffer_underflow.cpp +++ b/compiler-rt/test/gwp_asan/heap_buffer_underflow.cpp @@ -2,6 +2,9 @@ // RUN: %clangxx_gwp_asan %s -o %t // RUN: %expect_crash %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_gwp_asan %s -o %t -DALSO_FREE +// RUN: %expect_crash %run %t 2>&1 | FileCheck %s + // CHECK: GWP-ASan detected a memory error // CHECK: Buffer Underflow at 0x{{[a-f0-9]+}} (1 byte to the left // CHECK-SAME: of a {{[1-9][0-9]*}}-byte allocation @@ -11,8 +14,10 @@ #include "page_size.h" int main() { - char *Ptr = - reinterpret_cast(malloc(pageSize())); + char *Ptr = reinterpret_cast(malloc(pageSize())); +#ifdef ALSO_FREE + free(Ptr); +#endif // ALSO_FREE volatile char x = *(Ptr - 1); return 0; }