Why is SafeHandle.DangerousGetHandle() "Dangerous"? - c#

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:
Is there a better way to write this without using "unsafe" handles?
Why are unsafe handles dangerous?

The RegistryKey has a handle property. So you can use
private static IntPtr GetRegistryKeyHandle(RegistryKey rKey)
{
return rKey.Handle.DangerousGetHandle();
}
This is potentially dangerous, because the pointer you are getting may not be valid anymore when you are using it. Quote from MSDN
Using the DangerousGetHandle method can pose security risks because, if the handle has been marked as invalid with SetHandleAsInvalid, DangerousGetHandle still returns the original, potentially stale handle value. The returned handle can also be recycled at any point. At best, this means the handle might suddenly stop working. At worst, if the handle or the resource that the handle represents is exposed to untrusted code, this can lead to a recycling security attack on the reused or returned handle. For example, an untrusted caller can query data on the handle just returned and receive information for an entirely unrelated resource. See the DangerousAddRef and the DangerousRelease methods for more information about using the DangerousGetHandle methodsafely.

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.

Related

P/Invoke passing struct longer than native code expects

If I have a native code that expects a structure with two fields:
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern int my_method(ref MyStruct myStruct);
// this is what native code expects
public struct MyStruct {
IntPtr First;
IntPtr Second;
}
but instead I pass another struct to it, will it work or not - by design or by accident?
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
public static extern int my_method(ref MyLongerStruct myLongerStruct);
// this is what I want to pass to it
public struct MyLongerStruct {
IntPtr First;
IntPtr Second;
object ObjectPointer;
}
Will the object reference added to the end of the struct at C# side somehow affect P/Invoke call?
I shouldn't work. And even more, you need to add and properly set StructLayoutAttribute to the structure, as it explained here
I think, the result should be like this:
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct {
IntPtr First;
IntPtr Second;
}
If the total difference in structure is fields added to the end, and you use StructLayout to prevent the compiler from optimizing the memory layout of your struct (as Alex Butenko suggests), then it's unlikely that there will be any negative side effects apart from a slight speed hit.
When you pass a managed struct to an external function via P/Invoke (using the DllImport attribute) there is a marshaling phase that converts your struct to a compatible format for the target. For ref and out parameters the temporary is converted back when the invoked function returns, copying the values back to your struct instance. All of this is abstracted away, although exactly how the marshaling is performed for each member can be tweaked with the right attributes.
This is how the .NET framework handles strings in P/Invoke. Since it can't just send a string instance pointer to an API function that is expecting a char * (the two are nothing alike) there has to be some translation.
The fun part is that the marshaling code doesn't know anything about what the target is expecting other than what you tell it at the C# end, so if you are sending an extended version of the structure it will do the whole thing. At the other end the native code will get a pointer to a memory block containing the information it's expecting, and it won't have any way to tell that there is more after the end of the structure.
Apart from that, no problem... as long as you're passing by reference and not by value. Passing structs by value is something that should raise big red stop signs all over your brain. Don't do it, it's evil.

Constraints vs abstract class using SafeHandle

There is a method within BCryptNative called GetInt32Property.
It has the following signature:
internal static int GetInt32Property<T>(T algorithm, string property) where T : SafeHandle
This method only works when T is of type SafeBCryptAlgorithmHandle or SafeBCryptHashHandle. It calls native methods which are explicitly defined with those types of handles:
[DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
internal static extern ErrorCode BCryptGetAlgorithmProperty(SafeBCryptAlgorithmHandle hObject,
string pszProperty,
[MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
int cbOutput,
[In, Out] ref int pcbResult,
int flags);
[DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
internal static extern ErrorCode BCryptGetHashProperty(SafeBCryptHashHandle hObject,
string pszProperty,
[MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
int cbOutput,
[In, Out] ref int pcbResult,
int flags);
Microsoft uses function pointers / delegates to point to the correct native function. My question is, why didn't Microsoft just implemented the GetInt32Property method with the following signature:
internal static int GetInt32Property(SafeHandle algorithm, string property)
with the following native method:
[DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode BCryptGetProperty(SafeHandle hObject,
string pszProperty,
[MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
int cbOutput,
[In, Out] ref int pcbResult,
int flags);
Are there any downsides to this? (assuming that the SafeHandle passed to GetInt32Property is always either a SafeBCryptAlgorithmHandle or SafeBCryptHashHandle).
I'm just wondering about why Microsoft implemented this so relatively complicated.
Does it have to with:
Security-Transparent Code?
Type safety? (So that you never use any other than those two types)
Is it allowed to use SafeHandle explicitly?
According to the documentation the class must be inherited, and it is, however does a P/Invoked function handle it properly when given an abstract class of SafeHandle? Does it increment and decrement the reference counts appropriately?
It's hard to tell why Microsoft chose to implement something in one way or another, but I can answer your points.
The code isn't complex. The usage is clear (something like GetInt32Property(algorithm, str).
It doesn't force you to send one of the types you mentioned, you can still call it with a different class, as long as it implements SafeHandle.
The native methods that are used are actually the same. This is kind of odd, but I'm not an expert on this specific library so it may be for a good reason.
There's a hidden benefit for generic methods like this one. The typeof(T) == typeof(SafeBCryptHashHandle) type checks are not done in runtime, but in JIT time. This means that the method should perform slightly faster then a regular runtime check like algorith is SafeBCrypthHashHandle.
The SafeHandle class is an abstract class. This means that you cannot create an instance of it, but you can inherit it. The native function only get marshaled data, they don't get real references to objects. Don't worry about reference counting.

How can I convert IntPtr to UIntPtr?

I tried casting it like so:
UIntPtr x = (UIntPtr)intPtr;
... but the Compiler is not very happy with it and returned a compile error.
I need to do the conversion because the P/Invoke signature for RegOpenKeyEx requires a UIntPtr:
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern int RegOpenKeyEx(
UIntPtr hKey,
string subKey,
int ulOptions,
int samDesired,
out UIntPtr hkResult);
In order to get a handle, I am using SafeHandle.DangerousHandle() which returns an IntPtr:
/// <summary>
/// Get a pointer to a registry key.
/// </summary>
/// <param name="registryKey">Registry key to obtain the pointer of.</param>
/// <returns>Pointer to the given registry key.</returns>
IntPtr _getRegistryKeyHandle(RegistryKey registryKey)
{
//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
SafeHandle handle = (SafeHandle)fieldInfo.GetValue(registryKey);
//Get the unsafe handle
IntPtr dangerousHandle = handle.DangerousGetHandle();
return dangerousHandle;
}
After taking a look at msdn i noticed that both, UIntPtr and IntPtr, have the void* pointer conversion.
IntPtr a = ....;
UIntPtr b = (UIntPtr)a.ToPointer();
At this point you need unsafe code. The only way to avoid this is using the unchecked keyword and convert by using non-pointer types ( used here too ).
The reason why there is no conversion between the two pointers might be that UIntPtr like IntPtr does not have a fixed size because they are platform specific( see ).
I found BitConverter to be most reliable as it also works when using VB.NET which does not allow overflows from signed to unsigned values:
new UIntPtr(BitConverter.ToUInt64(BitConverter.GetBytes(handleIntPtr.ToInt64), 0))
Why don't you do simply do unchecked((IntPtr)(int)uintPtr) on 32 bit pointers, or unchecked((IntPtr)(long)uintPtr) on 64 bit pointers? Works well (and faster than the other solutions).
IntPtr can hold as many values as UIntPtr. Both have the same number of bits.
Overflow is a concept of arithmetic. Arithemtic is never performed on handles. This concept does not apply here.
In the entire BCL IntPtr is the standard type for handles. Use IntPtr everywhere.
The site says:
Changed IntPtr to UIntPtr: When invoking with IntPtr for the handles, you will run into an Overflow. UIntPtr is the right choice if you wish this to work correctly on 32 and 64 bit platforms.
Probably, his code was converting between types in an overflowing way. This is just a superstitious belief of his. He changed a few things. One of hem made it work. He now thinks it was the IntPtr change but it was something else.
Also, this choice has nothing to to bit the bitness of the process.
A much, much simpler solution, that does not require unsafe code, is to simply first cast the pointer to a long, and then to UIntPtr.
IntPtr ptr = …;
UIntPtr Uptr = (UIntPtr)(long)ptr;

int vs IntPtr when you have a handle?

First a background question:
In general, what is the difference between int and IntPtr? My guess is that it is an actual object rather than a value like an int or byte is. Assuming that is true:
So they are not the same. Yet I see handles represented as both.
IntPtr: Control.Handle
int (or uint): A PInvoke can be setup to return an int and it works just fine:
[DllImport("coredll.dll", SetLastError = true)]
public static extern int GetForegroundWindow();
private string GetActiveWindow()
{
const int nChars = 256;
int handle = 0;
StringBuilder Buff = new StringBuilder(nChars);
handle = CoreDLL.GetForegroundWindow();
if (CoreDLL.GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return "";
}
So, int vs IntPtr? Does it matter for handles? Can you use either?
int is 32 bits long. IntPtr is as long as a pointer for your architecture. Therefore, a pointer can be stored into an int only on 32 bit systems, while it can always be stored in an IntPtr.
Notice that your "int as a return value" example does not use an int to hold a pointer, but just to hold a numeric value. This does not mean that an int is automatically the correct size though: the author of that P/Invoke signature should have gone to the documentation for GetForegroundWindow and see that it returns a HWND.
Then, from windef.h in the Platform SDK (or this MSDN page) we can see that a HWND is a HANDLE which is a PVOID which is... a pointer!
Therefore, as far as I can tell, that signature is wrong, as the size of the return value of GetForegroundWindow depends on the architecture. Thus, it should also be an IntPtr.
Update:
While it can be inferred from the above, I think it's worthwhile to explicitly point out that:
Erroneously using int instead of IntPtr in 32-bit applications will never cause a problem, even if they run on 64-bit Windows; since most applications are 32-bit at this point in time, this will let you get away such mistakes very often.
Erroneously using int instead of IntPtr in 64-bit applications is not guaranteed to cause problems, since it is quite possible that in practice the values being encountered will fit in the 32 bits of the int. This further lessens the probability that a mistake will manifest as an application error.
Therefore, for an error to actually manifest there are three conditions that have to be satisfied at the same time:
An int is used where an IntPtr should be.
The executable image is 64-bit.
A value returned by some PInvoke call and stored as an int is actually larger than 32 bits.

How to convert a void* to a type that can be used in C#? Interoperability between C DLL and C#

I am a C/C++ programmer, but I was asked to update a program that was written in C# to communicate with a device. My knowledge of C# is very basic.
The previous version was totally written in C#, but now the API that in fact access the device was changed to C. I found out that I can import the C function APIs by using:
[DllImport("myapi.dll")]
public static extern int myfunct(
[MarshalAs(UnmanagedType.LPTStr)] string lpDeviceName,
IntPtr hpDevice);
In C this function prototype is:
int myFunct( LPTStr lpDeviceName, HANDLE* hpDevice );
Where HANDLE is defined as :
typedef void *HANDLE;
However this function does not work as expected. In fact, in the C# code call what kind of type I should declare and pass to the C# method?
Thanks for the help and sorry for any stupid question.
Actually, this is the wrong way of marshalling HANDLE *. It'll work, but not be reliable in the face of exceptions.
The function you posted looks like an object creation function (it's treating hpDevice as an output parameter, and returning an int status result).
The correct way to marshal it depends on exactly what type of object it's creating and how it is closed. Assuming that the HANDLE is closed by calling CloseHandle (which is true for most but not all HANDLE objects), then you can probably use one of the types that inherits from SafeHandleZeroOrMinusOneIsInvalid. For example, if the object is a registry key, then use SafeRegistryHandle; if it's a file, then use SafeFileHandle.
If it's some type for which there isn't an existing safe handle type (but does use CloseHandle to close it), then you'll have to define your own safe handle type derived from SafeHandleZeroOrMinusOneIsInvalid. If it's some type that doesn't use CloseHandle to close it, then you'll have to define your own safe handle type derived from SafeHandle.
Once you have determined the correct SafeHandle-derived type, then you can use it in the function call (using SafeFileHandle as an example):
[DllImport("myapi.dll")]
public static extern int myFunct(
[MarshalAs(UnmanagedType.LPTStr)] string lpDeviceName,
out SafeFileHandle hpDevice);
you are passing IntPtr instead of ref IntPtr, the definition should look like this:
[DllImport("myapi.dll")]
public static extern int myfunct(
[MarshalAs(UnmanagedType.LPTStr)] string lpDeviceName,
ref IntPtr hpDevice);

Categories

Resources