I’ve been quiet for 3 months and probably won’t have much time for blogs for next several months down the road. Today I got a chance to update my post OutOfMemoryException and Pinning to correct a mistake pointed out by our GC architect Patrick Dussud. Also I want to remind everyone that Michael Stanton posted a great article in April about how to traverse the GC heap using psscor.dll. This technique is quite useful to understand the internal of CLR’s GC. Talking about GC heap, I want to tell another GC heap corruption story.
My test buddy Chris Lyon debugged a customer program with me recently and found a very interesting user bug. He already posted the problem in an internal blog, I think it’s worthy to share it with the public community too. The problem is in this code:
SomeStruct arr = new SomeStruct[SomeSize];
GCHandle handle = GCHandle.Alloc(Marshal.SizeOf(typeof(SomeStruct)) * SomeSize, GCHandleType.Pinned);
IntPtr fixedAddr = handle.AddrOfPinnedObject ();
… //pass fixedAddr to unmanaged code to filled the array
This code tries to create a pinned GC handle for an array then pass the array’s address to unmanaged code to fill in. The code seems to assume GCHandle.Alloc has similiar semantics as malloc – tell it how much memory we need and it will allocate it for us. But it’s not true. GCHandle.Alloc only allocates a (pointer-sized) GC handle for a given object without allocating memory for the object. The first argument for this method is supposed to be the Object which the handle will point to. This program passes an Int32 (size of the array) to the method, but the compiler won’t give us any error because the nice handy auto-boxing feature. At runtime what would happen is CLR will box the integer to be a heap object, allocate a pinned handle for this integer object, then address of this object will be passed to unmanaged code. The unmanaged code will copy SomeSize * sizeof(SomeStruct) byte data to the 4 byte integer and overwrites other objects in the heap. We could just relax and wait the program to crash.
This example looks quite trivial, but it demos how easy GC heap could be corrupted by user error with help of unmanaged code. One suggestion: watch out any IntPtr you passed to unmanaged code!
This posting is provided “AS IS” with no warranties, and confers no rights.