The Labs \ Source Viewer \ SSCLI \ System.Runtime.InteropServices \ CriticalHandle

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*============================================================
  16. **
  17. ** Class:  CriticalHandle
  18. **
  19. **
  20. ** A specially designed handle wrapper to ensure we never leak
  21. ** an OS handle.  The runtime treats this class specially during
  22. ** P/Invoke marshaling and finalization.  Users should write
  23. ** subclasses of CriticalHandle for each distinct handle type.
  24. ** This class is similar to SafeHandle, but lacks the ref counting
  25. ** behavior on marshaling that prevents handle recycling errors
  26. ** or security holes. This lowers the overhead of using the handle
  27. ** considerably, but leaves the onus on the caller to protect
  28. ** themselves from any recycling effects.
  29. **
  30. ** **** NOTE ****
  31. **
  32. ** Since there are no ref counts tracking handle usage there is
  33. ** no thread safety either. Your application must ensure that
  34. ** usages of the handle do not cross with attempts to close the
  35. ** handle (or tolerate such crossings). Normal GC mechanics will
  36. ** prevent finalization until the handle class isn't used any more,
  37. ** but explicit Close or Dispose operations may be initiated at any
  38. ** time.
  39. **
  40. ** Similarly, multiple calls to Close or Dispose on different
  41. ** threads at the same time may cause the ReleaseHandle method to be
  42. ** called more than once.
  43. **
  44. ** In general (and as might be inferred from the lack of handle
  45. ** recycle protection) you should be very cautious about exposing
  46. ** CriticalHandle instances directly or indirectly to untrusted users.
  47. ** At a minimum you should restrict their ability to queue multiple
  48. ** operations against a single handle at the same time or block their
  49. ** access to Close and Dispose unless you are very comfortable with the
  50. ** semantics of passing an invalid (or possibly invalidated and
  51. ** reallocated) to the unamanged routines you marshal your handle to
  52. ** (and the effects of closing such a handle while those calls are in
  53. ** progress). The runtime cannot protect you from undefined program
  54. ** behvior that might result from such scenarios. You have been warned.
  55. **
  56. **
  57. ===========================================================*/
  58. using System;
  59. using System.Reflection;
  60. using System.Threading;
  61. using System.Security.Permissions;
  62. using System.Runtime.CompilerServices;
  63. using System.Runtime.ConstrainedExecution;
  64. using System.IO;
  65. /*
  66.   Problems addressed by the CriticalHandle class:
  67.   1) Critical finalization - ensure we never leak OS resources in SQL.  Done
  68.     without running truly arbitrary & unbounded amounts of managed code.
  69.   2) Reduced graph promotion - during finalization, keep object graph small
  70.   3) GC.KeepAlive behavior - P/Invoke vs. finalizer thread race (HandleRef)
  71.   4) Enforcement of the above via the type system - Don't use IntPtr anymore.
  72.   Subclasses of CriticalHandle will implement the ReleaseHandle
  73.   abstract method used to execute any code required to free the
  74.   handle. This method will be prepared as a constrained execution
  75.   region at instance construction time (along with all the methods in
  76.   its statically determinable call graph). This implies that we won't
  77.   get any inconvenient jit allocation errors or rude thread abort
  78.   interrupts while releasing the handle but the user must still write
  79.   careful code to avoid injecting fault paths of their own (see the
  80.   CER spec for more details). In particular, any sub-methods you call
  81.   should be decorated with a reliability contract of the appropriate
  82.   level. In most cases this should be:
  83.     ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)
  84.   Also, any P/Invoke methods should use the
  85.   SuppressUnmanagedCodeSecurity attribute to avoid a runtime security
  86.   check that can also inject failures (even if the check is guaranteed
  87.   to pass).
  88.   Subclasses must also implement the IsInvalid property so that the
  89.   infrastructure can tell when critical finalization is actually required.
  90.   Again, this method is prepared ahead of time. It's envisioned that direct
  91.   subclasses of CriticalHandle will provide an IsInvalid implementation that suits
  92.   the general type of handle they support (null is invalid, -1 is invalid etc.)
  93.   and then these classes will be further derived for specific handle types.
  94.   Most classes using CriticalHandle should not provide a finalizer.  If they do
  95.   need to do so (ie, for flushing out file buffers, needing to write some data
  96.   back into memory, etc), then they can provide a finalizer that will be
  97.   guaranteed to run before the CriticalHandle's critical finalizer.
  98.   Subclasses are expected to be written as follows (note that
  99.   SuppressUnmanagedCodeSecurity should always be used on any P/Invoke methods
  100.   invoked as part of ReleaseHandle, in order to switch the security check from
  101.   runtime to jit time and thus remove a possible failure path from the
  102.   invocation of the method):
  103.   internal sealed MyCriticalHandleSubclass : CriticalHandle {
  104.       // Called by P/Invoke when returning CriticalHandles
  105.       private MyCriticalHandleSubclass() : base(IntPtr.Zero)
  106.       {
  107.       }
  108.       // Do not provide a finalizer - CriticalHandle's critical finalizer will
  109.       // call ReleaseHandle for you.
  110.       public override bool IsInvalid {
  111.           get { return handle == IntPtr.Zero; }
  112.       }
  113.       [DllImport(Win32Native.KERNEL32), SuppressUnmanagedCodeSecurity, ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  114.       private static extern bool CloseHandle(IntPtr handle);
  115.       override protected bool ReleaseHandle()
  116.       {
  117.           return CloseHandle(handle);
  118.       }
  119.   }
  120.   Then elsewhere to create one of these CriticalHandles, define a method
  121.   with the following type of signature (CreateFile follows this model).
  122.   Note that when returning a CriticalHandle like this, P/Invoke will call your
  123.   classes default constructor.
  124.       [DllImport(Win32Native.KERNEL32)]
  125.       private static extern MyCriticalHandleSubclass CreateHandle(int someState);
  126. */
  127. namespace System.Runtime.InteropServices
  128. {
  129.    
  130.     // This class should not be serializable - it's a handle. We require unmanaged
  131.     // code permission to subclass CriticalHandle to prevent people from writing a
  132.     // subclass and suddenly being able to run arbitrary native code with the
  133.     // same signature as CloseHandle. This is technically a little redundant, but
  134.     // we'll do this to ensure we've cut off all attack vectors. Similarly, all
  135.     // methods have a link demand to ensure untrusted code cannot directly edit
  136.     // or alter a handle.
  137.     [SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
  138.     [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
  139.    
  140.     public abstract class CriticalHandle : CriticalFinalizerObject, IDisposable
  141.     {
  142.         // ! Do not add or rearrange fields as the EE depends on this layout.
  143.         //------------------------------------------------------------------
  144.         #if DEBUG
  145.         private string _stackTrace;
  146.         // Where we allocated this CriticalHandle.
  147.         #endif
  148.         protected IntPtr handle;
  149.         // This must be protected so derived classes can use out params.
  150.         private bool _isClosed;
  151.         // Set by SetHandleAsInvalid or Close/Dispose/finalization.
  152.         // Creates a CriticalHandle class. Users must then set the Handle property or allow P/Invoke marshaling to set it implicitly.
  153.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  154.         protected CriticalHandle(IntPtr invalidHandleValue)
  155.         {
  156.             handle = invalidHandleValue;
  157.             _isClosed = false;
  158.            
  159.             #if DEBUG
  160.             if (BCLDebug.SafeHandleStackTracesEnabled)
  161.                 _stackTrace = Environment.GetStackTrace(null, false);
  162.             else
  163.                 _stackTrace = "For a stack trace showing who allocated this CriticalHandle, set SafeHandleStackTraces to 1 and rerun your app.";
  164.             #endif
  165.         }
  166.        
  167.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  168.         ~CriticalHandle()
  169.         {
  170.             Dispose(false);
  171.         }
  172.        
  173.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  174.         private void Cleanup()
  175.         {
  176.             if (IsClosed)
  177.                 return;
  178.             _isClosed = true;
  179.            
  180.             if (IsInvalid)
  181.                 return;
  182.            
  183.             // Save last error from P/Invoke in case the implementation of
  184.             // ReleaseHandle trashes it (important because this ReleaseHandle could
  185.             // occur implicitly as part of unmarshaling another P/Invoke).
  186.             int lastError = Marshal.GetLastWin32Error();
  187.            
  188.             if (!ReleaseHandle())
  189.                 FireCustomerDebugProbe();
  190.            
  191.             Marshal.SetLastWin32Error(lastError);
  192.            
  193.             GC.SuppressFinalize(this);
  194.         }
  195.        
  196.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  197.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  198.         private extern void FireCustomerDebugProbe();
  199.        
  200.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  201.         protected void SetHandle(IntPtr handle)
  202.         {
  203.             this.handle = handle;
  204.         }
  205.        
  206.         // Returns whether the handle has been explicitly marked as closed
  207.         // (Close/Dispose) or invalid (SetHandleAsInvalid).
  208.         public bool IsClosed {
  209.             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  210.             get { return _isClosed; }
  211.         }
  212.        
  213.         // Returns whether the handle looks like an invalid value (i.e. matches one
  214.         // of the handle's designated illegal values). CriticalHandle itself doesn't
  215.         // know what an invalid handle looks like, so this method is abstract and
  216.         // must be provided by a derived type.
  217.         public abstract bool IsInvalid {
  218.             [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  219.             get;
  220.         }
  221.        
  222.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  223.         public void Close()
  224.         {
  225.             Dispose(true);
  226.         }
  227.        
  228.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  229.         public void Dispose()
  230.         {
  231.             Dispose(true);
  232.         }
  233.        
  234.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  235.         protected virtual void Dispose(bool disposing)
  236.         {
  237.             Cleanup();
  238.         }
  239.        
  240.         // This should only be called for cases when you know for a fact that
  241.         // your handle is invalid and you want to record that information.
  242.         // An example is calling a syscall and getting back ERROR_INVALID_HANDLE.
  243.         // This method will normally leak handles!
  244.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  245.         public void SetHandleAsInvalid()
  246.         {
  247.             _isClosed = true;
  248.             GC.SuppressFinalize(this);
  249.         }
  250.        
  251.         // Implement this abstract method in your derived class to specify how to
  252.         // free the handle. Be careful not write any code that's subject to faults
  253.         // in this method (the runtime will prepare the infrastructure for you so
  254.         // that no jit allocations etc. will occur, but don't allocate memory unless
  255.         // you can deal with the failure and still free the handle).
  256.         // The boolean returned should be true for success and false if a
  257.         // catastrophic error occured and you wish to trigger a diagnostic for
  258.         // debugging purposes (the SafeHandleCriticalFailure MDA).
  259.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
  260.         protected abstract bool ReleaseHandle();
  261.     }
  262.    
  263. }

Developer Fusion