When pinned object is passed to native code, its address is actually not fixed

Viewed 125

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.

0 Answers
Related