2020-07-15 16:46:50 -03:00
/*
* Copyright ( c ) 2020 , Andreas Kling < kling @ serenityos . org >
2021-08-09 16:28:56 -03:00
* Copyright ( c ) 2021 , Tobias Christiansen < tobyase @ serenityos . org >
2020-07-15 16:46:50 -03:00
*
2021-04-22 05:24:48 -03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-07-15 16:46:50 -03:00
*/
# include "MallocTracer.h"
# include "Emulator.h"
2020-07-16 12:04:27 -03:00
# include "MmapRegion.h"
2021-01-24 11:28:26 -03:00
# include <AK/Debug.h>
2020-10-15 15:46:52 -03:00
# include <AK/TemporaryChange.h>
2020-11-14 14:27:08 -03:00
# include <mallocdefs.h>
2020-07-20 21:29:59 -03:00
# include <string.h>
2021-03-12 13:29:37 -03:00
# include <unistd.h>
2020-07-15 16:46:50 -03:00
namespace UserspaceEmulator {
2020-11-16 10:39:05 -03:00
MallocTracer : : MallocTracer ( Emulator & emulator )
: m_emulator ( emulator )
2020-07-15 16:46:50 -03:00
{
}
2020-11-16 05:33:30 -03:00
template < typename Callback >
inline void MallocTracer : : for_each_mallocation ( Callback callback ) const
2020-11-14 19:07:07 -03:00
{
2020-11-16 10:39:05 -03:00
m_emulator . mmu ( ) . for_each_region ( [ & ] ( auto & region ) {
2021-01-01 15:37:36 -03:00
if ( is < MmapRegion > ( region ) & & static_cast < const MmapRegion & > ( region ) . is_malloc_block ( ) ) {
2020-11-16 05:33:30 -03:00
auto * malloc_data = static_cast < MmapRegion & > ( region ) . malloc_metadata ( ) ;
for ( auto & mallocation : malloc_data - > mallocations ) {
if ( mallocation . used & & callback ( mallocation ) = = IterationDecision : : Break )
return IterationDecision : : Break ;
}
}
return IterationDecision : : Continue ;
} ) ;
2020-11-14 19:07:07 -03:00
}
2021-05-23 07:52:19 -03:00
void MallocTracer : : update_metadata ( MmapRegion & mmap_region , size_t chunk_size )
{
mmap_region . set_malloc_metadata ( { } ,
adopt_own ( * new MallocRegionMetadata {
. region = mmap_region ,
. address = mmap_region . base ( ) ,
. chunk_size = chunk_size ,
. mallocations = { } ,
} ) ) ;
auto & malloc_data = * mmap_region . malloc_metadata ( ) ;
bool is_chunked_block = malloc_data . chunk_size < = size_classes [ num_size_classes - 1 ] ;
if ( is_chunked_block )
malloc_data . mallocations . resize ( ( ChunkedBlock : : block_size - sizeof ( ChunkedBlock ) ) / malloc_data . chunk_size ) ;
else
malloc_data . mallocations . resize ( 1 ) ;
// Mark the containing mmap region as a malloc block!
mmap_region . set_malloc ( true ) ;
}
2021-03-09 04:56:35 -03:00
void MallocTracer : : target_did_malloc ( Badge < Emulator > , FlatPtr address , size_t size )
2020-07-15 16:46:50 -03:00
{
2020-11-23 15:00:35 -03:00
if ( m_emulator . is_in_loader_code ( ) )
return ;
2020-12-25 11:39:26 -03:00
auto * region = m_emulator . mmu ( ) . find_region ( { 0x23 , address } ) ;
2021-02-23 16:42:32 -03:00
VERIFY ( region ) ;
2021-06-24 16:13:09 -03:00
auto & mmap_region = verify_cast < MmapRegion > ( * region ) ;
2020-08-01 04:13:45 -03:00
2020-07-20 21:29:59 -03:00
auto * shadow_bits = mmap_region . shadow_data ( ) + address - mmap_region . base ( ) ;
memset ( shadow_bits , 0 , size ) ;
2020-07-15 16:46:50 -03:00
if ( auto * existing_mallocation = find_mallocation ( address ) ) {
2021-02-23 16:42:32 -03:00
VERIFY ( existing_mallocation - > freed ) ;
2020-07-15 16:46:50 -03:00
existing_mallocation - > size = size ;
existing_mallocation - > freed = false ;
2020-11-16 10:39:05 -03:00
existing_mallocation - > malloc_backtrace = m_emulator . raw_backtrace ( ) ;
2020-07-16 15:55:41 -03:00
existing_mallocation - > free_backtrace . clear ( ) ;
2020-07-15 16:46:50 -03:00
return ;
}
2020-11-14 14:27:08 -03:00
2021-03-09 07:40:14 -03:00
if ( ! mmap_region . is_malloc_block ( ) ) {
2021-03-09 08:00:22 -03:00
auto chunk_size = mmap_region . read32 ( offsetof ( CommonHeader , m_size ) ) . value ( ) ;
2021-05-23 07:52:19 -03:00
update_metadata ( mmap_region , chunk_size ) ;
2020-11-14 14:27:08 -03:00
}
2021-03-09 09:27:05 -03:00
auto * mallocation = mmap_region . malloc_metadata ( ) - > mallocation_for_address ( address ) ;
VERIFY ( mallocation ) ;
* mallocation = { address , size , true , false , m_emulator . raw_backtrace ( ) , Vector < FlatPtr > ( ) } ;
2020-07-15 16:46:50 -03:00
}
2021-05-23 07:52:19 -03:00
void MallocTracer : : target_did_change_chunk_size ( Badge < Emulator > , FlatPtr block , size_t chunk_size )
{
if ( m_emulator . is_in_loader_code ( ) )
return ;
auto * region = m_emulator . mmu ( ) . find_region ( { 0x23 , block } ) ;
VERIFY ( region ) ;
2021-06-24 16:13:09 -03:00
auto & mmap_region = verify_cast < MmapRegion > ( * region ) ;
2021-05-23 07:52:19 -03:00
update_metadata ( mmap_region , chunk_size ) ;
}
2021-03-09 09:27:05 -03:00
ALWAYS_INLINE Mallocation * MallocRegionMetadata : : mallocation_for_address ( FlatPtr address ) const
2020-11-14 14:36:17 -03:00
{
2021-03-09 09:27:05 -03:00
auto index = chunk_index_for_address ( address ) ;
if ( ! index . has_value ( ) )
return nullptr ;
return & const_cast < Mallocation & > ( this - > mallocations [ index . value ( ) ] ) ;
2020-11-14 14:36:17 -03:00
}
2021-03-09 09:27:05 -03:00
ALWAYS_INLINE Optional < size_t > MallocRegionMetadata : : chunk_index_for_address ( FlatPtr address ) const
2020-11-14 14:36:17 -03:00
{
2021-05-18 03:32:05 -03:00
bool is_chunked_block = chunk_size < = size_classes [ num_size_classes - 1 ] ;
2020-11-16 09:18:28 -03:00
if ( ! is_chunked_block ) {
// This is a BigAllocationBlock
return 0 ;
}
2021-03-09 09:27:05 -03:00
auto offset_into_block = address - this - > address ;
if ( offset_into_block < sizeof ( ChunkedBlock ) )
return 0 ;
auto chunk_offset = offset_into_block - sizeof ( ChunkedBlock ) ;
auto chunk_index = chunk_offset / this - > chunk_size ;
if ( chunk_index > = mallocations . size ( ) )
return { } ;
return chunk_index ;
2020-11-14 14:36:17 -03:00
}
2021-03-09 04:56:35 -03:00
void MallocTracer : : target_did_free ( Badge < Emulator > , FlatPtr address )
2020-07-15 16:46:50 -03:00
{
2020-07-15 18:53:58 -03:00
if ( ! address )
return ;
2020-11-23 15:00:35 -03:00
if ( m_emulator . is_in_loader_code ( ) )
return ;
2020-07-15 18:53:58 -03:00
2020-11-14 14:27:08 -03:00
if ( auto * mallocation = find_mallocation ( address ) ) {
if ( mallocation - > freed ) {
reportln ( " \n =={}== \033 [31;1mDouble free() \033 [0m, {:p} " , getpid ( ) , address ) ;
reportln ( " =={}== Address {} has already been passed to free() " , getpid ( ) , address ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( ) ;
2020-11-14 14:27:08 -03:00
} else {
mallocation - > freed = true ;
2020-11-16 10:39:05 -03:00
mallocation - > free_backtrace = m_emulator . raw_backtrace ( ) ;
2020-07-15 16:46:50 -03:00
}
2020-11-14 14:27:08 -03:00
return ;
2020-07-15 16:46:50 -03:00
}
2020-09-30 10:30:42 -03:00
2020-10-24 04:45:51 -03:00
reportln ( " \n =={}== \033 [31;1mInvalid free() \033 [0m, {:p} " , getpid ( ) , address ) ;
reportln ( " =={}== Address {} has never been returned by malloc() " , getpid ( ) , address ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( ) ;
2020-07-15 16:46:50 -03:00
}
2021-03-09 04:56:35 -03:00
void MallocTracer : : target_did_realloc ( Badge < Emulator > , FlatPtr address , size_t size )
2020-11-08 06:10:41 -03:00
{
2020-11-23 15:00:35 -03:00
if ( m_emulator . is_in_loader_code ( ) )
return ;
2020-12-25 11:39:26 -03:00
auto * region = m_emulator . mmu ( ) . find_region ( { 0x23 , address } ) ;
2021-02-23 16:42:32 -03:00
VERIFY ( region ) ;
2021-06-24 16:13:09 -03:00
auto & mmap_region = verify_cast < MmapRegion > ( * region ) ;
2020-11-08 06:10:41 -03:00
2021-02-23 16:42:32 -03:00
VERIFY ( mmap_region . is_malloc_block ( ) ) ;
2020-11-08 06:10:41 -03:00
auto * existing_mallocation = find_mallocation ( address ) ;
2021-02-23 16:42:32 -03:00
VERIFY ( existing_mallocation ) ;
VERIFY ( ! existing_mallocation - > freed ) ;
2020-11-08 06:10:41 -03:00
size_t old_size = existing_mallocation - > size ;
auto * shadow_bits = mmap_region . shadow_data ( ) + address - mmap_region . base ( ) ;
if ( size > old_size ) {
memset ( shadow_bits + old_size , 1 , size - old_size ) ;
} else {
memset ( shadow_bits + size , 1 , old_size - size ) ;
}
existing_mallocation - > size = size ;
// FIXME: Should we track malloc/realloc backtrace separately perhaps?
2020-11-16 10:39:05 -03:00
existing_mallocation - > malloc_backtrace = m_emulator . raw_backtrace ( ) ;
2020-11-08 06:10:41 -03:00
}
2020-11-16 09:18:28 -03:00
Mallocation * MallocTracer : : find_mallocation ( FlatPtr address )
{
2020-11-16 10:39:05 -03:00
auto * region = m_emulator . mmu ( ) . find_region ( { 0x23 , address } ) ;
2020-11-16 09:18:28 -03:00
if ( ! region )
return nullptr ;
return find_mallocation ( * region , address ) ;
2020-07-15 16:46:50 -03:00
}
2020-11-16 05:33:30 -03:00
Mallocation * MallocTracer : : find_mallocation_before ( FlatPtr address )
2020-08-01 04:44:19 -03:00
{
Mallocation * found_mallocation = nullptr ;
2020-11-14 14:27:08 -03:00
for_each_mallocation ( [ & ] ( auto & mallocation ) {
2020-08-01 04:44:19 -03:00
if ( mallocation . address > = address )
2020-11-14 14:27:08 -03:00
return IterationDecision : : Continue ;
2020-08-01 04:44:19 -03:00
if ( ! found_mallocation | | ( mallocation . address > found_mallocation - > address ) )
2020-11-14 14:27:08 -03:00
found_mallocation = const_cast < Mallocation * > ( & mallocation ) ;
return IterationDecision : : Continue ;
} ) ;
2020-08-01 04:44:19 -03:00
return found_mallocation ;
}
2020-11-16 05:33:30 -03:00
Mallocation * MallocTracer : : find_mallocation_after ( FlatPtr address )
2020-11-13 06:58:31 -03:00
{
Mallocation * found_mallocation = nullptr ;
2020-11-14 14:27:08 -03:00
for_each_mallocation ( [ & ] ( auto & mallocation ) {
2020-11-13 06:58:31 -03:00
if ( mallocation . address < = address )
2020-11-14 14:27:08 -03:00
return IterationDecision : : Continue ;
2020-11-13 06:58:31 -03:00
if ( ! found_mallocation | | ( mallocation . address < found_mallocation - > address ) )
2020-11-14 14:27:08 -03:00
found_mallocation = const_cast < Mallocation * > ( & mallocation ) ;
return IterationDecision : : Continue ;
} ) ;
2020-11-13 06:58:31 -03:00
return found_mallocation ;
}
2022-04-01 14:58:27 -03:00
void MallocTracer : : audit_read ( Region const & region , FlatPtr address , size_t size )
2020-07-15 16:46:50 -03:00
{
2020-07-16 12:04:27 -03:00
if ( ! m_auditing_enabled )
return ;
2021-08-14 11:15:32 -03:00
if ( m_emulator . is_memory_auditing_suppressed ( ) ) {
return ;
}
if ( m_emulator . is_in_libsystem ( ) ) {
2020-11-23 15:00:35 -03:00
return ;
}
if ( m_emulator . is_in_loader_code ( ) ) {
2020-07-15 16:46:50 -03:00
return ;
2020-11-23 15:00:35 -03:00
}
2020-07-15 16:46:50 -03:00
2020-11-16 09:18:28 -03:00
auto * mallocation = find_mallocation ( region , address ) ;
2020-08-01 04:44:19 -03:00
if ( ! mallocation ) {
2020-10-24 04:45:51 -03:00
reportln ( " \n =={}== \033 [31;1mHeap buffer overflow \033 [0m, invalid {}-byte read at address {:p} " , getpid ( ) , size , address ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( ) ;
2020-11-13 06:58:31 -03:00
auto * mallocation_before = find_mallocation_before ( address ) ;
auto * mallocation_after = find_mallocation_after ( address ) ;
size_t distance_to_mallocation_before = mallocation_before ? ( address - mallocation_before - > address - mallocation_before - > size ) : 0 ;
size_t distance_to_mallocation_after = mallocation_after ? ( mallocation_after - > address - address ) : 0 ;
if ( mallocation_before & & ( ! mallocation_after | | distance_to_mallocation_before < distance_to_mallocation_after ) ) {
reportln ( " =={}== Address is {} byte(s) after block of size {}, identity {:p}, allocated at: " , getpid ( ) , distance_to_mallocation_before , mallocation_before - > size , mallocation_before - > address ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( mallocation_before - > malloc_backtrace ) ;
2020-11-13 06:58:31 -03:00
return ;
}
if ( mallocation_after & & ( ! mallocation_before | | distance_to_mallocation_after < distance_to_mallocation_before ) ) {
reportln ( " =={}== Address is {} byte(s) before block of size {}, identity {:p}, allocated at: " , getpid ( ) , distance_to_mallocation_after , mallocation_after - > size , mallocation_after - > address ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( mallocation_after - > malloc_backtrace ) ;
2020-08-01 04:44:19 -03:00
}
2020-07-15 16:46:50 -03:00
return ;
2020-08-01 04:44:19 -03:00
}
2020-07-15 16:46:50 -03:00
size_t offset_into_mallocation = address - mallocation - > address ;
if ( mallocation - > freed ) {
2020-10-24 04:45:51 -03:00
reportln ( " \n =={}== \033 [31;1mUse-after-free \033 [0m, invalid {}-byte read at address {:p} " , getpid ( ) , size , address ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( ) ;
2020-10-24 04:45:51 -03:00
reportln ( " =={}== Address is {} byte(s) into block of size {}, allocated at: " , getpid ( ) , offset_into_mallocation , mallocation - > size ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( mallocation - > malloc_backtrace ) ;
2020-10-24 04:45:51 -03:00
reportln ( " =={}== Later freed at: " , getpid ( ) ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( mallocation - > free_backtrace ) ;
2020-07-15 16:46:50 -03:00
return ;
}
}
2022-04-01 14:58:27 -03:00
void MallocTracer : : audit_write ( Region const & region , FlatPtr address , size_t size )
2020-07-15 16:46:50 -03:00
{
2020-07-16 12:04:27 -03:00
if ( ! m_auditing_enabled )
return ;
2021-08-14 11:15:32 -03:00
if ( m_emulator . is_memory_auditing_suppressed ( ) ) {
2020-07-15 16:46:50 -03:00
return ;
2021-08-14 11:15:32 -03:00
}
2020-07-15 16:46:50 -03:00
2020-11-23 15:00:35 -03:00
if ( m_emulator . is_in_loader_code ( ) ) {
return ;
}
2020-11-16 09:18:28 -03:00
auto * mallocation = find_mallocation ( region , address ) ;
2020-08-01 04:44:19 -03:00
if ( ! mallocation ) {
2020-10-24 04:45:51 -03:00
reportln ( " \n =={}== \033 [31;1mHeap buffer overflow \033 [0m, invalid {}-byte write at address {:p} " , getpid ( ) , size , address ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( ) ;
2020-11-13 06:58:31 -03:00
auto * mallocation_before = find_mallocation_before ( address ) ;
auto * mallocation_after = find_mallocation_after ( address ) ;
size_t distance_to_mallocation_before = mallocation_before ? ( address - mallocation_before - > address - mallocation_before - > size ) : 0 ;
size_t distance_to_mallocation_after = mallocation_after ? ( mallocation_after - > address - address ) : 0 ;
if ( mallocation_before & & ( ! mallocation_after | | distance_to_mallocation_before < distance_to_mallocation_after ) ) {
reportln ( " =={}== Address is {} byte(s) after block of size {}, identity {:p}, allocated at: " , getpid ( ) , distance_to_mallocation_before , mallocation_before - > size , mallocation_before - > address ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( mallocation_before - > malloc_backtrace ) ;
2020-11-13 06:58:31 -03:00
return ;
}
if ( mallocation_after & & ( ! mallocation_before | | distance_to_mallocation_after < distance_to_mallocation_before ) ) {
reportln ( " =={}== Address is {} byte(s) before block of size {}, identity {:p}, allocated at: " , getpid ( ) , distance_to_mallocation_after , mallocation_after - > size , mallocation_after - > address ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( mallocation_after - > malloc_backtrace ) ;
2020-08-01 04:44:19 -03:00
}
2020-07-15 16:46:50 -03:00
return ;
2020-08-01 04:44:19 -03:00
}
2020-07-15 16:46:50 -03:00
size_t offset_into_mallocation = address - mallocation - > address ;
if ( mallocation - > freed ) {
2020-10-24 04:45:51 -03:00
reportln ( " \n =={}== \033 [31;1mUse-after-free \033 [0m, invalid {}-byte write at address {:p} " , getpid ( ) , size , address ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( ) ;
2020-10-24 04:45:51 -03:00
reportln ( " =={}== Address is {} byte(s) into block of size {}, allocated at: " , getpid ( ) , offset_into_mallocation , mallocation - > size ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( mallocation - > malloc_backtrace ) ;
2020-10-24 04:45:51 -03:00
reportln ( " =={}== Later freed at: " , getpid ( ) ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( mallocation - > free_backtrace ) ;
2020-07-15 16:46:50 -03:00
return ;
}
}
2021-04-09 19:29:20 -03:00
void MallocTracer : : populate_memory_graph ( )
2020-07-16 12:04:27 -03:00
{
2021-04-09 19:29:20 -03:00
// Create Node for each live Mallocation
for_each_mallocation ( [ & ] ( auto & mallocation ) {
if ( mallocation . freed )
2020-11-14 14:27:08 -03:00
return IterationDecision : : Continue ;
2021-04-09 19:29:20 -03:00
m_memory_graph . set ( mallocation . address , { } ) ;
return IterationDecision : : Continue ;
} ) ;
// Find pointers from each memory region to another
for_each_mallocation ( [ & ] ( auto & mallocation ) {
if ( mallocation . freed )
2020-11-14 14:27:08 -03:00
return IterationDecision : : Continue ;
2021-04-09 19:29:20 -03:00
size_t pointers_in_mallocation = mallocation . size / sizeof ( u32 ) ;
auto & edges_from_mallocation = m_memory_graph . find ( mallocation . address ) - > value ;
2020-07-16 12:04:27 -03:00
for ( size_t i = 0 ; i < pointers_in_mallocation ; + + i ) {
2021-04-09 19:29:20 -03:00
auto value = m_emulator . mmu ( ) . read32 ( { 0x23 , mallocation . address + i * sizeof ( u32 ) } ) ;
auto other_address = value . value ( ) ;
if ( ! value . is_uninitialized ( ) & & m_memory_graph . contains ( value . value ( ) ) ) {
2021-05-01 16:10:08 -03:00
if constexpr ( REACHABLE_DEBUG )
reportln ( " region/mallocation {:p} is reachable from other mallocation {:p} " , other_address , mallocation . address ) ;
2021-04-09 19:29:20 -03:00
edges_from_mallocation . edges_from_node . append ( other_address ) ;
2020-07-16 12:04:27 -03:00
}
}
2020-11-14 14:27:08 -03:00
return IterationDecision : : Continue ;
} ) ;
2020-07-16 12:04:27 -03:00
2021-04-09 19:29:20 -03:00
// Find mallocations that are pointed to by other regions
Vector < FlatPtr > reachable_mallocations = { } ;
2020-11-16 10:39:05 -03:00
m_emulator . mmu ( ) . for_each_region ( [ & ] ( auto & region ) {
2020-07-16 12:04:27 -03:00
// Skip the stack
if ( region . is_stack ( ) )
2020-07-16 14:14:54 -03:00
return IterationDecision : : Continue ;
2020-07-16 14:27:03 -03:00
if ( region . is_text ( ) )
return IterationDecision : : Continue ;
2020-11-23 15:00:35 -03:00
if ( ! region . is_readable ( ) )
return IterationDecision : : Continue ;
2020-07-16 12:04:27 -03:00
// Skip malloc blocks
2021-01-01 15:37:36 -03:00
if ( is < MmapRegion > ( region ) & & static_cast < const MmapRegion & > ( region ) . is_malloc_block ( ) )
2020-07-16 14:14:54 -03:00
return IterationDecision : : Continue ;
2020-07-16 12:04:27 -03:00
size_t pointers_in_region = region . size ( ) / sizeof ( u32 ) ;
2021-04-09 19:29:20 -03:00
2020-07-16 12:04:27 -03:00
for ( size_t i = 0 ; i < pointers_in_region ; + + i ) {
auto value = region . read32 ( i * sizeof ( u32 ) ) ;
2021-04-09 19:29:20 -03:00
auto other_address = value . value ( ) ;
if ( ! value . is_uninitialized ( ) & & m_memory_graph . contains ( value . value ( ) ) ) {
2021-05-01 16:10:08 -03:00
if constexpr ( REACHABLE_DEBUG )
reportln ( " region/mallocation {:p} is reachable from region {:p}-{:p} " , other_address , region . base ( ) , region . end ( ) - 1 ) ;
2021-04-09 19:29:20 -03:00
m_memory_graph . find ( other_address ) - > value . is_reachable = true ;
reachable_mallocations . append ( other_address ) ;
2020-07-16 12:04:27 -03:00
}
}
2020-07-16 14:14:54 -03:00
return IterationDecision : : Continue ;
} ) ;
2021-04-09 19:29:20 -03:00
// Propagate reachability
// There are probably better ways to do that
Vector < FlatPtr > visited = { } ;
for ( size_t i = 0 ; i < reachable_mallocations . size ( ) ; + + i ) {
auto reachable = reachable_mallocations . at ( i ) ;
if ( visited . contains_slow ( reachable ) )
continue ;
visited . append ( reachable ) ;
auto & mallocation_node = m_memory_graph . find ( reachable ) - > value ;
if ( ! mallocation_node . is_reachable )
mallocation_node . is_reachable = true ;
for ( auto & edge : mallocation_node . edges_from_node ) {
reachable_mallocations . append ( edge ) ;
}
}
}
void MallocTracer : : dump_memory_graph ( )
{
for ( auto & key : m_memory_graph . keys ( ) ) {
auto value = m_memory_graph . find ( key ) - > value ;
dbgln ( " Block {:p} [{}reachable] ({} edges) " , key , ! value . is_reachable ? " not " : " " , value . edges_from_node . size ( ) ) ;
for ( auto & edge : value . edges_from_node ) {
dbgln ( " -> {:p} " , edge ) ;
}
}
2020-07-16 12:04:27 -03:00
}
void MallocTracer : : dump_leak_report ( )
{
TemporaryChange change ( m_auditing_enabled , false ) ;
2020-07-16 14:15:38 -03:00
size_t bytes_leaked = 0 ;
2020-07-16 12:04:27 -03:00
size_t leaks_found = 0 ;
2021-04-09 19:29:20 -03:00
populate_memory_graph ( ) ;
2021-05-01 16:10:08 -03:00
if constexpr ( REACHABLE_DEBUG )
dump_memory_graph ( ) ;
2021-04-09 19:29:20 -03:00
2020-11-14 14:27:08 -03:00
for_each_mallocation ( [ & ] ( auto & mallocation ) {
2020-07-16 12:04:27 -03:00
if ( mallocation . freed )
2020-11-14 14:27:08 -03:00
return IterationDecision : : Continue ;
2021-04-09 19:29:20 -03:00
auto & value = m_memory_graph . find ( mallocation . address ) - > value ;
if ( value . is_reachable )
2020-11-14 14:27:08 -03:00
return IterationDecision : : Continue ;
2020-07-16 12:04:27 -03:00
+ + leaks_found ;
2020-07-16 14:15:38 -03:00
bytes_leaked + = mallocation . size ;
2020-10-24 04:45:51 -03:00
reportln ( " \n =={}== \033 [31;1mLeak \033 [0m, {}-byte allocation at address {:p} " , getpid ( ) , mallocation . size , mallocation . address ) ;
2020-11-16 10:39:05 -03:00
m_emulator . dump_backtrace ( mallocation . malloc_backtrace ) ;
2020-11-14 14:27:08 -03:00
return IterationDecision : : Continue ;
} ) ;
2020-07-16 12:04:27 -03:00
2020-07-16 14:15:38 -03:00
if ( ! leaks_found )
2020-10-24 04:45:51 -03:00
reportln ( " \n =={}== \033 [32;1mNo leaks found! \033 [0m " , getpid ( ) ) ;
2020-07-16 14:15:38 -03:00
else
2020-10-24 04:45:51 -03:00
reportln ( " \n =={}== \033 [31;1m{} leak(s) found: {} byte(s) leaked \033 [0m " , getpid ( ) , leaks_found , bytes_leaked ) ;
2020-07-16 12:04:27 -03:00
}
2020-07-15 16:46:50 -03:00
}