Access Violation Bug Migrating to 64-bit


I have posted a question on stackoverflow.com and it got answered within a few minutes. Then I realized how stupid this bug was.

I have a Delphi project that was compiled by Delphi 2007, and the binary executable is a 32-bit COM DLL. I later, migrate the entire project into Delphi XE3 with little effects (such as rewriting 32-bit assembly, pay attention to Unicode strings). It compiles and gives me a 64-bit COM DLL without problems.

On Win7, 64-bit 8GB, previous testing did not reveal any problems until I run the DLL on Win8, 64-bit 6GB OS. An Access Violation occur whenever I tried to invoke file-related reading (using a custom File Stream Class). I narrow the bug into this function and surely the problem lies on the CopyMemory.

function TRPMFileReadStream.Read(var Buffer; const Count: Longint): Longint;
begin
  if ((Self.FPosition >= 0) and (Count > 0)) then
  begin
    Result := Self.FSize - Self.FPosition;
    if ((Result > 0) and (Result >= Count)) then
    begin
      if (Result > Count) then
      begin
        Result := Count;
      end;
      CopyMemory(
        Pointer(@Buffer),
        Pointer(LongWord(Self.FMemory) + Self.FPosition),
        Result
      );
      Inc(Self.FPosition, Result);
      Exit;
    end;
  end;
  Result := 0;
end;

I didn’t realize this until someone pointed out the mistake.  When I cast the variable Self.FMemory into type LongWord (32-bit address), the 64-bit pointer actually got truncated, and it more likely that the truncated 32-bit pointer points to an invalid memory address. The correct way is to use NativeUInt to cast to pointers. The NativeUInt is 32-bit  for 32-bit executables (alias for LongInt, Integer) and 64-bit for 64-bit executables (alias for Int64). So it is quite important to do a quick search on all 32-bit type casting such as LongWord, DWORD, Integer, Cardinal, and LongInt, especially if these type castings are for pointer arithmetic.

It is also suggested that to use directly the pointer math arithmetic. The {$POINTERMATH ON} is automatically turned on for PByte. And therefore, the following code let the compilers decide the size of the pointers.

CopyMemory(
  @Buffer,
  PByte(Self.FMemory) + Self.FPosition,
  Result
);

It is also recommended to run the top-down memory allocation tests in order to get rid of such errors.  We can set registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Memory Management\AllocationPreference
to 0x100000 to allow top-down memory allocation scheme.

References:

1. http://stackoverflow.com/questions/544296/as-a-programmer-what-do-i-need-to-worry-about-when-moving-to-64-bit-windows#545097

2. http://stackoverflow.com/questions/15930608/copymemory-causes-access-violation-on-win8

3. http://msdn.microsoft.com/en-us/library/bb613473(VS.85).aspx

–EOF (The Ultimate Computing & Technology Blog) —

GD Star Rating
loading...
534 words
Last Post: Optimized ABS function in Assembly
Next Post: Fast Integer Log10

The Permanent URL is: Access Violation Bug Migrating to 64-bit

Leave a Reply