Welcome to MSDN Blogs Sign in | Join | Help

GCHandles, Boxing and Heap Corruption

A GCHandle is a struct used to hold onto a managed object to be used by unmanaged code.  With a GCHandle you can (among other things):

  • Prevent an object from being garbage collected if unmanaged code has the only live reference to it
  • Pin an object in memory, so it won’t be relocated in memory by the garbage collector
  • Get the memory address of the pinned object

The last point is interesting.  When dealing with managed code, addresses of reference objects (objects allocated on the heap) are not constant.  As the GC tries to reclaim memory, it moves objects around in memory and compacts the heap.  If you’re P/Invoking into an unmanaged DLL, you may need to pass the address of some object.  That’s where GCHandles come in.

The syntax for allocating a pinned GCHandle is as follows (C# code):

Int[] arr = new int[10];
GCHandle gch = GCHandle.Alloc(arr, GCHandleType.Pinned);

The above code pins arr in memory (so it won’t be moved), and creates a new GCHandle that “wraps” arr.  You can now get the address of arr using GCHandle.AddrOfPinnedObject.

Note: you can only pin blittable types in memory (types that have the same representation in managed and unmanaged code).  Blittable types include primitive types and arrays.

The problem occurs when you accidentally misuse Alloc.  For example, I’ve seen code like this:

Object obj = new Object();
GCHandle gch2 = GCHandle.Alloc(Marshal.SizeOf(typeof(obj)), GCHandleType.Pinned);

It looks like the developer tried to allocate a GCHandle with the same size as obj in memory.  So what does this code actually do?

Marshall.SizeOf returns an int that when passed to a method expecting an Object, is boxed into a newly heap-allocated Object.  So the new GCHandle obediently pins this new Object in memory.  Then when you pass gch2.AddrOfPinnedObject to your unmanaged code… trouble.

Consider an unmanaged method takes an array of ints, and increments each element.  You’ve passed it an address, and it dutifully increments each value it finds for the length of the array.  Congratulations, you’ve just corrupted memory.

If you’re lucky, this crashes the runtime right away.  If you’re unlucky, your app may continue to run and crash sometime in the future or your app’s data may be messed up.

Published Friday, September 17, 2004 11:58 AM by clyon
Filed under: ,

Comments

# re: GCHandles, Boxing and Heap Corruption

Saturday, September 18, 2004 7:44 PM by Dino
Wow, I sure wouldn't want to be unlucky.

# re: GCHandles, Boxing and Heap Corruption

Thursday, September 23, 2004 1:59 AM by Gyula
Chris, what about invoking non-managed code with ref arguments?

eg.
[DllImport("adsapi32")]
static extern internal int DRV_DeviceOpen(uint card, ref int hndDriver);

...

int hnd; // it might be a member of a class
Adsapi.DRV_DeviceOpen(devNum, ref hnd);

in this case, should I pin 'hnd'?

# re: GCHandles, Boxing and Heap Corruption

Thursday, September 23, 2004 2:15 AM by Gyula
btw I'm using .NET 2.0 beta

# GCHandle.ToIntPtr vs. GCHandle.AddrOfPinnedObject

Monday, October 09, 2006 12:35 AM by Mike Stall's .NET Debugging Blog

Both GCHande's ToIntPtr and AddrOrPinnedObject take in a GCHandle and return an IntPtr. I had to do a

# re: GCHandles, Boxing and Heap Corruption

Wednesday, August 29, 2007 6:52 AM by rcw8892

I am pulling my hair out – I am trying to find a replacement for VB6 ObjPtr. Got some VB6 code I need to convert.  I have tried this but it fails.

  Private Function ObjPtr(ByVal o As Object) As Integer

       Dim GC As GCHandle = GCHandle.Alloc(o, GCHandleType.Pinned)

       Dim ret As Integer = GC.AddrOfPinnedObject.ToInt32

       GC.Free()

       Return ret

   End Function

The object is a VB 2005 TextBox.  

Any ideas how to solve this issue?

# re: GCHandles, Boxing and Heap Corruption

Friday, August 31, 2007 2:38 PM by clyon

Hi rcw8892

Your ObjPtr function will fail froa  a few reasons.  

1) o has to be a blittable type (see http://msdn2.microsoft.com/en-us/library/aa904048(VS.71).aspx)

2) You're trying to use the address after you've freed the handle.  Once you call Free, there's no guarantee that the address is still valid, since the GC could have moved your object.

-Chris

# ASP.NET Tip: How to avoid creating a GC Hole

Thursday, September 25, 2008 8:51 AM by ASP.NET Debugging

There are only a few things that can make a .NET process crash.  The most common one is an Unhandled

# Blittable and Non-Blittable,P/Invoke,Heap Crash,Marshal

Wednesday, October 08, 2008 10:36 AM by Little Snail

MostcommoncauseofCLRcrashismanagedheapscorruptionwhichoftenoccursinP/Invokeapplicatio...

# Scott Ellington’s Blog » Blog Archive » adventures in unmanaged land

Anonymous comments are disabled
 
Page view tracker