释放动态数组后阻止页脚损坏



当用户登录服务器,服务器发送与用户名/密码匹配结果相关的响应时,我遇到了这个错误。包含回复数据包的数组导致错误。我不知道该如何修复,这是我第一次看到这样的错误。

代码

主要方法

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方法

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日志

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]

块页脚损坏是FastMM4 FullDebugMode错误。这意味着在某个地方,代码中的某些内容正在写入超过分配内存的末尾。可能最好的候选者是任何Move调用。

这有多大的可重复性?如果每次都发生这种情况,有一种简单的方法可以追踪:

  • 在分配动态数组的行上放置一个断点(SetLength调用)
  • 当您到达该断点时,运行它(这样SetLength调用就会运行),然后点击CTRL-F7(Evaluate/modify)
  • 使用@运算符获取数组中最后一个字节的地址。(例如,如果数组有20个元素长,请计算@Reply[19]。)这将为您提供一个十六进制的内存地址。将该值加1可获得超出数组边界的第一个字节的地址
  • 转到"断点"窗格,然后单击"添加断点"按钮一侧的下拉箭头。添加数据断点
  • 对于地址,请给出您刚刚想到的内存地址。对于"长度",请说1
  • 运行该程序,当该位置的内存被覆盖时,它将中断到调试器,您将能够看到是什么在改变它

相关内容

  • 没有找到相关文章

最新更新