forked from OSchip/llvm-project
[compiler-rt] recommit of r218481: ASan debugging API for report info extraction and locating addresses
Reviewed at http://reviews.llvm.org/D4527 Fixed a test case failure on 32-bit Linux, I did right shift on intptr_t, instead it should have been uintptr_t. llvm-svn: 218538
This commit is contained in:
parent
80bef312b5
commit
7e38e429b7
|
@ -62,6 +62,32 @@ extern "C" {
|
|||
// Print the description of addr (useful when debugging in gdb).
|
||||
void __asan_describe_address(void *addr);
|
||||
|
||||
// Useful for calling from a debugger to get information about an ASan error.
|
||||
// Returns 1 if an error has been (or is being) reported, otherwise returns 0.
|
||||
int __asan_report_present();
|
||||
|
||||
// Useful for calling from a debugger to get information about an ASan error.
|
||||
// If an error has been (or is being) reported, the following functions return
|
||||
// the pc, bp, sp, address, access type (0 = read, 1 = write), access size and
|
||||
// bug description (e.g. "heap-use-after-free"). Otherwise they return 0.
|
||||
void *__asan_get_report_pc();
|
||||
void *__asan_get_report_bp();
|
||||
void *__asan_get_report_sp();
|
||||
void *__asan_get_report_address();
|
||||
int __asan_get_report_access_type();
|
||||
size_t __asan_get_report_access_size();
|
||||
const char *__asan_get_report_description();
|
||||
|
||||
// Useful for calling from the debugger to get information about a pointer.
|
||||
// Returns the category of the given pointer as a constant string.
|
||||
// Possible return values are "global", "stack", "stack-fake", "heap",
|
||||
// "heap-invalid", "shadow-low", "shadow-gap", "shadow-high", "unknown".
|
||||
// If global or stack, tries to also return the variable name, address and
|
||||
// size. If heap, tries to return the chunk address and size. 'name' should
|
||||
// point to an allocated buffer of size 'name_size'.
|
||||
const char *__asan_locate_address(void *addr, char *name, size_t name_size,
|
||||
void **region_address, size_t *region_size);
|
||||
|
||||
// Useful for calling from the debugger to get the allocation stack trace
|
||||
// and thread ID for a heap address. Stores up to 'size' frames into 'trace',
|
||||
// returns the number of stored frames or 0 on error.
|
||||
|
|
|
@ -17,10 +17,70 @@
|
|||
#include "asan_flags.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_report.h"
|
||||
#include "asan_thread.h"
|
||||
|
||||
namespace __asan {
|
||||
|
||||
void GetInfoForStackVar(uptr addr, AddressDescription *descr, AsanThread *t) {
|
||||
descr->name[0] = 0;
|
||||
descr->region_address = 0;
|
||||
descr->region_size = 0;
|
||||
descr->region_kind = "stack";
|
||||
|
||||
uptr offset = 0;
|
||||
uptr frame_pc = 0;
|
||||
const char *frame_descr = t->GetFrameNameByAddr(addr, &offset, &frame_pc);
|
||||
InternalMmapVector<StackVarDescr> vars(16);
|
||||
if (!ParseFrameDescription(frame_descr, &vars)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uptr i = 0; i < vars.size(); i++) {
|
||||
if (offset <= vars[i].beg + vars[i].size) {
|
||||
internal_strncat(descr->name, vars[i].name_pos,
|
||||
Min(descr->name_size, vars[i].name_len));
|
||||
descr->region_address = addr - (offset - vars[i].beg);
|
||||
descr->region_size = vars[i].size;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetInfoForHeapAddress(uptr addr, AddressDescription *descr) {
|
||||
AsanChunkView chunk = FindHeapChunkByAddress(addr);
|
||||
|
||||
descr->name[0] = 0;
|
||||
descr->region_address = 0;
|
||||
descr->region_size = 0;
|
||||
|
||||
if (!chunk.IsValid()) {
|
||||
descr->region_kind = "heap-invalid";
|
||||
return;
|
||||
}
|
||||
|
||||
descr->region_address = chunk.Beg();
|
||||
descr->region_size = chunk.UsedSize();
|
||||
descr->region_kind = "heap";
|
||||
}
|
||||
|
||||
void AsanLocateAddress(uptr addr, AddressDescription *descr) {
|
||||
if (DescribeAddressIfShadow(addr, descr, /* print */ false)) {
|
||||
return;
|
||||
}
|
||||
if (GetInfoForAddressIfGlobal(addr, descr)) {
|
||||
return;
|
||||
}
|
||||
asanThreadRegistry().Lock();
|
||||
AsanThread *thread = FindThreadByStackAddress(addr);
|
||||
asanThreadRegistry().Unlock();
|
||||
if (thread) {
|
||||
GetInfoForStackVar(addr, descr, thread);
|
||||
return;
|
||||
}
|
||||
GetInfoForHeapAddress(addr, descr);
|
||||
}
|
||||
|
||||
uptr AsanGetStack(uptr addr, uptr *trace, uptr size, u32 *thread_id,
|
||||
bool alloc_stack) {
|
||||
AsanChunkView chunk = FindHeapChunkByAddress(addr);
|
||||
|
@ -55,6 +115,16 @@ uptr AsanGetStack(uptr addr, uptr *trace, uptr size, u32 *thread_id,
|
|||
|
||||
using namespace __asan;
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
const char *__asan_locate_address(uptr addr, char *name, uptr name_size,
|
||||
uptr *region_address, uptr *region_size) {
|
||||
AddressDescription descr = { name, name_size, 0, 0, 0 };
|
||||
AsanLocateAddress(addr, &descr);
|
||||
if (region_address) *region_address = descr.region_address;
|
||||
if (region_size) *region_size = descr.region_size;
|
||||
return descr.region_kind;
|
||||
}
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
|
||||
return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ true);
|
||||
|
|
|
@ -71,6 +71,14 @@ ALWAYS_INLINE void PoisonRedZones(const Global &g) {
|
|||
}
|
||||
}
|
||||
|
||||
const uptr kMinimalDistanceFromAnotherGlobal = 64;
|
||||
|
||||
bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
|
||||
if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
|
||||
if (addr >= g.beg + g.size_with_redzone) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ReportGlobal(const Global &g, const char *prefix) {
|
||||
Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
|
||||
prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
|
||||
|
@ -82,19 +90,45 @@ static void ReportGlobal(const Global &g, const char *prefix) {
|
|||
}
|
||||
}
|
||||
|
||||
bool DescribeAddressIfGlobal(uptr addr, uptr size) {
|
||||
static bool DescribeOrGetInfoIfGlobal(uptr addr, uptr size, bool print,
|
||||
Global *output_global) {
|
||||
if (!flags()->report_globals) return false;
|
||||
BlockingMutexLock lock(&mu_for_globals);
|
||||
bool res = false;
|
||||
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
|
||||
const Global &g = *l->g;
|
||||
if (flags()->report_globals >= 2)
|
||||
ReportGlobal(g, "Search");
|
||||
res |= DescribeAddressRelativeToGlobal(addr, size, g);
|
||||
if (print) {
|
||||
if (flags()->report_globals >= 2)
|
||||
ReportGlobal(g, "Search");
|
||||
res |= DescribeAddressRelativeToGlobal(addr, size, g);
|
||||
} else {
|
||||
if (IsAddressNearGlobal(addr, g)) {
|
||||
CHECK(output_global);
|
||||
*output_global = g;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool DescribeAddressIfGlobal(uptr addr, uptr size) {
|
||||
return DescribeOrGetInfoIfGlobal(addr, size, /* print */ true,
|
||||
/* output_global */ nullptr);
|
||||
}
|
||||
|
||||
bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) {
|
||||
Global g = {};
|
||||
if (DescribeOrGetInfoIfGlobal(addr, /* size */ 1, /* print */ false, &g)) {
|
||||
internal_strncpy(descr->name, g.name, descr->name_size);
|
||||
descr->region_address = g.beg;
|
||||
descr->region_size = g.size;
|
||||
descr->region_kind = "global";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 FindRegistrationSite(const Global *g) {
|
||||
CHECK(global_registration_site_vector);
|
||||
for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) {
|
||||
|
|
|
@ -90,6 +90,28 @@ extern "C" {
|
|||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_describe_address(uptr addr);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __asan_report_present();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __asan_get_report_pc();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __asan_get_report_bp();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __asan_get_report_sp();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __asan_get_report_address();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __asan_get_report_access_type();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __asan_get_report_access_size();
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
const char * __asan_get_report_description();
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
const char * __asan_locate_address(uptr addr, char *name, uptr name_size,
|
||||
uptr *region_address, uptr *region_size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size,
|
||||
u32 *thread_id);
|
||||
|
|
|
@ -31,6 +31,19 @@ static char *error_message_buffer = 0;
|
|||
static uptr error_message_buffer_pos = 0;
|
||||
static uptr error_message_buffer_size = 0;
|
||||
|
||||
struct ReportData {
|
||||
uptr pc;
|
||||
uptr sp;
|
||||
uptr bp;
|
||||
uptr addr;
|
||||
bool is_write;
|
||||
uptr access_size;
|
||||
const char *description;
|
||||
};
|
||||
|
||||
static bool report_happened = false;
|
||||
static ReportData report_data = {};
|
||||
|
||||
void AppendToErrorMessageBuffer(const char *buffer) {
|
||||
if (error_message_buffer) {
|
||||
uptr length = internal_strlen(buffer);
|
||||
|
@ -262,9 +275,7 @@ static void PrintGlobalLocation(InternalScopedString *str,
|
|||
|
||||
bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
|
||||
const __asan_global &g) {
|
||||
static const uptr kMinimalDistanceFromAnotherGlobal = 64;
|
||||
if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
|
||||
if (addr >= g.beg + g.size_with_redzone) return false;
|
||||
if (!IsAddressNearGlobal(addr, g)) return false;
|
||||
InternalScopedString str(4096);
|
||||
Decorator d;
|
||||
str.append("%s", d.Location());
|
||||
|
@ -290,21 +301,20 @@ bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DescribeAddressIfShadow(uptr addr) {
|
||||
bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr, bool print) {
|
||||
if (AddrIsInMem(addr))
|
||||
return false;
|
||||
static const char kAddrInShadowReport[] =
|
||||
"Address %p is located in the %s.\n";
|
||||
if (AddrIsInShadowGap(addr)) {
|
||||
Printf(kAddrInShadowReport, addr, "shadow gap area");
|
||||
return true;
|
||||
}
|
||||
if (AddrIsInHighShadow(addr)) {
|
||||
Printf(kAddrInShadowReport, addr, "high shadow area");
|
||||
return true;
|
||||
}
|
||||
if (AddrIsInLowShadow(addr)) {
|
||||
Printf(kAddrInShadowReport, addr, "low shadow area");
|
||||
const char *area_type = nullptr;
|
||||
if (AddrIsInShadowGap(addr)) area_type = "shadow gap";
|
||||
else if (AddrIsInHighShadow(addr)) area_type = "high shadow";
|
||||
else if (AddrIsInLowShadow(addr)) area_type = "low shadow";
|
||||
if (area_type != nullptr) {
|
||||
if (print) {
|
||||
Printf("Address %p is located in the %s area.\n", addr, area_type);
|
||||
} else {
|
||||
CHECK(descr);
|
||||
descr->region_kind = area_type;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
CHECK(0 && "Address is not in memory and not in shadow?");
|
||||
|
@ -582,7 +592,7 @@ void DescribeThread(AsanThreadContext *context) {
|
|||
// immediately after printing error report.
|
||||
class ScopedInErrorReport {
|
||||
public:
|
||||
ScopedInErrorReport() {
|
||||
explicit ScopedInErrorReport(ReportData *report = nullptr) {
|
||||
static atomic_uint32_t num_calls;
|
||||
static u32 reporting_thread_tid;
|
||||
if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
|
||||
|
@ -602,6 +612,8 @@ class ScopedInErrorReport {
|
|||
// Die() to bypass any additional checks.
|
||||
internal__exit(flags()->exitcode);
|
||||
}
|
||||
if (report) report_data = *report;
|
||||
report_happened = true;
|
||||
ASAN_ON_ERROR();
|
||||
// Make sure the registry and sanitizer report mutexes are locked while
|
||||
// we're printing an error report.
|
||||
|
@ -922,8 +934,6 @@ using namespace __asan; // NOLINT
|
|||
|
||||
void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
|
||||
uptr access_size) {
|
||||
ScopedInErrorReport in_report;
|
||||
|
||||
// Determine the error type.
|
||||
const char *bug_descr = "unknown-crash";
|
||||
if (AddrIsInMem(addr)) {
|
||||
|
@ -971,6 +981,11 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size,
|
||||
bug_descr };
|
||||
ScopedInErrorReport in_report(&report);
|
||||
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report("ERROR: AddressSanitizer: %s on address "
|
||||
|
@ -1012,6 +1027,38 @@ void __asan_describe_address(uptr addr) {
|
|||
asanThreadRegistry().Unlock();
|
||||
}
|
||||
|
||||
int __asan_report_present() {
|
||||
return report_happened ? 1 : 0;
|
||||
}
|
||||
|
||||
uptr __asan_get_report_pc() {
|
||||
return report_data.pc;
|
||||
}
|
||||
|
||||
uptr __asan_get_report_bp() {
|
||||
return report_data.bp;
|
||||
}
|
||||
|
||||
uptr __asan_get_report_sp() {
|
||||
return report_data.sp;
|
||||
}
|
||||
|
||||
uptr __asan_get_report_address() {
|
||||
return report_data.addr;
|
||||
}
|
||||
|
||||
int __asan_get_report_access_type() {
|
||||
return report_data.is_write ? 1 : 0;
|
||||
}
|
||||
|
||||
uptr __asan_get_report_access_size() {
|
||||
return report_data.access_size;
|
||||
}
|
||||
|
||||
const char *__asan_get_report_description() {
|
||||
return report_data.description;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_ptr_sub(void *a, void *b) {
|
||||
|
|
|
@ -25,13 +25,24 @@ struct StackVarDescr {
|
|||
uptr name_len;
|
||||
};
|
||||
|
||||
struct AddressDescription {
|
||||
char *name;
|
||||
uptr name_size;
|
||||
uptr region_address;
|
||||
uptr region_size;
|
||||
const char *region_kind;
|
||||
};
|
||||
|
||||
// The following functions prints address description depending
|
||||
// on the memory type (shadow/heap/stack/global).
|
||||
void DescribeHeapAddress(uptr addr, uptr access_size);
|
||||
bool DescribeAddressIfGlobal(uptr addr, uptr access_size);
|
||||
bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
|
||||
const __asan_global &g);
|
||||
bool DescribeAddressIfShadow(uptr addr);
|
||||
bool IsAddressNearGlobal(uptr addr, const __asan_global &g);
|
||||
bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr);
|
||||
bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr = nullptr,
|
||||
bool print = true);
|
||||
bool ParseFrameDescription(const char *frame_descr,
|
||||
InternalMmapVector<StackVarDescr> *vars);
|
||||
bool DescribeAddressIfStack(uptr addr, uptr access_size);
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
// Checks the ASan memory address type debugging API, makes sure it returns
|
||||
// the correct memory type for heap, stack, global and shadow addresses and
|
||||
// that it correctly finds out which region (and name and size) the address
|
||||
// belongs to.
|
||||
// RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1
|
||||
|
||||
#include <assert.h>
|
||||
#include <sanitizer/asan_interface.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int global_var;
|
||||
|
||||
int main() {
|
||||
int local_var;
|
||||
char *heap_ptr = (char *)malloc(10);
|
||||
|
||||
char name[100];
|
||||
void *region_address;
|
||||
size_t region_size;
|
||||
const char *type;
|
||||
|
||||
type = __asan_locate_address(&global_var, name, 100,
|
||||
®ion_address, ®ion_size);
|
||||
assert(0 == strcmp(name, "global_var"));
|
||||
assert(0 == strcmp(type, "global"));
|
||||
assert(region_address == &global_var);
|
||||
assert(region_size == sizeof(global_var));
|
||||
|
||||
type = __asan_locate_address((char *)(&global_var)+1, name, 100,
|
||||
®ion_address, ®ion_size);
|
||||
assert(0 == strcmp(name, "global_var"));
|
||||
assert(0 == strcmp(type, "global"));
|
||||
assert(region_address == &global_var);
|
||||
assert(region_size == sizeof(global_var));
|
||||
|
||||
type = __asan_locate_address(&local_var, name, 100,
|
||||
®ion_address, ®ion_size);
|
||||
assert(0 == strcmp(name, "local_var"));
|
||||
assert(0 == strcmp(type, "stack"));
|
||||
assert(region_address == &local_var);
|
||||
assert(region_size == sizeof(local_var));
|
||||
|
||||
type = __asan_locate_address((char *)(&local_var)+1, name, 100,
|
||||
®ion_address, ®ion_size);
|
||||
assert(0 == strcmp(name, "local_var"));
|
||||
assert(0 == strcmp(type, "stack"));
|
||||
assert(region_address == &local_var);
|
||||
assert(region_size == sizeof(local_var));
|
||||
|
||||
type = __asan_locate_address(heap_ptr, name, 100,
|
||||
®ion_address, ®ion_size);
|
||||
assert(0 == strcmp(type, "heap"));
|
||||
assert(region_address == heap_ptr);
|
||||
assert(10 == region_size);
|
||||
|
||||
type = __asan_locate_address(heap_ptr+1, name, 100,
|
||||
®ion_address, ®ion_size);
|
||||
assert(0 == strcmp(type, "heap"));
|
||||
assert(region_address == heap_ptr);
|
||||
assert(10 == region_size);
|
||||
|
||||
size_t shadow_scale;
|
||||
size_t shadow_offset;
|
||||
__asan_get_shadow_mapping(&shadow_scale, &shadow_offset);
|
||||
|
||||
uintptr_t shadow_ptr = (((uintptr_t)heap_ptr) >> shadow_scale)
|
||||
+ shadow_offset;
|
||||
type = __asan_locate_address((void *)shadow_ptr, NULL, 0, NULL, NULL);
|
||||
assert((0 == strcmp(type, "high shadow")) || 0 == strcmp(type, "low shadow"));
|
||||
|
||||
uintptr_t shadow_gap = (shadow_ptr >> shadow_scale) + shadow_offset;
|
||||
type = __asan_locate_address((void *)shadow_gap, NULL, 0, NULL, NULL);
|
||||
assert(0 == strcmp(type, "shadow gap"));
|
||||
|
||||
free(heap_ptr);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// Checks that the ASan debugging API for getting report information
|
||||
// returns correct values.
|
||||
// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <sanitizer/asan_interface.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
char *heap_ptr = (char *)malloc(10);
|
||||
free(heap_ptr);
|
||||
int present = __asan_report_present();
|
||||
fprintf(stderr, "%s\n", (present == 0) ? "no report" : "");
|
||||
// CHECK: no report
|
||||
heap_ptr[0] = 'A'; // BOOM
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __asan_on_error() {
|
||||
int present = __asan_report_present();
|
||||
void *pc = __asan_get_report_pc();
|
||||
void *bp = __asan_get_report_bp();
|
||||
void *sp = __asan_get_report_sp();
|
||||
void *addr = __asan_get_report_address();
|
||||
int is_write = __asan_get_report_access_type();
|
||||
size_t access_size = __asan_get_report_access_size();
|
||||
const char *description = __asan_get_report_description();
|
||||
|
||||
fprintf(stderr, "%s\n", (present == 1) ? "report" : "");
|
||||
// CHECK: report
|
||||
fprintf(stderr, "pc: %p\n", pc);
|
||||
// CHECK: pc: 0x[[PC:[0-9a-f]+]]
|
||||
fprintf(stderr, "bp: %p\n", bp);
|
||||
// CHECK: bp: 0x[[BP:[0-9a-f]+]]
|
||||
fprintf(stderr, "sp: %p\n", sp);
|
||||
// CHECK: sp: 0x[[SP:[0-9a-f]+]]
|
||||
fprintf(stderr, "addr: %p\n", addr);
|
||||
// CHECK: addr: 0x[[ADDR:[0-9a-f]+]]
|
||||
fprintf(stderr, "type: %s\n", (is_write ? "write" : "read"));
|
||||
// CHECK: type: write
|
||||
fprintf(stderr, "access_size: %ld\n", access_size);
|
||||
// CHECK: access_size: 1
|
||||
fprintf(stderr, "description: %s\n", description);
|
||||
// CHECK: description: heap-use-after-free
|
||||
}
|
||||
|
||||
// CHECK: AddressSanitizer: heap-use-after-free on address {{0x0*}}[[ADDR]] at pc {{0x0*}}[[PC]] bp {{0x0*}}[[BP]] sp {{0x0*}}[[SP]]
|
||||
// CHECK: WRITE of size 1 at {{0x0*}}[[ADDR]] thread T0
|
Loading…
Reference in New Issue