我正在检查 TMemoryStream 类,发现以下例程:

procedure TMemoryStream.LoadFromStream(Stream: TStream);
var
  Count: Longint;
begin
  Stream.Position := 0;
  Count := Stream.Size; // <-- assigning Int64 to Longint
  SetSize(Count);
  if Count <> 0 then Stream.ReadBuffer(FMemory^, Count);
end;

在将Int64分配给Longint的过程中,我已经看到了很多这种模式。

我的理解是,在32位和64位Windows中, Longint 是4个字节, Int64 是8个字节,因此,如果我的文件大小是$1 FFFF FFFF == 8589934591 == 8 GB,则此例程将完全无法读取,因为最终计数将是$ FFFF FFFF == -1

我不知道这是如何允许的,并且可能没有考虑在内(也许没有多少人试图读取8 GB以上的文件)。

最佳答案

我为此记录了一张票,并且它显然已在东京10.2中修复。这是64位编译的问题。

https://quality.embarcadero.com/browse/RSP-19094
TCustomMemoryStreamTMemoryStream中的大文件(> 2GB)都存在问题。在TMemoryStream中,问题很简单,因为需要将局部变量声明为NativeInt而不是LongInt,并且Capacity需要更改为NativeInt。在TCustomMemoryStream中,它们更加微妙,因为这两种TCustomMemoryStream.Read方法都将Int64-Int64计算的结果直接分配给LongInt。即使此计算的结果不大于LongInt,它也会溢出。

如果要在西雅图解决此问题,则需要执行代码 Hook ,替换System.Classes单元或推出自己的TMemoryStream替换类。请记住,对于最后一个选项,您还需要替换TBytesStreamTStringStream,因为它们来自TMemoryStream

最后一个选项的另一个问题是第三方组件将没有您的“修复程序”。对于我们来说,我们只有几个地方需要处理大于2GB的文件,因此我们将它们切换了。

TCustomMemoryStream.Read的修复程序(必须同时针对这两种方法)如下所示:

function TCustomMemoryStream.Read(var Buffer; Count: Longint): Longint;
{ These 2 lines are new }
var
  remaining: Int64;
begin
  if (FPosition >= 0) and (Count >= 0) then
  begin
    remaining{Result} := FSize - FPosition;
    if remaining{Result} > 0 then
    begin
      if remaining{Result} > Count then
        Result := Count
      else
        Result := remaining;
      Move((PByte(FMemory) + FPosition)^, Buffer, Result);
      Inc(FPosition, Result);
      Exit;
    end;
  end;
  Result := 0;
end;

关于delphi - 在Stream.read中将Longint计数与Int64大小一起使用是否危险?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48039254/

10-09 16:57