From e2a0135523a0a04d5ed6c9c6f98e3bb3820a44b8 Mon Sep 17 00:00:00 2001 From: freyers Date: Fri, 15 May 2026 21:34:07 +0000 Subject: [PATCH] fix(memory): detect double-free guard on 64-bit builds The freed-block sentinel was compared against the 32-bit literal 0xdddddddd, but the block is poisoned with memset(.., 0xdd, ..) so the size_t/uintptr_t sentinel is 0xdddddddddddddddd on 64-bit. The comparison never matched, so double-free of guarded allocations went undetected on every 64-bit build. Compare against a size_t-wide pattern. Add a regression test. --- CCPMemory.cpp | 5 +++-- tests/CcpMemory.cpp | 13 +++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CCPMemory.cpp b/CCPMemory.cpp index e5bb53b..2118262 100644 --- a/CCPMemory.cpp +++ b/CCPMemory.cpp @@ -46,6 +46,7 @@ const int CCP_MEMORY_GUARD_SIZE_BACK = 64; const int CCP_MEMORY_GUARD_SIZE = CCP_MEMORY_GUARD_SIZE_FRONT + CCP_MEMORY_GUARD_SIZE_BACK + sizeof( size_t ); const uint8_t CCP_MEMORY_GUARD_VALUE_FRONT = 0xee; const uint8_t CCP_MEMORY_GUARD_VALUE_BACK = 0xef; +const size_t CCP_MEMORY_FREED_PATTERN = static_cast( 0xddddddddddddddddULL ); // Should memory tracking in Telemetry be enabled? Defaults to yes. This can skew results of // timing analysis of functions that do a lot of allocations, such as the yaml parser. @@ -494,7 +495,7 @@ void CCPFreeWithGuard( void* p ) // Get the size size_t orgSize = *(size_t*)p0; - if( orgSize == 0xdddddddd ) + if( orgSize == CCP_MEMORY_FREED_PATTERN ) { CCP_LOGERR( "Freeing a block that was already freed at 0x%p", p ); CCP_DEBUG_BREAK(); @@ -596,7 +597,7 @@ void CCPAlignedFree( void* p ) if( s_guardAllocations ) { - if( (uintptr_t)allocatedPtr == 0xdddddddd ) + if( (uintptr_t)allocatedPtr == CCP_MEMORY_FREED_PATTERN ) { CCP_LOGERR( "Freeing an aligned block that was already freed at 0x%p", p ); CCP_DEBUG_BREAK(); diff --git a/tests/CcpMemory.cpp b/tests/CcpMemory.cpp index 183e43b..b3757f8 100644 --- a/tests/CcpMemory.cpp +++ b/tests/CcpMemory.cpp @@ -262,6 +262,19 @@ TEST( CcpMemory, CanDetectInvalidMemoryAccessBeforeMemoryBlock ) } } +TEST( CcpMemory, CanDetectDoubleFreeWithGuard ) +{ + const size_t size = 123; + void* memory = CCPMallocWithGuard( size ); + EXPECT_NE( nullptr, memory ); + CCPFreeWithGuard( memory ); + + LogHelper logHelper; + SilencedFreeWithGuard( memory ); + + EXPECT_TRUE( logHelper.HasMessage( CCP::LOGTYPE_ERR, "already freed" ) ); +} + TEST( CcpMemory, CanDuplicateString ) { const char* string = "just a string";