[英]Is this FastMM4 Invalid Pointer Exception a bug in FastMM for Delphi 5?
In Delphi 5, with FastMM active, the call to FreeMem
in the following minimum-reproducible code triggers an Invalid Pointer Exception : 在Delphi 5中,当FastMM处于活动状态时,在以下最小可重现代码中调用
FreeMem
会触发无效指针异常 :
program Project1;
{$APPTYPE CONSOLE}
uses
FastMM4,
SysUtils,
Windows;
procedure Main;
var
token: THandle;
returnLength: Cardinal;
p: Pointer;
begin
OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, {out}token);
//Get the size of the buffer required.
//It's normally going to be 38 bytes. We'll use 16KB to eliminate the possibility of buffer overrun
// Windows.GetTokenInformation(token, TokenUser, nil, 0, {var}returnLength);
p := GetMemory(16384); //GetMemory(returnLength);
Windows.GetTokenInformation(token, TokenUser, p, 1024, {var}returnLength);
FreeMem({var}p); //FreeMem is the documented way to free memory allocated with GetMemory.
// FreeMemory(p); //FreeMemory is the C++ compatible version of FreeMem.
end;
begin
Main;
end.
The call to FreeMme
fails with an EInvalidPointerException
: 对
FreeMme
的调用因EInvalidPointerException
FreeMme
失败:
FreeMem({var}p); //error
The error will stop happening if: 如果出现以下错误,则错误将停止:
GetTokenInformation
GetTokenInformation
FreeMemory
(rather than FreeMem
) FreeMemory
(而不是FreeMem
) I've reproduced the error on a fresh install of Delphi 5 on a freshly installed Windows 7 machine. 我在新安装的Windows 7机器上重新安装了Delphi 5的错误。 FastMM4 v4.992.
FastMM4 v4.992。
It's only: 这只是:
If it is a bug in FastMM4, i can workaround it. 如果它是FastMM4中的错误,我可以解决它。 Rather than calling:
而不是打电话:
I can manually allocate the buffer another way: 我可以用另一种方式手动分配缓冲区:
If it's not a bug in FastMM4, i'd like to fix the above code. 如果它不是FastMM4中的错误,我想修复上面的代码。
I was under the impression that FastMM takes over memory management, which is why i was surprised to discover: 我的印象是FastMM接管内存管理,这就是为什么我惊讶地发现:
FreeMem({var}p);
failed FreeMemory(p);
works Internally, FreeMem is implemented as a call to the memory manager. 在内部, FreeMem实现为对内存管理器的调用。 In this case the memory manager (FastMM) returns non-zero, causing the call to
reInvalidPtr
: 在这种情况下,内存管理器(FastMM)返回非零值,导致调用
reInvalidPtr
:
System.pas System.pas
procedure _FreeMem;
asm
TEST EAX,EAX
JE @@1
CALL MemoryManager.FreeMem
OR EAX,EAX
JNE @@2
@@1: RET
@@2: MOV AL,reInvalidPtr
JMP Error
end;
and the implementation of MemoryManager.FreeMem ends up being: 并且MemoryManager.FreeMem的实现最终是:
FastMM4.pas FastMM4.pas
function FastFreeMem(APointer: Pointer);
The implementation of FreeMemory is: FreeMemory的实现是:
System.pas : System.pas :
function FreeMemory(P: Pointer): Integer; cdecl;
begin
if P = nil then
Result := 0
else
Result := SysFreeMem(P);
end;
And SysFreeMem is implemented in: SysFreeMem实现在:
GetMem.inc : GetMem.inc :
function SysFreeMem(p: Pointer): Integer;
// Deallocate memory block.
label
abort;
var
u, n : PUsed;
f : PFree;
prevSize, nextSize, size : Integer;
begin
heapErrorCode := cHeapOk;
if not initialized and not InitAllocator then begin
heapErrorCode := cCantInit;
result := cCantInit;
exit;
end;
try
if IsMultiThread then EnterCriticalSection(heapLock);
u := p;
u := PUsed(PChar(u) - sizeof(TUsed)); { inv: u = address of allocated block being freed }
size := u.sizeFlags;
{ inv: size = SET(block size) + [block flags] }
{ validate that the interpretation of this block as a used block is correct }
if (size and cThisUsedFlag) = 0 then begin
heapErrorCode := cBadUsedBlock;
goto abort;
end;
{ inv: the memory block addressed by 'u' and 'p' is an allocated block }
Dec(AllocMemCount);
Dec(AllocMemSize,size and not cFlags - sizeof(TUsed));
if (size and cPrevFreeFlag) <> 0 then begin
{ previous block is free, coalesce }
prevSize := PFree(PChar(u)-sizeof(TFree)).size;
if (prevSize < sizeof(TFree)) or ((prevSize and cFlags) <> 0) then begin
heapErrorCode := cBadPrevBlock;
goto abort;
end;
f := PFree(PChar(u) - prevSize);
if f^.size <> prevSize then begin
heapErrorCode := cBadPrevBlock;
goto abort;
end;
inc(size, prevSize);
u := PUsed(f);
DeleteFree(f);
end;
size := size and not cFlags;
{ inv: size = block size }
n := PUsed(PChar(u) + size);
{ inv: n = block following the block to free }
if PChar(n) = curAlloc then begin
{ inv: u = last block allocated }
dec(curAlloc, size);
inc(remBytes, size);
if remBytes > cDecommitMin then
FreeCurAlloc;
result := cHeapOk;
exit;
end;
if (n.sizeFlags and cThisUsedFlag) <> 0 then begin
{ inv: n is a used block }
if (n.sizeFlags and not cFlags) < sizeof(TUsed) then begin
heapErrorCode := cBadNextBlock;
goto abort;
end;
n.sizeFlags := n.sizeFlags or cPrevFreeFlag
end else begin
{ inv: block u & n are both free; coalesce }
f := PFree(n);
if (f.next = nil) or (f.prev = nil) or (f.size < sizeof(TFree)) then begin
heapErrorCode := cBadNextBlock;
goto abort;
end;
nextSize := f.size;
inc(size, nextSize);
DeleteFree(f);
{ inv: last block (which was free) is not on free list }
end;
InsertFree(u, size);
abort:
result := heapErrorCode;
finally
if IsMultiThread then LeaveCriticalSection(heapLock);
end;
end;
So it makes that sense that FreeMemory doesn't trigger the error; 因此,感觉FreeMemory不会触发错误; FreeMemory is not taken over by the memory manager.
FreeMemory不会由内存管理器接管。
I guess that is why FreeMemory is not the documented counterpart to GetMemory : 🕗 我想这就是为什么FreeMemory不是记录对口GetMemory: 🕗
FreeMem
is not the documented way to free memory allocated with GetMemory
- that's apparently an error in the old documentation that has since been corrected. FreeMem
不是用GetMemory
分配的释放内存的文档化方法 - 这显然是旧文档中的错误,此后已被纠正。 From the documentation for System.GetMemory ( emphasis added): 从System.GetMemory的文档 ( 重点添加):
GetMemory
allocates a memory block.GetMemory
分配一个内存块。
GetMemory
allocates a block of the given Size on the heap, and returns the address of this memory.GetMemory
分配给定Size的块,并返回该内存的地址。 The bytes of the allocated buffer are not set to zero.分配的缓冲区的字节不设置为零。 To dispose of the buffer, use
FreeMemory
.要处理缓冲区,请使用
FreeMemory
。 If there is not enough memory available to allocate the block, anEOutOfMemory
exception is raised.如果没有足够的可用内存来分配块,则会
EOutOfMemory
异常。
If you allocate the memory with GetMem
, use FreeMem
. 如果分配与内存
GetMem
,使用FreeMem
。 If the allocation is done with GetMemory
, use FreeMemory
. 如果分配与完成
GetMemory
,使用FreeMemory
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.