delphifastmmfiremonkey-fm2

Block footer corruption after freeing dynamic array


I am encountering this error when a user logins into a server and the server sends the response related to username/password match result. The array which has the reply packet is causing the error. I do not know as to how can I get this fixed, its my first time seeing such an error.

Code

Main Method

procedure TAgUDP.ProcessLoginPacket
          ( const APacket : TBytes; const ABinding : TIdSocketHandle );
var
  Username : String;
  Password : String;
  Index    : WORD;
  ID       : TGUID;
  Reply    : TBytes;
  Peer     : TAgUDPSPeer;

begin

  // Extracting Username
  SetLength ( Username, APacket [LI_LNUN] );
  Index := 5 + HEADER_END + ( APacket [LI_LNIP] * 2 );
  Move  ( APacket [Index], Username [1], APacket [LI_LNUN] * 2 );

  // Checking whether the peer is already connected
  if not Assigned ( fClientsList.FindPeer ( Username )) then
  begin

    // Extracting Password
    SetLength ( Password, APacket [LI_LNPW] );
    Index := Index + ( APacket [LI_LNUN] * 2 );
    Move  ( APacket [Index], Password [1], APacket [LI_LNPW] * 2 );

    // Creating Peer Object
    CreateGUID ( ID );
    Peer :=
    TAgUDPSPeer.Create ( ABinding.PeerIP, ABinding.PeerPort, ID, Self );

    // Checking whether the database has a record of the user
    if fOnLogin ( Peer, Username, Password, Reply ) then
    begin

      // Assigning username and adding to connected clients list
      Peer.fName := Username;
      fClientsList.Add ( Peer );

      // Extracting local ip+port pair
      SetLength ( Peer.fLIP,  APacket [LI_LNIP] );
      Move  ( APacket [LI_PORT], Peer.fLPort,   2 );
      Move  ( APacket [LI_IPST], Peer.fLIP [1], APacket [LI_LNIP] * 2 );

      // -----------------------------------------------------------------------
      // Building login reply packet

      Index := StrSize ( Peer.fIP ) + HEADER_END + 3;
      SetLength ( Reply, Index + Length ( Reply ));
      Move ( Reply [0], Reply [Index], Length ( Reply ) - Index );

      Reply [0] := Byte ( AgOp_LoginReply );
      Move ( fID,      Reply [HEADER_SID], 16 );
      Move ( Peer.fID, Reply [HEADER_RID], 16 );

      Reply [LR_LNIP] := Length ( Peer.fIP );
      Move ( Peer.fPort,   Reply [LR_PORT], 2 );
      Move ( Peer.fIP [1], Reply [LR_IPST], Length ( Reply ) - 3 );

      SendBuffer ( ABinding.PeerIP, ABinding.PeerPort, Reply );

    end
    else
    begin

      // Building login reply packet - for login rejection

      Peer.Free;
      SetLength ( Reply, 35 );
      Reply [0] := Byte ( AgOp_LoginReply );
      SendBuffer ( ABinding.PeerIP, ABinding.PeerPort, Reply );;

    end;

  end;
  // Deliberate freeing of the array to make the debugger stop after this, after exception otherwise it stops after end
  SetLength ( Reply, 0 );

end;

fOnLogin Method

function  TNetworkModule.OnLogin
          ( const APeer: TAgUDPSPeer; const AUsername, APassword: String;
            var ABuffer: TBytes ): Boolean;
var
  DS      : TSQLDataset;
  Profile : TUser;
begin

  DS := TSQLDataSet.Create ( nil );
  DS.SQLConnection := DBConnection;
  DS.CommandText   :=
  'SELECT user_id,rating,clan '+
  'FROM users '+
  'WHERE username='''+ AUsername +''' AND password='''+ APassword +'''';
  DS.Open;
  Result  := not DS.Eof;

  if Result then
  begin

    fLog.Log( ['Event'],
              ['A login request has been accepted. Username: '+ AUsername] );

    // Storing user information
    Profile := TUser.Create ( APeer, DS.Fields [0].AsInteger,
                              DS.Fields [1].AsInteger, DS.Fields [2].AsString );

    // Adding user to lobby
    fHall.AddUser ( Profile );

    // Making a packet to have additional information of the user to be transmitted to
    // it in the login reply packet 
    SetLength ( ABuffer, 4 + StrSize ( Profile.Clan ));
    Move ( Profile.Rating, ABuffer [0], 4 );
    Move ( Profile.Clan,   ABuffer [4], Length ( ABuffer ) - 4 );

  end
  else
    fLog.Log( ['Event'],
              ['A login request has been denied. Username: '+ AUsername] );

  DS.Close;
  DS.Free;

end;

FastMM Log

FastMM has detected an error during a FreeMem operation. The block footer has been corrupted. 

The block size is: 68

This block was allocated by thread 0x9C, and the stack trace (return addresses) at the time was:
419E3E [FastMM4.pas][FastMM4][DebugReallocMem$qqrpvi][8935]
40692F [System.pas][System][@ReallocMem$qqrrpvi][4325]
40C165 [System.pas][System][DynArraySetLength$qqrrpvpvipi][31888]
40C296 [System.pas][System][@DynArraySetLength$qqrv][31967]
A4B26B [AgUDPServer.pas][AgUDPServer][TAgUDP.ProcessLoginPacket$qqrx25System.%DynamicArray$tuc%xp30Idsockethandle.TIdSocketHandle][872]
A4BF94 [AgUDPServer.pas][AgUDPServer][TAgUDP.DoUDPRead$qqrp32Idudpserver.TIdUDPListenerThreadx25System.%DynamicArray$tuc%p30Idsockethandle.TIdSocketHandle][1247]
A486A0 [IdUDPServer.pas][IdUDPServer][TIdUDPListenerThread.UDPRead$qqrv][415]
A485E4 [IdUDPServer.pas][IdUDPServer][TIdUDPListenerThread.Run$qqrv][392]
A466D8 [IdThread.pas][IdThread][TIdThread.Execute$qqrv][363]
484E7D [System.Classes.pas][System.Classes][Classes.ThreadProc$qqrp22System.Classes.TThread][14569]
409F9E [System.pas][System][ThreadWrapper$qqspv][21627]

The block was previously used for an object of class: TDBXDynalinkConnection

The block is currently used for an object of class: Unknown

The allocation number is: 182223

The block was previously freed by thread 0x9C, and the stack trace (return addresses) at the time was:
40690E [System.pas][System][@FreeMem$qqrpv][4251]
4082FD [System.pas][System][TObject.FreeInstance$qqrv][14978]
408A8D [System.pas][System][@ClassDestroy$qqrxp14System.TObject][16273]
AF0F57 [Data.DBXCommon.pas][Data.DBXCommon][Dbxcommon.TDBXConnection.$bdtr$qqrv][8476]
4083FB [System.pas][System][TObject.Free$qqrv][15046]
B6E0A3 [Data.SqlExpr.pas][Data.SqlExpr][Sqlexpr.TSQLConnection.DoDisconnect$qqrv][2563]
A77FE5 [Data.DB.pas][Data.DB][Db.TCustomConnection.SetConnected$qqro][3405]
A77F6F [Data.DB.pas][Data.DB][Db.TCustomConnection.Close$qqrv][3386]
B6D010 [Data.SqlExpr.pas][Data.SqlExpr][Sqlexpr.TSQLConnection.$bdtr$qqrv][2217]
4083FB [System.pas][System][TObject.Free$qqrv][15046]
B7181C [Data.SqlExpr.pas][Data.SqlExpr][Sqlexpr.TCustomSQLDataSet.InternalFreeCommand$qqrv][4046]

The current thread ID is 0x9C, and the stack trace (return addresses) leading to this error is:
40690E [System.pas][System][@FreeMem$qqrpv][4251]
40C3EA [System.pas][System][@DynArrayClear$qqrrpvpv][32152]
40C085 [System.pas][System][DynArrayClear$qqrrpvpv][31815]
40C0C1 [System.pas][System][DynArraySetLength$qqrrpvpvipi][31839]
40C296 [System.pas][System][@DynArraySetLength$qqrv][31967]
A4B3FC [AgUDPServer.pas][AgUDPServer][TAgUDP.ProcessLoginPacket$qqrx25System.%DynamicArray$tuc%xp30Idsockethandle.TIdSocketHandle][904]
A4BF94 [AgUDPServer.pas][AgUDPServer][TAgUDP.DoUDPRead$qqrp32Idudpserver.TIdUDPListenerThreadx25System.%DynamicArray$tuc%p30Idsockethandle.TIdSocketHandle][1247]
A486A0 [IdUDPServer.pas][IdUDPServer][TIdUDPListenerThread.UDPRead$qqrv][415]
A485E4 [IdUDPServer.pas][IdUDPServer][TIdUDPListenerThread.Run$qqrv][392]
A466D8 [IdThread.pas][IdThread][TIdThread.Execute$qqrv][363]
484E7D [System.Classes.pas][System.Classes][Classes.ThreadProc$qqrp22System.Classes.TThread][14569]

Solution

  • Block footer corruption is a FastMM4 FullDebugMode error. It means that somewhere, something in your code is writing past the end of allocated memory. Probably the best candidate to look at is any Move calls.

    How reproducible is this? If it happens every time, there's an easy way to track it down: