11
11
#include " gwp_asan/options.h"
12
12
13
13
#include < assert.h>
14
+ #include < inttypes.h>
15
+ #include < stdio.h>
14
16
#include < stdlib.h>
15
17
#include < string.h>
16
18
#include < time.h>
@@ -26,32 +28,59 @@ namespace {
26
28
// referenced by users outside this translation unit, in order to avoid
27
29
// init-order-fiasco.
28
30
GuardedPoolAllocator *SingletonPtr = nullptr ;
31
+
32
+ class ScopedBoolean {
33
+ public:
34
+ ScopedBoolean (bool &B) : Bool(B) { Bool = true ; }
35
+ ~ScopedBoolean () { Bool = false ; }
36
+
37
+ private:
38
+ bool &Bool;
39
+ };
40
+
41
+ void defaultPrintStackTrace (uintptr_t *Trace, options::Printf_t Printf) {
42
+ if (Trace[0 ] == 0 )
43
+ Printf (" <unknown (does your allocator support backtracing?)>\n " );
44
+
45
+ for (size_t i = 0 ; Trace[i] != 0 ; ++i) {
46
+ Printf (" #%zu 0x%zx in <unknown>\n " , i, Trace[i]);
47
+ }
48
+ Printf (" \n " );
49
+ }
29
50
} // anonymous namespace
30
51
31
52
// Gets the singleton implementation of this class. Thread-compatible until
32
53
// init() is called, thread-safe afterwards.
33
54
GuardedPoolAllocator *getSingleton () { return SingletonPtr; }
34
55
35
56
void GuardedPoolAllocator::AllocationMetadata::RecordAllocation (
36
- uintptr_t AllocAddr, size_t AllocSize) {
57
+ uintptr_t AllocAddr, size_t AllocSize, options::Backtrace_t Backtrace ) {
37
58
Addr = AllocAddr;
38
59
Size = AllocSize;
39
60
IsDeallocated = false ;
40
61
41
- // TODO(hctim): Implement stack trace collection.
42
62
// TODO(hctim): Ask the caller to provide the thread ID, so we don't waste
43
63
// other thread's time getting the thread ID under lock.
44
64
AllocationTrace.ThreadID = getThreadID ();
45
65
DeallocationTrace.ThreadID = kInvalidThreadID ;
46
- AllocationTrace.Trace [0 ] = 0 ;
66
+ if (Backtrace)
67
+ Backtrace (AllocationTrace.Trace , kMaximumStackFrames );
68
+ else
69
+ AllocationTrace.Trace [0 ] = 0 ;
47
70
DeallocationTrace.Trace [0 ] = 0 ;
48
71
}
49
72
50
- void GuardedPoolAllocator::AllocationMetadata::RecordDeallocation () {
73
+ void GuardedPoolAllocator::AllocationMetadata::RecordDeallocation (
74
+ options::Backtrace_t Backtrace) {
51
75
IsDeallocated = true ;
52
- // TODO(hctim): Implement stack trace collection. Ensure that the unwinder is
53
- // not called if we have our recursive flag called, otherwise non-reentrant
54
- // unwinders may deadlock.
76
+ // Ensure that the unwinder is not called if the recursive flag is set,
77
+ // otherwise non-reentrant unwinders may deadlock.
78
+ if (Backtrace && !ThreadLocals.RecursiveGuard ) {
79
+ ScopedBoolean B (ThreadLocals.RecursiveGuard );
80
+ Backtrace (DeallocationTrace.Trace , kMaximumStackFrames );
81
+ } else {
82
+ DeallocationTrace.Trace [0 ] = 0 ;
83
+ }
55
84
DeallocationTrace.ThreadID = getThreadID ();
56
85
}
57
86
@@ -93,6 +122,11 @@ void GuardedPoolAllocator::init(const options::Options &Opts) {
93
122
94
123
PerfectlyRightAlign = Opts.PerfectlyRightAlign ;
95
124
Printf = Opts.Printf ;
125
+ Backtrace = Opts.Backtrace ;
126
+ if (Opts.PrintBacktrace )
127
+ PrintBacktrace = Opts.PrintBacktrace ;
128
+ else
129
+ PrintBacktrace = defaultPrintStackTrace;
96
130
97
131
size_t PoolBytesRequired =
98
132
PageSize * (1 + MaxSimultaneousAllocations) +
@@ -126,17 +160,6 @@ void GuardedPoolAllocator::init(const options::Options &Opts) {
126
160
installSignalHandlers ();
127
161
}
128
162
129
- namespace {
130
- class ScopedBoolean {
131
- public:
132
- ScopedBoolean (bool &B) : Bool(B) { Bool = true ; }
133
- ~ScopedBoolean () { Bool = false ; }
134
-
135
- private:
136
- bool &Bool;
137
- };
138
- } // anonymous namespace
139
-
140
163
void *GuardedPoolAllocator::allocate (size_t Size ) {
141
164
// GuardedPagePoolEnd == 0 when GWP-ASan is disabled. If we are disabled, fall
142
165
// back to the supporting allocator.
@@ -169,7 +192,7 @@ void *GuardedPoolAllocator::allocate(size_t Size) {
169
192
// unmapped.
170
193
markReadWrite (reinterpret_cast <void *>(getPageAddr (Ptr )), Size );
171
194
172
- Meta->RecordAllocation (Ptr , Size );
195
+ Meta->RecordAllocation (Ptr , Size , Backtrace );
173
196
174
197
return reinterpret_cast <void *>(Ptr );
175
198
}
@@ -196,7 +219,7 @@ void GuardedPoolAllocator::deallocate(void *Ptr) {
196
219
// Ensure that the deallocation is recorded before marking the page as
197
220
// inaccessible. Otherwise, a racy use-after-free will have inconsistent
198
221
// metadata.
199
- Meta->RecordDeallocation ();
222
+ Meta->RecordDeallocation (Backtrace );
200
223
}
201
224
202
225
markInaccessible (reinterpret_cast <void *>(SlotStart),
@@ -328,85 +351,109 @@ Error GuardedPoolAllocator::diagnoseUnknownError(uintptr_t AccessPtr,
328
351
329
352
// If we have reached here, the error is still unknown. There is no metadata
330
353
// available.
354
+ *Meta = nullptr ;
331
355
return Error::UNKNOWN;
332
356
}
333
357
334
- // Prints the provided error and metadata information. Returns true if there is
335
- // additional context that can be provided, false otherwise (i.e. returns false
336
- // if Error == {UNKNOWN, INVALID_FREE without metadata}).
337
- bool printErrorType (Error E, uintptr_t AccessPtr, AllocationMetadata *Meta,
338
- options::Printf_t Printf) {
358
+ namespace {
359
+ // Prints the provided error and metadata information.
360
+ void printErrorType (Error E, uintptr_t AccessPtr, AllocationMetadata *Meta,
361
+ options::Printf_t Printf, uint64_t ThreadID) {
362
+ // Print using intermediate strings. Platforms like Android don't like when
363
+ // you print multiple times to the same line, as there may be a newline
364
+ // appended to a log file automatically per Printf() call.
365
+ const char *ErrorString;
339
366
switch (E) {
340
367
case Error::UNKNOWN:
341
- Printf (" GWP-ASan couldn't automatically determine the source of the "
342
- " memory error when accessing 0x%zx. It was likely caused by a wild "
343
- " memory access into the GWP-ASan pool.\n " ,
344
- AccessPtr);
345
- return false ;
368
+ ErrorString = " GWP-ASan couldn't automatically determine the source of "
369
+ " the memory error. It was likely caused by a wild memory "
370
+ " access into the GWP-ASan pool. The error occured" ;
371
+ break ;
346
372
case Error::USE_AFTER_FREE:
347
- Printf (" Use after free occurred when accessing memory at: 0x%zx\n " ,
348
- AccessPtr);
373
+ ErrorString = " Use after free" ;
349
374
break ;
350
375
case Error::DOUBLE_FREE:
351
- Printf (" Double free occurred when trying to free memory at: 0x%zx\n " ,
352
- AccessPtr);
376
+ ErrorString = " Double free" ;
353
377
break ;
354
378
case Error::INVALID_FREE:
355
- Printf (
356
- " Invalid (wild) free occurred when trying to free memory at: 0x%zx\n " ,
357
- AccessPtr);
358
- // It's possible for an invalid free to fall onto a slot that has never been
359
- // allocated. If this is the case, there is no valid metadata.
360
- if (Meta == nullptr )
361
- return false ;
379
+ ErrorString = " Invalid (wild) free" ;
362
380
break ;
363
381
case Error::BUFFER_OVERFLOW:
364
- Printf (" Buffer overflow occurred when accessing memory at: 0x%zx\n " ,
365
- AccessPtr);
382
+ ErrorString = " Buffer overflow" ;
366
383
break ;
367
384
case Error::BUFFER_UNDERFLOW:
368
- Printf (" Buffer underflow occurred when accessing memory at: 0x%zx\n " ,
369
- AccessPtr);
385
+ ErrorString = " Buffer underflow" ;
370
386
break ;
371
387
}
372
388
373
- Printf (" 0x%zx is " , AccessPtr);
374
- if (AccessPtr < Meta->Addr )
375
- Printf (" located %zu bytes to the left of a %zu-byte allocation located at "
376
- " 0x%zx\n " ,
377
- Meta->Addr - AccessPtr, Meta->Size , Meta->Addr );
378
- else if (AccessPtr > Meta->Addr )
379
- Printf (" located %zu bytes to the right of a %zu-byte allocation located at "
380
- " 0x%zx\n " ,
381
- AccessPtr - Meta->Addr , Meta->Size , Meta->Addr );
389
+ constexpr size_t kDescriptionBufferLen = 128 ;
390
+ char DescriptionBuffer[kDescriptionBufferLen ];
391
+ if (Meta) {
392
+ if (E == Error::USE_AFTER_FREE) {
393
+ snprintf (DescriptionBuffer, kDescriptionBufferLen ,
394
+ " (%zu byte%s into a %zu-byte allocation at 0x%zx)" ,
395
+ AccessPtr - Meta->Addr , (AccessPtr - Meta->Addr == 1 ) ? " " : " s" ,
396
+ Meta->Size , Meta->Addr );
397
+ } else if (AccessPtr < Meta->Addr ) {
398
+ snprintf (DescriptionBuffer, kDescriptionBufferLen ,
399
+ " (%zu byte%s to the left of a %zu-byte allocation at 0x%zx)" ,
400
+ Meta->Addr - AccessPtr, (Meta->Addr - AccessPtr == 1 ) ? " " : " s" ,
401
+ Meta->Size , Meta->Addr );
402
+ } else if (AccessPtr > Meta->Addr ) {
403
+ snprintf (DescriptionBuffer, kDescriptionBufferLen ,
404
+ " (%zu byte%s to the right of a %zu-byte allocation at 0x%zx)" ,
405
+ AccessPtr - Meta->Addr , (AccessPtr - Meta->Addr == 1 ) ? " " : " s" ,
406
+ Meta->Size , Meta->Addr );
407
+ } else {
408
+ snprintf (DescriptionBuffer, kDescriptionBufferLen ,
409
+ " (a %zu-byte allocation)" , Meta->Size );
410
+ }
411
+ }
412
+
413
+ // Possible number of digits of a 64-bit number: ceil(log10(2^64)) == 20. Add
414
+ // a null terminator, and round to the nearest 8-byte boundary.
415
+ constexpr size_t kThreadBufferLen = 24 ;
416
+ char ThreadBuffer[kThreadBufferLen ];
417
+ if (ThreadID == GuardedPoolAllocator::kInvalidThreadID )
418
+ snprintf (ThreadBuffer, kThreadBufferLen , " <unknown>" );
382
419
else
383
- Printf (" a %zu-byte allocation\n " , Meta->Size );
384
- return true ;
420
+ snprintf (ThreadBuffer, kThreadBufferLen , " %" PRIu64, ThreadID);
421
+
422
+ Printf (" %s at 0x%zx %s by thread %s here:\n " , ErrorString, AccessPtr,
423
+ DescriptionBuffer, ThreadBuffer);
385
424
}
386
425
387
- void printThreadInformation (Error E, uintptr_t AccessPtr,
388
- AllocationMetadata *Meta,
389
- options::Printf_t Printf) {
390
- Printf (" 0x%zx was allocated by thread " , AccessPtr);
391
- if (Meta->AllocationTrace .ThreadID == UINT64_MAX)
392
- Printf (" UNKNOWN.\n " );
393
- else
394
- Printf (" %zu.\n " , Meta->AllocationTrace .ThreadID );
426
+ void printAllocDeallocTraces (uintptr_t AccessPtr, AllocationMetadata *Meta,
427
+ options::Printf_t Printf,
428
+ options::PrintBacktrace_t PrintBacktrace) {
429
+ assert (Meta != nullptr && " Metadata is non-null for printAllocDeallocTraces" );
395
430
396
- if (E == Error::USE_AFTER_FREE || E == Error::DOUBLE_FREE ) {
397
- Printf ( " 0x%zx was freed by thread " , AccessPtr);
398
- if (Meta-> AllocationTrace . ThreadID == UINT64_MAX )
399
- Printf (" UNKNOWN. \n " );
431
+ if (Meta-> IsDeallocated ) {
432
+ if (Meta-> DeallocationTrace . ThreadID ==
433
+ GuardedPoolAllocator:: kInvalidThreadID )
434
+ Printf (" 0x%zx was deallocated by thread <unknown> here: \n " , AccessPtr );
400
435
else
401
- Printf (" %zu.\n " , Meta->AllocationTrace .ThreadID );
436
+ Printf (" 0x%zx was deallocated by thread %zu here:\n " , AccessPtr,
437
+ Meta->DeallocationTrace .ThreadID );
438
+
439
+ PrintBacktrace (Meta->DeallocationTrace .Trace , Printf);
402
440
}
441
+
442
+ if (Meta->AllocationTrace .ThreadID == GuardedPoolAllocator::kInvalidThreadID )
443
+ Printf (" 0x%zx was allocated by thread <unknown> here:\n " , Meta->Addr );
444
+ else
445
+ Printf (" 0x%zx was allocated by thread %zu here:\n " , Meta->Addr ,
446
+ Meta->AllocationTrace .ThreadID );
447
+
448
+ PrintBacktrace (Meta->AllocationTrace .Trace , Printf);
403
449
}
404
450
405
451
struct ScopedEndOfReportDecorator {
406
452
ScopedEndOfReportDecorator (options::Printf_t Printf) : Printf(Printf) {}
407
453
~ScopedEndOfReportDecorator () { Printf (" *** End GWP-ASan report ***\n " ); }
408
454
options::Printf_t Printf;
409
455
};
456
+ } // anonymous namespace
410
457
411
458
void GuardedPoolAllocator::reportErrorInternal (uintptr_t AccessPtr, Error E) {
412
459
if (!pointerIsMine (reinterpret_cast <void *>(AccessPtr))) {
@@ -434,22 +481,21 @@ void GuardedPoolAllocator::reportErrorInternal(uintptr_t AccessPtr, Error E) {
434
481
Meta = nullptr ;
435
482
}
436
483
437
- // Print the error information, and if there is no valid metadata, stop here.
438
- if (!printErrorType (E, AccessPtr, Meta, Printf)) {
439
- return ;
440
- }
484
+ // Print the error information.
485
+ uint64_t ThreadID = getThreadID ();
486
+ printErrorType (E, AccessPtr, Meta, Printf, ThreadID);
487
+ if (Backtrace) {
488
+ static constexpr unsigned kMaximumStackFramesForCrashTrace = 128 ;
489
+ uintptr_t Trace[kMaximumStackFramesForCrashTrace ];
490
+ Backtrace (Trace, kMaximumStackFramesForCrashTrace );
441
491
442
- // Ensure that we have a valid metadata pointer from this point forward.
443
- if (Meta == nullptr ) {
444
- Printf (" GWP-ASan internal unreachable error. Metadata is not null.\n " );
445
- return ;
492
+ PrintBacktrace (Trace, Printf);
493
+ } else {
494
+ Printf (" <unknown (does your allocator support backtracing?)>\n\n " );
446
495
}
447
496
448
- printThreadInformation (E, AccessPtr, Meta, Printf);
449
- // TODO(hctim): Implement stack unwinding here. Ask the caller to provide us
450
- // with the base pointer, and we unwind the stack to give a stack trace for
451
- // the access.
452
- // TODO(hctim): Implement dumping here of allocation/deallocation traces.
497
+ if (Meta)
498
+ printAllocDeallocTraces (AccessPtr, Meta, Printf, PrintBacktrace);
453
499
}
454
500
455
501
TLS_INITIAL_EXEC
0 commit comments