Skip to content

Commit bdf8636

Browse files
rzikmCopilot
andauthored
Fix race between zlib Inflate/Deflate and Dispose (#128752)
Fixes #128550. `Inflater`/`Deflater` synchronize their native zlib calls via a managed `lock`, but `Dispose` is not synchronized, so a concurrent `Dispose` on one thread could run `InflateEnd`/`DeflateEnd` and free the underlying `z_stream` while another thread was mid-call inside the native `inflate`/`deflate`, leading to use-after-free crashes. The fix moves the protection down into `ZLibStreamHandle`. `Inflate`, `InflateReset2_`, and `Deflate` now bracket their native invocations with `DangerousAddRef`/`DangerousRelease`, which is exactly what `SafeHandle` is designed for: it defers `ReleaseHandle` (and therefore the `InflateEnd`/`DeflateEnd` P/Invoke) until all in-flight callers have released their refs. The redundant `EnsureNotDisposed` check is removed from these methods since `DangerousAddRef` already throws `ObjectDisposedException` for a closed handle. The other `ZLibStreamHandle` members (`DeflateEnd`, `InflateEnd`, `InflateInit2_`, `DeflateInit2_`) are intentionally left as-is: they only run from initialization or from `ReleaseHandle`/`Dispose` paths that are not the racing party. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent af7e729 commit bdf8636

1 file changed

Lines changed: 47 additions & 21 deletions

File tree

src/libraries/Common/src/System/IO/Compression/ZLibNative.cs

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -288,11 +288,6 @@ public uint AvailOut
288288
set { _zStream.availOut = value; }
289289
}
290290

291-
private void EnsureNotDisposed()
292-
{
293-
ObjectDisposedException.ThrowIf(InitializationState == State.Disposed, this);
294-
}
295-
296291
private void EnsureState(State requiredState)
297292
{
298293
if (InitializationState != requiredState)
@@ -348,18 +343,28 @@ private unsafe void DeflateInit2_(CompressionLevel level, int windowBits, int me
348343

349344
public unsafe ErrorCode Deflate(FlushCode flush)
350345
{
351-
EnsureNotDisposed();
352-
EnsureState(State.InitializedForDeflate);
346+
bool refAdded = false;
347+
try
348+
{
349+
DangerousAddRef(ref refAdded);
350+
EnsureState(State.InitializedForDeflate);
353351

354-
fixed (ZStream* stream = &_zStream)
352+
fixed (ZStream* stream = &_zStream)
353+
{
354+
return Interop.ZLib.Deflate(stream, flush);
355+
}
356+
}
357+
finally
355358
{
356-
return Interop.ZLib.Deflate(stream, flush);
359+
if (refAdded)
360+
{
361+
DangerousRelease();
362+
}
357363
}
358364
}
359365

360-
public unsafe ErrorCode DeflateEnd()
366+
private unsafe ErrorCode DeflateEnd()
361367
{
362-
EnsureNotDisposed();
363368
EnsureState(State.InitializedForDeflate);
364369

365370
fixed (ZStream* stream = &_zStream)
@@ -395,29 +400,50 @@ private unsafe void InflateInit2_(int windowBits)
395400

396401
public unsafe ErrorCode InflateReset2_(int windowBits)
397402
{
398-
EnsureNotDisposed();
399-
EnsureState(State.InitializedForInflate);
403+
bool refAdded = false;
404+
try
405+
{
406+
DangerousAddRef(ref refAdded);
407+
EnsureState(State.InitializedForInflate);
400408

401-
fixed (ZStream* stream = &_zStream)
409+
fixed (ZStream* stream = &_zStream)
410+
{
411+
return Interop.ZLib.InflateReset2_(stream, windowBits);
412+
}
413+
}
414+
finally
402415
{
403-
return Interop.ZLib.InflateReset2_(stream, windowBits);
416+
if (refAdded)
417+
{
418+
DangerousRelease();
419+
}
404420
}
405421
}
406422

407423
public unsafe ErrorCode Inflate(FlushCode flush)
408424
{
409-
EnsureNotDisposed();
410-
EnsureState(State.InitializedForInflate);
425+
bool refAdded = false;
426+
try
427+
{
428+
DangerousAddRef(ref refAdded);
429+
EnsureState(State.InitializedForInflate);
411430

412-
fixed (ZStream* stream = &_zStream)
431+
fixed (ZStream* stream = &_zStream)
432+
{
433+
return Interop.ZLib.Inflate(stream, flush);
434+
}
435+
}
436+
finally
413437
{
414-
return Interop.ZLib.Inflate(stream, flush);
438+
if (refAdded)
439+
{
440+
DangerousRelease();
441+
}
415442
}
416443
}
417444

418-
public unsafe ErrorCode InflateEnd()
445+
private unsafe ErrorCode InflateEnd()
419446
{
420-
EnsureNotDisposed();
421447
EnsureState(State.InitializedForInflate);
422448

423449
fixed (ZStream* stream = &_zStream)

0 commit comments

Comments
 (0)