Using restricted execution areas

I have a Visual Studio 2008 C # .NET 3.5 application that P / calls a native method that takes a file descriptor as a parameter. I originally just used FileStream.SafeFileHandle.DangerousGetHandle () to get the file descriptor. But after turning on FX COP, I received CA2001 warning about this. So, after a little research, I discovered "Limited execution areas." This is new to me, and I have not seen much information about it. I was hoping that someone more experienced could take a look and verify that I did it right.

class MyClass { public static bool Write(string filename) { using (var fs = new System.IO.FileStream(filename, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None)) { bool got_handle; bool result; System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); try { } finally { fs.SafeFileHandle.DangerousAddRef(ref got_handle); result = NativeMethods.Foo(fs.SafeFileHandle.DangerousGetHandle()); if (got_handle) fs.SafeFileHandle.DangerousRelease(); } return result; } } } internal sealed class NativeMethods { [DllImport("mylib.dll", EntryPoint = "Foo", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] public static extern bool Foo(IntPtr hFile); } 

Thanks PaulH

+3
c # pinvoke handle cer
source share
3 answers

Here you do a few things.

  • Execute the code in the finally block to prevent the use of ThreadAbortExceptions during the execution of your safe code.

  • Before the try / finally trick, you call PrepareConstrainedRegions, which basically does nothing but verify that there is enough stack stack to ensure that at least some method calls can be called so that your safe code is not caught by StackOverFlowException exceptions.

So yes, the code looks as safe as possible. The CER white paper says the CLR really recognizes this try / finally block and takes additional steps. From what I saw, there is not much difference, except that OutOfMemoryExceptions also linger after running the CER code.

To be sure that your code meets your expectations, you should create tests for these purposes.

  • Stack exhaustion
  • From memory
  • Thread.Abort

Writing reliable code is really difficult, and even most of the BCL classes are not hard on things like Joe Duffy explains. Even if your code is not a BCL code failure. You won’t get much extra benefit until the bulk of the BCL code can cope with these extreme conditions in a well-defined way.

Regards, Alois Kraus

+6
source share

A really safe way to handle this is to pass SafeHandle instead of the IntPtr link - the P / Invoke level is safe and will automatically work for you. The only exception is when you call your own API to close your handle, as SafeHandle becomes disposed of while you use it.

For example:

 [DllImport( "qwave.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true )] internal static extern bool QOSCreateHandle( ref QosVersion version, out QosSafeHandle qosHandle ); [DllImport( "qwave.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true )] [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )] internal static extern bool QOSCloseHandle( IntPtr qosHandle ); [DllImport( "qwave.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true )] internal static extern bool QOSAddSocketToFlow( QosSafeHandle qosHandle, IntPtr socket, byte[] destAddr, QosTrafficType trafficType, QosFlowFlags flags, ref uint flowId ); /// <summary> /// Safely stores a handle to the QWave QoS win32 API and ensures the handle is properly /// closed when all references to the handle have been garbage collected. /// </summary> public class QosSafeHandle : SafeHandle { /// <summary> /// Initializes a new instance of the QosSafeHandle class. /// </summary> public QosSafeHandle() : base( IntPtr.Zero, true ) { } /// <summary> /// Whether or not the handle is invalid. /// </summary> public override bool IsInvalid { get { return this.handle == IntPtr.Zero; } } /// <summary> /// Releases the Qos API instance handle. /// </summary> /// <returns></returns> protected override bool ReleaseHandle() { QosNativeMethods.QOSCloseHandle( this.handle ); return true; } } 

However, this may not be possible if the SafeHandle implementation is passed as a parameter in the structure, or if the base descriptor is larger than IntPtr. For example, the Win32 SSPI api uses descriptors, which are two IntPtrs. To deal with this situation, you must do the CER manually.

Improper use of CER. DangerousAddRef may still not work. The following is a template used by Microsoft in a .Net source:

 public static bool Write( string filename ) { using( var fs = new System.IO.FileStream( filename, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None ) ) { bool got_handle; bool result; // The CER is here to ensure that reference counting on fs.SafeFileHandle is never // corrupted. RuntimeHelpers.PrepareConstrainedRegions(); try { fs.SafeFileHandle.DangerousAddRef( ref got_handle ); } catch( Exception e ) { if( got_handle ) { fs.SafeFileHandle.DangerousRelease(); } got_handle = false; throw; } finally { if( got_handle ) { result = NativeMethods.Foo( fs.SafeFileHandle.DangerousGetHandle() ); fs.SafeFileHandle.DangerousRelease(); } } return result; } } 

This template can be seen in the Microsoft help source - see _ SafeNetHandle.cs , line 2071.

+2
source share

I don’t see how you can have problems at all if you do not throw exceptions inside the try block.

  • Is the code inside the finally section atomic?
  • Does NativeMethods.Foo() chance of memory leak or thread interruption?
0
source share

All Articles