Delphi: quickly failing in use-after free scenarios
Posted by jpluimers on 2021/03/03
Two tricks that can help in use-after-free scenarios.
Call ScanMemoryPoolForCorruptions often
One of the scenarios of use after free, is that memory blocks get corrupted.
FastMM4 normall checks this at process end using the CheckBlocksOnShutdown
method (in FastMM4.pas when writing this private at line 11156), but you can also do this process manually using the ScanMemoryPoolForCorruptions
method (also in FastMM4.pas, but public at line L1356).
You can automate this process by setting the FullDebugModeScanMemoryPoolBeforeEveryOperation
flag to True
while in FullDebugMode
as you see in the quoted code blocks below.
Note that calling ScanMemoryPoolForCorruptions
between allocations might reveal wild pointer dereferences between allocations.
- Added a global variable "FullDebugModeScanMemoryPoolBeforeEveryOperation". When this variable is set to true and FullDebugMode is enabled, then the entire memory pool is checked for consistency before every GetMem, FreeMem and ReallocMem operation. An "Out of Memory" error is raised if a corruption is found (and this variable is set to false to prevent recursive errors). This obviously incurs a massive performance hit, so enable it only when hunting for elusive memory corruption bugs. (Thanks to Marcus Mönnig.)
…
{If this variable is set to true and FullDebugMode is enabled, then the entire memory pool is checked for consistency before every memory operation. Note that this incurs a massive performance hit on top of the already significant FullDebugMode overhead, so enable this option only when absolutely necessary.} FullDebugModeScanMemoryPoolBeforeEveryOperation: Boolean = False;
Call any virtual method on an instance reference
A quick way to test use-after free scenarios is to call a virtual method on an instance.
Virtual methods mean that the Virtual Method Table needs to be used as a starting point, so any nil pointer will get dereferenced.
Two simple methods that you can call, which have no side effects, except for referencing memory, and are virtual on [WayBack] TObject
are [WayBack] GetHashCode
and [WayBack] ToString
. Both methods got added in Delphi 2009, and now support 64-bit and 32-bit compilers are below.
If you use use these in addition to FastMM4 clearing memory, and FastMM4 redirecting virtual methods of freed objects, you have a good chance of catching use-after free.
Without FastMM, they are also of good help, especially when the freed memory has since then been overwritten by new usage. FastMM4 is a lot more strict about this, so definitely recommended.
Calling these two methods help you to quickly fail with an EAccessViolation
[WayBack] in use-after-free scenarios.
More on the virtual method table is for instance in [WayBack] Hallvard’s Blog: Method calls compiler implementation.
unit System; interface type TObject = class public function GetHashCode(): Integer; virtual; function ToString(): string; virtual; end; implementation function TObject.GetHashCode(): Integer; begin {$IFDEF CPUX64} Result := Integer(IntPtr(Self)) xor Integer(IntPtr(Self) shr 32); {$ELSE !CPUX64} Result := Integer(IntPtr(Self)); {$ENDIF !CPUX64} end; function TObject.ToString(): string; begin Result := ClassName; end; end.
–jeroen
Leave a comment