Search code examples
c#.net-3.5pinvokehandles

Why is SafeHandle.DangerousGetHandle() "Dangerous"?


This is the first time ever that I'll be using SafeHandle.

I need to call this P/Invoke method that needs an UIntPtr.

    [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
    public static extern int RegOpenKeyEx(
      UIntPtr hKey,
      string subKey,
      int ulOptions,
      int samDesired,
      out UIntPtr hkResult);

This UIntPtr will be derived from .NET's RegistryKey class. I will be using the method above to convert the RegistryKey class to an IntPtr so I can use the above P/Invoke:

        private static IntPtr GetRegistryKeyHandle(RegistryKey rKey)
        {
            //Get the type of the RegistryKey
            Type registryKeyType = typeof(RegistryKey);

            //Get the FieldInfo of the 'hkey' member of RegistryKey
            System.Reflection.FieldInfo fieldInfo =
                registryKeyType.GetField("hkey", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

            //Get the handle held by hkey
            if (fieldInfo != null)
            {
                SafeHandle handle = (SafeHandle)fieldInfo.GetValue(rKey);

                //Get the unsafe handle
                IntPtr dangerousHandle = handle.DangerousGetHandle();                
                return dangerousHandle;
            }
}

Questions:

  1. Is there a better way to write this without using "unsafe" handles?
  2. Why are unsafe handles dangerous?

Solution

  • What you are doing is in fact dangerous. The RegistryKey object you use can get garbage collected and finalized while you are using the IntPtr. Which makes the handle value invalid which makes your code randomly fail. Well, okay, random failure is not exactly dangerous but it does open the door to a handle recycle attack if you in fact keep a hold of the handle for an extended period of time. The random failure mode ought to be enough to inspire you to do something about it.

    Make your pinvoke declaration look like this:

    [DllImport("advapi32.dll", CharSet=CharSet.Auto)]
    internal static extern int RegOpenKeyEx(SafeRegistryHandle key, string subkey, 
        int options, int sam, out SafeRegistryHandle result);
    

    So you can consistently use the safe handle wrapper class. Adjust the reflection code accordingly.