mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 03:27:44 +00:00
UserspaceEmulator: Implement very basic leak checking :^)
Upon exit, the emulator will now print a leak report of any malloc allocations that are still live and don't have pointers to their base address anywhere in either another live mallocation, or in one of the non-malloc-block memory regions. Note that the malloc-block memory region check is not fully functional and this will work even better once we get that fixed. This is pretty cool. :^)
This commit is contained in:
parent
7e13244238
commit
f6584bfc36
4 changed files with 78 additions and 2 deletions
|
@ -174,6 +174,10 @@ int Emulator::exec()
|
||||||
if (trace)
|
if (trace)
|
||||||
m_cpu.dump();
|
m_cpu.dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auto* tracer = malloc_tracer())
|
||||||
|
tracer->dump_leak_report();
|
||||||
|
|
||||||
return m_exit_status;
|
return m_exit_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#include "MallocTracer.h"
|
#include "MallocTracer.h"
|
||||||
#include "Emulator.h"
|
#include "Emulator.h"
|
||||||
|
#include "MmapRegion.h"
|
||||||
#include <AK/LogStream.h>
|
#include <AK/LogStream.h>
|
||||||
|
|
||||||
namespace UserspaceEmulator {
|
namespace UserspaceEmulator {
|
||||||
|
@ -81,6 +82,9 @@ MallocTracer::Mallocation* MallocTracer::find_mallocation(FlatPtr address)
|
||||||
|
|
||||||
void MallocTracer::audit_read(FlatPtr address, size_t size)
|
void MallocTracer::audit_read(FlatPtr address, size_t size)
|
||||||
{
|
{
|
||||||
|
if (!m_auditing_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
if (Emulator::the().is_in_malloc_or_free())
|
if (Emulator::the().is_in_malloc_or_free())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -101,6 +105,9 @@ void MallocTracer::audit_read(FlatPtr address, size_t size)
|
||||||
|
|
||||||
void MallocTracer::audit_write(FlatPtr address, size_t size)
|
void MallocTracer::audit_write(FlatPtr address, size_t size)
|
||||||
{
|
{
|
||||||
|
if (!m_auditing_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
if (Emulator::the().is_in_malloc_or_free())
|
if (Emulator::the().is_in_malloc_or_free())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -112,11 +119,69 @@ void MallocTracer::audit_write(FlatPtr address, size_t size)
|
||||||
|
|
||||||
if (mallocation->freed) {
|
if (mallocation->freed) {
|
||||||
dbgprintf("\n");
|
dbgprintf("\n");
|
||||||
dbgprintf("==%d== \033[31;1mUse-after-free\033[0m, invalid %zu-byte write at address %p\n", s_pid, size, address);
|
dbgprintf("==%d== \033[31;1mUse-after-free\033[0m, invalid %zu-byte write at address %p\n", s_pid, size, address);
|
||||||
dbgprintf("==%d== Address is %zu bytes into freed block of size %zu\n", s_pid, offset_into_mallocation, mallocation->size);
|
dbgprintf("==%d== Address is %zu bytes into freed block of size %zu\n", s_pid, offset_into_mallocation, mallocation->size);
|
||||||
Emulator::the().dump_backtrace();
|
Emulator::the().dump_backtrace();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MallocTracer::is_reachable(const Mallocation& mallocation) const
|
||||||
|
{
|
||||||
|
ASSERT(!mallocation.freed);
|
||||||
|
|
||||||
|
// 1. Search in active (non-freed) mallocations for pointers to this mallocation
|
||||||
|
for (auto& other_mallocation : m_mallocations) {
|
||||||
|
if (&mallocation == &other_mallocation)
|
||||||
|
continue;
|
||||||
|
size_t pointers_in_mallocation = other_mallocation.size / sizeof(u32);
|
||||||
|
for (size_t i = 0; i < pointers_in_mallocation; ++i) {
|
||||||
|
auto value = Emulator::the().mmu().read32({ 0x20, other_mallocation.address + i * sizeof(u32) });
|
||||||
|
if (value == mallocation.address) {
|
||||||
|
dbgprintf("mallocation %p is reachable from other mallocation %p\n", mallocation.address, other_mallocation.address);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Search in other memory regions for pointers to this mallocation
|
||||||
|
for (auto& region : Emulator::the().mmu().regions()) {
|
||||||
|
// Skip the stack
|
||||||
|
if (region.is_stack())
|
||||||
|
continue;
|
||||||
|
// Skip malloc blocks
|
||||||
|
if (region.is_mmap() && static_cast<const MmapRegion&>(region).is_malloc_block())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
size_t pointers_in_region = region.size() / sizeof(u32);
|
||||||
|
for (size_t i = 0; i < pointers_in_region; ++i) {
|
||||||
|
auto value = region.read32(i * sizeof(u32));
|
||||||
|
if (value == mallocation.address) {
|
||||||
|
dbgprintf("mallocation %p is reachable from region %p-%p\n", mallocation.address, region.base(), region.end() - 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MallocTracer::dump_leak_report()
|
||||||
|
{
|
||||||
|
TemporaryChange change(m_auditing_enabled, false);
|
||||||
|
|
||||||
|
size_t leaks_found = 0;
|
||||||
|
for (auto& mallocation : m_mallocations) {
|
||||||
|
if (mallocation.freed)
|
||||||
|
continue;
|
||||||
|
if (is_reachable(mallocation))
|
||||||
|
continue;
|
||||||
|
++leaks_found;
|
||||||
|
dbgprintf("\n");
|
||||||
|
dbgprintf("==%d== \033[31;1mLeak\033[0m, %zu-byte allocation at address %p\n", s_pid, mallocation.size, mallocation.address);
|
||||||
|
}
|
||||||
|
|
||||||
|
dbgprintf("==%d== %zu leak(s) found\n", s_pid, leaks_found);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,8 @@ public:
|
||||||
void audit_read(FlatPtr address, size_t);
|
void audit_read(FlatPtr address, size_t);
|
||||||
void audit_write(FlatPtr address, size_t);
|
void audit_write(FlatPtr address, size_t);
|
||||||
|
|
||||||
|
void dump_leak_report();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Mallocation {
|
struct Mallocation {
|
||||||
bool contains(FlatPtr a) const
|
bool contains(FlatPtr a) const
|
||||||
|
@ -57,8 +59,11 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
Mallocation* find_mallocation(FlatPtr);
|
Mallocation* find_mallocation(FlatPtr);
|
||||||
|
bool is_reachable(const Mallocation&) const;
|
||||||
|
|
||||||
Vector<Mallocation> m_mallocations;
|
Vector<Mallocation> m_mallocations;
|
||||||
|
|
||||||
|
bool m_auditing_enabled { true };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,8 @@ public:
|
||||||
|
|
||||||
SharedBufferRegion* shbuf_region(int shbuf_id);
|
SharedBufferRegion* shbuf_region(int shbuf_id);
|
||||||
|
|
||||||
|
NonnullOwnPtrVector<Region>& regions() { return m_regions; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OwnPtr<Region> m_tls_region;
|
OwnPtr<Region> m_tls_region;
|
||||||
NonnullOwnPtrVector<Region> m_regions;
|
NonnullOwnPtrVector<Region> m_regions;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue