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