The Problem
Even though I pinned object in managed code, the actual address passed to native code rarely changes.
How can I assure GC to pin the actual address passed to native code?
Situation : https://youtu.be/-v8GgS6mUY0
Details
I am writing a C# wrapper library for zlib based on zlibnet.
But decompressing with inflate() always failed, returning Z_STREAM_ERROR.
The error was coming from zlib's inflateStateCheck().
ZLibWrapper\ZLibWrapper\ZLibStreams.cs: DeflateStream()
_zstream = new ZStream();
int ret;
if (this._compMode == CompressionMode.Compress)
ret = ZLibNative.DeflateInit(ref _zstream, level, WriteType);
else
ret = ZLibNative.InflateInit(ref _zstream, OpenType);
ZLibWrapper\ZLibWrapper\ZLibStreams.cs: Read()
int zlibError = ZLibNative.Inflate(ref _zstream, ZLibFlush.Z_NO_FLUSH);
zlib-1.2.11\inflate.c: inflateStateCheck()
state = (struct inflate_state FAR *)strm->state;
if (state == Z_NULL || state->strm != strm ||
state->mode < HEAD || state->mode > SYNC)
return 1; // Z_STREAM_ERROR
state->strm was set at inflateInit2_, and strm was set at inflate.
C#'s garbage collector moved address of _zstream in between, casuing Z_STREAM_ERROR.
Adding GCHandle.Alloc(..., GCHandleType.Pinned) to pin the address of _zstream addressed most of the issue, but now it is happening randomly.
New code of DeflateStream()
_zstream = new ZStream();
_zstreamPtr = GCHandle.Alloc(_zstream, GCHandleType.Pinned); // NEW!
int ret;
if (this._compMode == CompressionMode.Compress)
ret = ZLibNative.DeflateInit(ref _zstream, level, WriteType);
else
ret = ZLibNative.InflateInit(ref _zstream, OpenType);
After some debugging, I found out _zstream was pinned but the actual address passed to zlib's inflate() sometimes changes.
[Managed Side]
inflateInit2_ : 0x02EBDBC4
inflate : 0x02EBDBC4
[Native Side (zlib)]
inflateInit2_ : 0x02EBCB74
inflate : 0x02BDB3E8
How can I fix this?
Source Code
To reproduce this issue, clone this repository and run tests.
Tested Envrionment
- zlibwapi.dll : zlib 1.2.11, compiled with VC2017
(x86 : ReleaseWithoutAsm, x64 : Release) - .Net Framework : 4.5, 4.5.2, 4.6.2, 4.7
EDIT: This issue does not happen when .Net Framework's clrcompression.dll, stripped version of zlib 1.2.3, is used instead of zlib 1.2.11.