delphi

ShowMessage() after FreeMem() triggers exception 'privileged instruction'


procedure TForm1.Button1Click(Sender: TObject);
var
  pBuffer: PAnsiChar;
begin
  FreeMem(pBuffer);
  ShowMessage('Test1');
end;

This code is wrong since we are using FreeMem() without using GetMem() first. But I'm just wondering why Delphi hangs so badly if this ever happens in a huge project.

Running from the IDE, we don't get any error on the FreeMem() line, but on the ShowMessage():

Project Project1.exe raised exception class $C0000096 with message 'privileged instruction at 0x02df81f8'.

Running the executable, the whole App hangs with a white Message. No possible to close it. If you leave that window for a while then you get more white messages in 'cascade'.

image

If you replace ShowMessage() with Application.MessageBox() then no error is raised, but you get the error 'Invalid Pointer' when trying to close the App, but curiously just sometimes.

procedure TForm1.Button1Click(Sender: TObject);
var
  pBuffer: PAnsiChar;
begin
  FreeMem(pBuffer);
  Application.MessageBox(PChar('Test'), PChar(Application.Title), MB_ICONINFORMATION OR MB_TASKMODAL);
end;

image

I would like to know if it's possible to use FreeMem() in a safe way. I've tried this proc to replace FreeMem() found here: Free memory and nil in Delphi using a single function But it didn't help, I get the same error when using ShowMessage() after using FreeMemAndNil().

procedure TForm1.FreeMemAndNil(var P);
var
  Tmp: Pointer;
begin
  Tmp := Pointer(P);
  Pointer(P) := nil;
  FreeMem(Tmp);
end;

Solution

  • You are invoking Undefined Behavior by freeing memory that has not been allocated properly.

    A local pointer variable is not automatically zeroed out by the compiler. Your pBuffer variable is uninitialized, so it will contain whatever random value happens to already be in the memory block that it occupies. So, you are passing an invalid pointer to FreeMem() and thus will corrupt random memory.

    Your ShowMessage() call just happens to be an unwitting victim of that corruption. Your MessageBox() call "works" simply because it is using different memory that wasn't corrupted. But none of this is guaranteed behavior.

    You are responsible for initializing your pointers properly. You can safely pass in a nil pointer to FreeMem(), but you cannot safely pass in a non-nil pointer to memory that was not allocated with GetMem() or other compatible function.

    FreeMemAndNil() is simply a bandaid. It nils out a pointer while freeing the memory that the pointer is pointing at, but it does not help you at all if the pointer is invalid to begin with, nor does it nil out any other pointers to the same memory. You are ultimately responsible for managing your pointers correctly.