Add a thread-local ring buffer of heap allocations

Summary:
We need this in order to properly report heap-use-after-free,
since we don't have a quarantine.

This is a first part of the code, more like a proof of concept.
But I'd like to commit at as is and proceed with refactoring,
adding a ThreadRegistry, and extending the functionality.

Reviewers: eugenis

Subscribers: kubamracek, delcypher, #sanitizers, llvm-commits

Differential Revision: https://reviews.llvm.org/D51394

llvm-svn: 340971
This commit is contained in:
Kostya Serebryany 2018-08-29 21:07:07 +00:00
parent 4821ebf73f
commit 51e1a83850
6 changed files with 60 additions and 4 deletions

View File

@ -36,7 +36,7 @@ enum {
struct Metadata {
u64 state : 2;
u64 requested_size : 62;
u32 requested_size; // Current use cases of hwasan do not expect sizes > 4G.
u32 alloc_context_id;
u32 free_context_id;
};
@ -155,7 +155,7 @@ static void *HwasanAllocate(StackTrace *stack, uptr size, uptr alignment,
Metadata *meta =
reinterpret_cast<Metadata *>(allocator.GetMetaData(allocated));
meta->state = CHUNK_ALLOCATED;
meta->requested_size = size;
meta->requested_size = static_cast<u32>(size);
meta->alloc_context_id = StackDepotPut(*stack);
if (zeroise) {
internal_memset(allocated, 0, size);
@ -194,7 +194,8 @@ void HwasanDeallocate(StackTrace *stack, void *user_ptr) {
uptr size = meta->requested_size;
meta->state = CHUNK_FREE;
meta->requested_size = 0;
meta->free_context_id = StackDepotPut(*stack);
u32 free_context_id = StackDepotPut(*stack);
meta->free_context_id = free_context_id;
// This memory will not be reused by anyone else, so we are free to keep it
// poisoned.
HwasanThread *t = GetCurrentThread();
@ -209,6 +210,9 @@ void HwasanDeallocate(StackTrace *stack, void *user_ptr) {
if (t) {
AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
allocator.Deallocate(cache, p);
if (auto *ha = t->heap_allocations())
ha->push({reinterpret_cast<uptr>(user_ptr), free_context_id,
static_cast<u32>(size)});
} else {
SpinMutexLock l(&fallback_mutex);
AllocatorCache *cache = &fallback_allocator_cache;
@ -252,6 +256,7 @@ void *HwasanReallocate(StackTrace *stack, void *user_old_p, uptr new_size,
internal_memcpy(new_p, old_p, memcpy_size);
HwasanDeallocate(stack, old_p);
}
// FIXME: update t->heap_allocations or simplify HwasanReallocate.
return new_p;
}

View File

@ -15,6 +15,7 @@
#define HWASAN_ALLOCATOR_H
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_ring_buffer.h"
namespace __hwasan {
@ -49,6 +50,17 @@ class HwasanChunkView {
HwasanChunkView FindHeapChunkByAddress(uptr address);
// Information about one (de)allocation that happened in the past.
// These are recorded in a thread-local ring buffer.
struct HeapAllocationRecord {
uptr tagged_addr;
u32 free_context_id;
u32 requested_size;
};
typedef RingBuffer<HeapAllocationRecord> HeapAllocationsRingBuffer;
} // namespace __hwasan
#endif // HWASAN_ALLOCATOR_H

View File

@ -44,3 +44,7 @@ HWASAN_FLAG(int, malloc_fill_byte, 0xbe,
"Value used to fill the newly allocated memory.")
HWASAN_FLAG(int, free_fill_byte, 0x55,
"Value used to fill deallocated memory.")
HWASAN_FLAG(int, heap_history_size, 1023,
"The number of heap (de)allocations remembered per thread. "
"Affects the quality of heap-related reports, but not the ability "
"to find bugs.")

View File

@ -15,6 +15,7 @@
#include "hwasan.h"
#include "hwasan_allocator.h"
#include "hwasan_mapping.h"
#include "hwasan_thread.h"
#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
@ -61,6 +62,21 @@ struct HeapAddressDescription {
}
};
bool FindHeapAllocation(HeapAllocationsRingBuffer *rb,
uptr tagged_addr,
HeapAllocationRecord *har) {
if (!rb) return false;
for (uptr i = 0, size = rb->size(); i < size; i++) {
auto h = (*rb)[i];
if (h.tagged_addr <= tagged_addr &&
h.tagged_addr + h.requested_size > tagged_addr) {
*har = h;
return true;
}
}
return false;
}
bool GetHeapAddressInformation(uptr addr, uptr access_size,
HeapAddressDescription *description) {
HwasanChunkView chunk = FindHeapChunkByAddress(addr);
@ -182,6 +198,18 @@ void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
PrintAddressDescription(address, access_size);
// Temporary functionality; to be folded into PrintAddressDescription.
// TODOs:
// * implement ThreadRegistry
// * check all threads, not just the current one.
// * remove reduntant fields from the allocator metadata
// * use the allocations found in the ring buffer for the main report.
HeapAllocationRecord har;
HwasanThread *t = GetCurrentThread();
if (t && FindHeapAllocation(t->heap_allocations(), addr, &har))
Printf("Address found in the ring buffer: %p %u %u\n", har.tagged_addr,
har.free_context_id, har.requested_size);
PrintTagsAroundAddr(tag_ptr);
ReportErrorSummary(bug_type, stack);

View File

@ -32,7 +32,8 @@ HwasanThread *HwasanThread::Create(thread_callback_t start_routine,
thread->arg_ = arg;
thread->destructor_iterations_ = GetPthreadDestructorIterations();
thread->random_state_ = flags()->random_tags ? RandomSeed() : 0;
if (auto sz = flags()->heap_history_size)
thread->heap_allocations_ = RingBuffer<HeapAllocationRecord>::New(sz);
return thread;
}
@ -76,6 +77,8 @@ void HwasanThread::Destroy() {
malloc_storage().CommitBack();
ClearShadowForThreadStackAndTLS();
uptr size = RoundUpTo(sizeof(HwasanThread), GetPageSizeCached());
if (heap_allocations_)
heap_allocations_->Delete();
UnmapOrDie(this, size);
DTLS_Destroy();
}

View File

@ -50,6 +50,9 @@ class HwasanThread {
void LeaveInterceptorScope() { in_interceptor_scope_--; }
HwasanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; }
HeapAllocationsRingBuffer *heap_allocations() {
return heap_allocations_;
}
tag_t GenerateRandomTag();
@ -75,6 +78,7 @@ class HwasanThread {
u32 random_buffer_;
HwasanThreadLocalMallocStorage malloc_storage_;
HeapAllocationsRingBuffer *heap_allocations_;
};
HwasanThread *GetCurrentThread();