2020-07-15 16:46:50 -03:00
/*
* Copyright ( c ) 2020 , Andreas Kling < kling @ serenityos . org >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include "MallocTracer.h"
# include "Emulator.h"
2020-07-16 12:04:27 -03:00
# include "MmapRegion.h"
2020-07-15 16:46:50 -03:00
# include <AK/LogStream.h>
2020-10-15 15:46:52 -03:00
# include <AK/TemporaryChange.h>
2020-07-20 21:29:59 -03:00
# include <string.h>
2020-07-15 16:46:50 -03:00
2020-07-16 12:16:21 -03:00
//#define REACHABLE_DEBUG
2020-07-15 16:46:50 -03:00
namespace UserspaceEmulator {
MallocTracer : : MallocTracer ( )
{
}
void MallocTracer : : target_did_malloc ( Badge < SoftCPU > , FlatPtr address , size_t size )
{
2020-07-20 21:29:59 -03:00
auto * region = Emulator : : the ( ) . mmu ( ) . find_region ( { 0x20 , address } ) ;
ASSERT ( region ) ;
ASSERT ( region - > is_mmap ( ) ) ;
auto & mmap_region = static_cast < MmapRegion & > ( * region ) ;
2020-08-01 04:13:45 -03:00
// Mark the containing mmap region as a malloc block!
mmap_region . set_malloc ( true ) ;
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 ) ) {
ASSERT ( existing_mallocation - > freed ) ;
existing_mallocation - > size = size ;
existing_mallocation - > freed = false ;
2020-07-16 15:55:41 -03:00
existing_mallocation - > malloc_backtrace = Emulator : : the ( ) . raw_backtrace ( ) ;
existing_mallocation - > free_backtrace . clear ( ) ;
2020-07-15 16:46:50 -03:00
return ;
}
2020-07-16 12:14:58 -03:00
m_mallocations . append ( { address , size , false , Emulator : : the ( ) . raw_backtrace ( ) , Vector < FlatPtr > ( ) } ) ;
2020-07-15 16:46:50 -03:00
}
void MallocTracer : : target_did_free ( Badge < SoftCPU > , FlatPtr address )
{
2020-07-15 18:53:58 -03:00
if ( ! address )
return ;
2020-07-15 16:46:50 -03:00
for ( auto & mallocation : m_mallocations ) {
if ( mallocation . address = = address ) {
2020-07-15 18:23:21 -03:00
if ( mallocation . freed ) {
2020-10-24 04:45:51 -03:00
reportln ( " \n =={}== \033 [31;1mDouble free() \033 [0m, {:p} " , getpid ( ) , address ) ;
reportln ( " =={}== Address {} has already been passed to free() " , getpid ( ) , address ) ;
2020-07-15 18:23:21 -03:00
Emulator : : the ( ) . dump_backtrace ( ) ;
2020-07-16 12:14:58 -03:00
} else {
mallocation . freed = true ;
mallocation . free_backtrace = Emulator : : the ( ) . raw_backtrace ( ) ;
2020-07-15 18:23:21 -03:00
}
2020-07-15 16:46:50 -03:00
return ;
}
}
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-07-15 18:23:21 -03:00
Emulator : : the ( ) . dump_backtrace ( ) ;
2020-07-15 16:46:50 -03:00
}
2020-11-08 06:10:41 -03:00
void MallocTracer : : target_did_realloc ( Badge < SoftCPU > , FlatPtr address , size_t size )
{
auto * region = Emulator : : the ( ) . mmu ( ) . find_region ( { 0x20 , address } ) ;
ASSERT ( region ) ;
ASSERT ( region - > is_mmap ( ) ) ;
auto & mmap_region = static_cast < MmapRegion & > ( * region ) ;
ASSERT ( mmap_region . is_malloc_block ( ) ) ;
auto * existing_mallocation = find_mallocation ( address ) ;
ASSERT ( existing_mallocation ) ;
ASSERT ( ! existing_mallocation - > freed ) ;
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?
existing_mallocation - > malloc_backtrace = Emulator : : the ( ) . raw_backtrace ( ) ;
}
2020-07-15 16:46:50 -03:00
MallocTracer : : Mallocation * MallocTracer : : find_mallocation ( FlatPtr address )
{
for ( auto & mallocation : m_mallocations ) {
if ( mallocation . contains ( address ) )
return & mallocation ;
}
return nullptr ;
}
2020-08-01 04:44:19 -03:00
MallocTracer : : Mallocation * MallocTracer : : find_mallocation_before ( FlatPtr address )
{
Mallocation * found_mallocation = nullptr ;
for ( auto & mallocation : m_mallocations ) {
if ( mallocation . address > = address )
continue ;
if ( ! found_mallocation | | ( mallocation . address > found_mallocation - > address ) )
found_mallocation = & mallocation ;
}
return found_mallocation ;
}
2020-11-13 06:58:31 -03:00
MallocTracer : : Mallocation * MallocTracer : : find_mallocation_after ( FlatPtr address )
{
Mallocation * found_mallocation = nullptr ;
for ( auto & mallocation : m_mallocations ) {
if ( mallocation . address < = address )
continue ;
if ( ! found_mallocation | | ( mallocation . address < found_mallocation - > address ) )
found_mallocation = & mallocation ;
}
return found_mallocation ;
}
2020-07-15 16:46:50 -03:00
void MallocTracer : : audit_read ( FlatPtr address , size_t size )
{
2020-07-16 12:04:27 -03:00
if ( ! m_auditing_enabled )
return ;
2020-07-15 16:46:50 -03:00
if ( Emulator : : the ( ) . is_in_malloc_or_free ( ) )
return ;
auto * mallocation = find_mallocation ( 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-08-01 04:44:19 -03:00
Emulator : : the ( ) . 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 ) ;
Emulator : : the ( ) . dump_backtrace ( mallocation_before - > malloc_backtrace ) ;
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 ) ;
Emulator : : the ( ) . 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-07-15 16:46:50 -03:00
Emulator : : the ( ) . 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-07-16 12:23:39 -03:00
Emulator : : the ( ) . dump_backtrace ( mallocation - > malloc_backtrace ) ;
2020-10-24 04:45:51 -03:00
reportln ( " =={}== Later freed at: " , getpid ( ) ) ;
2020-07-16 12:23:39 -03:00
Emulator : : the ( ) . dump_backtrace ( mallocation - > free_backtrace ) ;
2020-07-15 16:46:50 -03:00
return ;
}
}
void MallocTracer : : audit_write ( FlatPtr address , size_t size )
{
2020-07-16 12:04:27 -03:00
if ( ! m_auditing_enabled )
return ;
2020-07-15 16:46:50 -03:00
if ( Emulator : : the ( ) . is_in_malloc_or_free ( ) )
return ;
auto * mallocation = find_mallocation ( 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-08-01 04:44:19 -03:00
Emulator : : the ( ) . 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 ) ;
Emulator : : the ( ) . dump_backtrace ( mallocation_before - > malloc_backtrace ) ;
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 ) ;
Emulator : : the ( ) . 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-07-15 16:46:50 -03:00
Emulator : : the ( ) . 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-07-16 12:23:39 -03:00
Emulator : : the ( ) . dump_backtrace ( mallocation - > malloc_backtrace ) ;
2020-10-24 04:45:51 -03:00
reportln ( " =={}== Later freed at: " , getpid ( ) ) ;
2020-07-16 12:23:39 -03:00
Emulator : : the ( ) . dump_backtrace ( mallocation - > free_backtrace ) ;
2020-07-15 16:46:50 -03:00
return ;
}
}
2020-07-16 12:04:27 -03:00
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 ;
2020-07-16 19:24:23 -03:00
if ( other_mallocation . freed )
continue ;
2020-07-16 12:04:27 -03:00
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 ) } ) ;
2020-07-20 21:29:59 -03:00
if ( value . value ( ) = = mallocation . address & & ! value . is_uninitialized ( ) ) {
2020-07-16 12:16:21 -03:00
# ifdef REACHABLE_DEBUG
2020-10-24 04:45:51 -03:00
reportln ( " mallocation {:p} is reachable from other mallocation {:p} " , mallocation . address , other_mallocation . address ) ;
2020-07-16 12:16:21 -03:00
# endif
2020-07-16 12:04:27 -03:00
return true ;
}
}
}
2020-07-16 14:14:54 -03:00
bool reachable = false ;
2020-07-16 12:04:27 -03:00
// 2. Search in other memory regions for pointers to this mallocation
2020-07-16 14:14:54 -03:00
Emulator : : the ( ) . 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-07-16 12:04:27 -03:00
// Skip malloc blocks
if ( region . is_mmap ( ) & & 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 ) ;
for ( size_t i = 0 ; i < pointers_in_region ; + + i ) {
auto value = region . read32 ( i * sizeof ( u32 ) ) ;
2020-07-20 21:29:59 -03:00
if ( value . value ( ) = = mallocation . address & & ! value . is_uninitialized ( ) ) {
2020-07-16 12:16:21 -03:00
# ifdef REACHABLE_DEBUG
2020-10-24 04:45:51 -03:00
reportln ( " mallocation {:p} is reachable from region {:p}-{:p} " , mallocation . address , region . base ( ) , region . end ( ) - 1 ) ;
2020-07-16 12:16:21 -03:00
# endif
2020-07-16 14:14:54 -03:00
reachable = true ;
return IterationDecision : : Break ;
2020-07-16 12:04:27 -03:00
}
}
2020-07-16 14:14:54 -03:00
return IterationDecision : : Continue ;
} ) ;
return reachable ;
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 ;
for ( auto & mallocation : m_mallocations ) {
if ( mallocation . freed )
continue ;
if ( is_reachable ( mallocation ) )
continue ;
+ + 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-07-16 12:14:58 -03:00
Emulator : : the ( ) . dump_backtrace ( mallocation . malloc_backtrace ) ;
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
}