4

関数を使用してZLib、メモリ内のストリームを圧縮/解凍しています。無効なストリームを解凍しようとすると、メモリ リークが発生します。次のコードでは、メモリ リークが発生します。

uses
  Winapi.Windows, System.Classes, System.ZLib;

function DecompressStream(const AStream: TMemoryStream): Boolean;
var
  ostream: TMemoryStream;
begin
  ostream := TMemoryStream.Create;
  try
    AStream.Position := 0;

    // ISSUE: Memory leak happening here
    try
      ZDecompressStream(AStream, ostream);
    except
      Exit(FALSE);
    end;

    AStream.Clear;
    ostream.Position := 0;
    AStream.CopyFrom(ostream, ostream.Size);
    result := TRUE;
  finally
    ostream.Free;
  end;
end;

var
  s: TMemoryStream;

begin
  ReportMemoryLeaksOnShutdown := TRUE;

  s := TMemoryStream.Create;
  try
    DecompressStream(s);
  finally
    s.Free;
  end;
end.

ここで空を解凍しようとするTMemoryStreamと、実行の最後にメモリリークが発生したことが示されます。Delphi XE2 でのテスト。

現実の世界では、アプリケーションが無効なストリームを解凍してメモリをリークしようとする可能性があるため、このリークの発生を防ぐ方法はありますか。

QC: http://qc.embarcadero.com/wc/qcmain.aspx?d=120329 - XE6 以降で修正済みと主張

4

1 に答える 1

3

これは、Delphi RTL コードのバグです。の実装でZDecompressStream例外が発生し、片付けに失敗します。コードを見てみましょう:

procedure ZDecompressStream(inStream, outStream: TStream);
const
  bufferSize = 32768;
var
  zstream: TZStreamRec;
  zresult: Integer;
  inBuffer: TBytes;
  outBuffer: TBytes;
  inSize: Integer;
  outSize: Integer;
begin
  SetLength(inBuffer, BufferSize);
  SetLength(outBuffer, BufferSize);
  FillChar(zstream, SizeOf(TZStreamRec), 0);

  ZCompressCheck(InflateInit(zstream));   <--- performs heap allocation

  inSize := inStream.Read(inBuffer, bufferSize);

  while inSize > 0 do
  begin
    zstream.next_in := @inBuffer[0];
    zstream.avail_in := inSize;

    repeat
      zstream.next_out := @outBuffer[0];
      zstream.avail_out := bufferSize;

      ZCompressCheck(inflate(zstream, Z_NO_FLUSH));

      // outSize := zstream.next_out - outBuffer;
      outSize := bufferSize - zstream.avail_out;

      outStream.Write(outBuffer, outSize);
    until (zstream.avail_in = 0) and (zstream.avail_out > 0);

    inSize := inStream.Read(inBuffer, bufferSize);
  end;

  repeat
    zstream.next_out := @outBuffer[0];
    zstream.avail_out := bufferSize;

    zresult := ZCompressCheck(inflate(zstream, Z_FINISH));

    // outSize := zstream.next_out - outBuffer;
    outSize := bufferSize - zstream.avail_out;

    outStream.Write(outBuffer, outSize);
  until (zresult = Z_STREAM_END) and (zstream.avail_out > 0);

  ZCompressCheck(inflateEnd(zstream));   <--- tidy up, frees heap allocation
end;

これは XE3 から取得しましたが、すべてのバージョンで本質的に同じであると考えています。問題を強調しました。への呼び出しinflateInitは、ヒープからメモリを割り当てます。への呼び出しとペアにする必要がありますinflateEndZCompressCheckエラーが発生すると例外が発生するため、呼び出しは決してinflateEnd発生しません。したがって、コードがリークします。

inflateInitそのユニットへの、およびそのユニット内の他の呼び出しはinflateEnd、try/finally で正しく保護されています。間違っているのは、この関数での使用のようです。

Zlibユニットを正しく実装されているバージョンに置き換えることをお勧めします。

于 2013-11-08T09:30:38.903 に答える