[MSBuild] Fix minor assembly resolution issue
[mono.git] / mcs / class / corlib / System.Runtime.InteropServices / SafeHandle.cs
1 //
2 // System.Runtime.InteropServices.SafeHandle
3 //
4 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the
8 // "Software"), to deal in the Software without restriction, including
9 // without limitation the rights to use, copy, modify, merge, publish,
10 // distribute, sublicense, and/or sell copies of the Software, and to
11 // permit persons to whom the Software is furnished to do so, subject to
12 // the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be
15 // included in all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 //
25 // Notes:
26 //     This code is only API complete, but it lacks the runtime support
27 //     for CriticalFinalizerObject and any P/Invoke wrapping that might
28 //     happen.
29 //
30 //     For details, see:
31 //     http://blogs.msdn.com/cbrumme/archive/2004/02/20/77460.aspx
32 //
33 //     CER-like behavior is implemented for Close and DangerousAddRef
34 //     via the try/finally uninterruptible pattern in case of async
35 //     exceptions like ThreadAbortException.
36 //
37 // On implementing SafeHandles:
38 //     http://blogs.msdn.com/bclteam/archive/2005/03/15/396335.aspx
39 //
40 // Issues:
41 //
42 //     TODO: Although DangerousAddRef has been implemented, I need to
43 //     find out whether the runtime performs the P/Invoke if the
44 //     handle has been disposed already.
45 //
46
47 //
48 // Copyright (c) Microsoft. All rights reserved.
49 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
50 //
51 // Files:
52 //  - mscorlib/system/runtime/interopservices/safehandle.cs
53 //
54
55 using System;
56 using System.Runtime.InteropServices;
57 using System.Runtime.ConstrainedExecution;
58 using System.Runtime.CompilerServices;
59 using System.Threading;
60
61 namespace System.Runtime.InteropServices
62 {
63         [StructLayout (LayoutKind.Sequential)]
64         public abstract class SafeHandle : CriticalFinalizerObject, IDisposable
65         {
66                 /* Warning: the offset of handle is mapped inside the runtime
67                  * if you move this, you must updated the runtime definition of
68                  * MonoSafeHandle */
69                 protected IntPtr handle;
70
71                 /*
72                  * To prevent handle recycling security attacks we must enforce the
73                  * following invariant: we cannot successfully AddRef a handle on which
74                  * we've committed to the process of releasing.
75                  *
76                  * We ensure this by never AddRef'ing a handle that is marked closed and
77                  * never marking a handle as closed while the ref count is non-zero. For
78                  * this to be thread safe we must perform inspection/updates of the two
79                  * values as a single atomic operation. We achieve this by storing them both
80                  * in a single aligned DWORD and modifying the entire state via interlocked
81                  * compare exchange operations.
82                  *
83                  * Additionally we have to deal with the problem of the Dispose operation.
84                  * We must assume that this operation is directly exposed to untrusted
85                  * callers and that malicious callers will try and use what is basically a
86                  * Release call to decrement the ref count to zero and free the handle while
87                  * it's still in use (the other way a handle recycling attack can be
88                  * mounted). We combat this by allowing only one Dispose to operate against
89                  * a given safe handle (which balances the creation operation given that
90                  * Dispose suppresses finalization). We record the fact that a Dispose has
91                  * been requested in the same state field as the ref count and closed state.
92                  *
93                  * So the state field ends up looking like this:
94                  *
95                  *  31                                                        2  1   0
96                  * +-----------------------------------------------------------+---+---+
97                  * |                           Ref count                       | D | C |
98                  * +-----------------------------------------------------------+---+---+
99                  *
100                  * Where D = 1 means a Dispose has been performed and C = 1 means the
101                  * underlying handle has (or will be shortly) released.
102                  */
103                 int state;
104
105                 bool owns_handle;
106                 bool fully_initialized;
107
108                 const int RefCount_Mask = 0x7ffffffc;
109                 const int RefCount_One = 0x4;
110
111                 enum State {
112                         Closed = 0x00000001,
113                         Disposed = 0x00000002,
114                 }
115
116 #if NET_2_1
117                 protected SafeHandle ()
118                 {
119                         throw new NotImplementedException ();
120                 }
121 #endif
122
123                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
124                 protected SafeHandle (IntPtr invalidHandleValue, bool ownsHandle)
125                 {
126                         handle = invalidHandleValue;
127                         state = RefCount_One;
128                         owns_handle = ownsHandle;
129
130                         if (!owns_handle)
131                                 GC.SuppressFinalize (this);
132
133                         fully_initialized = true;
134                 }
135
136                 ~SafeHandle ()
137                 {
138                         Dispose (false);
139                 }
140
141                 public bool IsClosed {
142                         [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
143                         get {
144                                 return (state & (int) State.Closed) != 0;
145                         }
146                 }
147
148                 public abstract bool IsInvalid {
149                         [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
150                         get;
151                 }
152
153                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
154                 protected void SetHandle (IntPtr handle)
155                 {
156                         this.handle = handle;
157                 }
158
159                 /*
160                  * This should only be called for cases when you know for a fact that
161                  * your handle is invalid and you want to record that information.
162                  * An example is calling a syscall and getting back ERROR_INVALID_HANDLE.
163                  * This method will normally leak handles!
164                  */
165                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
166                 public void SetHandleAsInvalid ()
167                 {
168                         int old_state, new_state;
169
170                         do {
171                                 old_state = state;
172                                 new_state = old_state | (int) State.Closed;
173                         } while (Interlocked.CompareExchange (ref state, new_state, old_state) != old_state);
174
175                         GC.SuppressFinalize (this);
176                 }
177
178                 /*
179                  * This method is necessary for getting an IntPtr out of a SafeHandle.
180                  * Used to tell whether a call to create the handle succeeded by comparing
181                  * the handle against a known invalid value, and for backwards
182                  * compatibility to support the handle properties returning IntPtrs on
183                  * many of our Framework classes.
184                  * Note that this method is dangerous for two reasons:
185                  *  1) If the handle has been marked invalid with SetHandleasInvalid,
186                  *     DangerousGetHandle will still return the original handle value.
187                  *  2) The handle returned may be recycled at any point. At best this means
188                  *     the handle might stop working suddenly. At worst, if the handle or
189                  *     the resource the handle represents is exposed to untrusted code in
190                  *     any way, this can lead to a handle recycling security attack (i.e. an
191                  *     untrusted caller can query data on the handle you've just returned
192                  *     and get back information for an entirely unrelated resource).
193                  */
194                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
195                 public IntPtr DangerousGetHandle ()
196                 {
197                         return handle;
198                 }
199
200                 /*
201                  * Add a reason why this handle should not be relinquished (i.e. have
202                  * ReleaseHandle called on it). This method has dangerous in the name since
203                  * it must always be used carefully (e.g. called within a CER) to avoid
204                  * leakage of the handle. It returns a boolean indicating whether the
205                  * increment was actually performed to make it easy for program logic to
206                  * back out in failure cases (i.e. is a call to DangerousRelease needed).
207                  * It is passed back via a ref parameter rather than as a direct return so
208                  * that callers need not worry about the atomicity of calling the routine
209                  * and assigning the return value to a variable (the variable should be
210                  * explicitly set to false prior to the call). The only failure cases are
211                  * when the method is interrupted prior to processing by a thread abort or
212                  * when the handle has already been (or is in the process of being)
213                  * released.
214                  */
215                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
216                 public void DangerousAddRef (ref bool success)
217                 {
218                         if (!fully_initialized)
219                                 throw new InvalidOperationException ();
220
221                         int old_state, new_state;
222
223                         do {
224                                 old_state = state;
225
226                                 if ((old_state & (int) State.Closed) != 0)
227                                         throw new ObjectDisposedException ("handle");
228
229                                 new_state = old_state + RefCount_One;
230                         } while (Interlocked.CompareExchange (ref state, new_state, old_state) != old_state);
231
232                         success = true;
233                 }
234
235                 /*
236                  * Partner to DangerousAddRef. This should always be successful when used in
237                  * a correct manner (i.e. matching a successful DangerousAddRef and called
238                  * from a region such as a CER where a thread abort cannot interrupt
239                  * processing). In the same way that unbalanced DangerousAddRef calls can
240                  * cause resource leakage, unbalanced DangerousRelease calls may cause
241                  * invalid handle states to become visible to other threads. This
242                  * constitutes a potential security hole (via handle recycling) as well as a
243                  * correctness problem -- so don't ever expose Dangerous* calls out to
244                  * untrusted code.
245                  */
246                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
247                 public void DangerousRelease ()
248                 {
249                         DangerousReleaseInternal (false);
250                 }
251
252                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
253                 public void Close ()
254                 {
255                         Dispose (true);
256                 }
257
258                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
259                 public void Dispose ()
260                 {
261                         Dispose (true);
262                         GC.SuppressFinalize (this);
263                 }
264
265                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
266                 protected virtual void Dispose (bool disposing)
267                 {
268                         if (disposing) {
269                                 if (!fully_initialized)
270                                         throw new InvalidOperationException ();
271                                 DisposeInternal ();
272                         } else {
273                                 if (fully_initialized)
274                                         DisposeInternal ();
275                         }
276                 }
277
278                 void DisposeInternal ()
279                 {
280                         DangerousReleaseInternal (true);
281                         GC.SuppressFinalize (this);
282                 }
283
284                 void DangerousReleaseInternal (bool dispose)
285                 {
286                         if (!fully_initialized)
287                                 throw new InvalidOperationException ();
288
289                         int old_state, new_state;
290
291                         /* See AddRef above for the design of the synchronization here. Basically we
292                          * will try to decrement the current ref count and, if that would take us to
293                          * zero refs, set the closed state on the handle as well. */
294                         bool perform_release = false;
295
296                         do {
297                                 old_state = state;
298
299                                 /* If this is a Dispose operation we have additional requirements (to
300                                  * ensure that Dispose happens at most once as the comments in AddRef
301                                  * detail). We must check that the dispose bit is not set in the old
302                                  * state and, in the case of successful state update, leave the disposed
303                                  * bit set. Silently do nothing if Dispose has already been called
304                                  * (because we advertise that as a semantic of Dispose). */
305                                 if (dispose && (old_state & (int) State.Disposed) != 0)
306                                         return;
307
308                                 /* We should never see a ref count of zero (that would imply we have
309                                  * unbalanced AddRef and Releases). (We might see a closed state before
310                                  * hitting zero though -- that can happen if SetHandleAsInvalid is
311                                  * used). */
312                                 if ((old_state & RefCount_Mask) == 0)
313                                         throw new ObjectDisposedException ("handle");
314
315                                 perform_release =
316                                         (old_state & RefCount_Mask) == RefCount_One
317                                          && (old_state & (int) State.Closed) == 0
318                                          && owns_handle;
319
320                                 if (perform_release && IsInvalid)
321                                         perform_release = false;
322
323                                 /* Attempt the update to the new state, fail and retry if the initial
324                                  * state has been modified in the meantime. Decrement the ref count by
325                                  * substracting SH_RefCountOne from the state then OR in the bits for
326                                  * Dispose (if that's the reason for the Release) and closed (if the
327                                  * initial ref count was 1). */
328                                 new_state =
329                                         (old_state - RefCount_One)
330                                          | ((old_state & RefCount_Mask) == RefCount_One ? (int) State.Closed : 0)
331                                          | (dispose ? (int) State.Disposed : 0);
332                         } while (Interlocked.CompareExchange (ref state, new_state, old_state) != old_state);
333
334                         if (perform_release)
335                                 ReleaseHandle ();
336                 }
337
338                 /*
339                  * Implement this abstract method in your derived class to specify how to
340                  * free the handle. Be careful not write any code that's subject to faults
341                  * in this method (the runtime will prepare the infrastructure for you so
342                  * that no jit allocations etc. will occur, but don't allocate memory unless
343                  * you can deal with the failure and still free the handle).
344                  * The boolean returned should be true for success and false if the runtime
345                  * should fire a SafeHandleCriticalFailure MDA (CustomerDebugProbe) if that
346                  * MDA is enabled.
347                  */
348                 [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
349                 protected abstract bool ReleaseHandle ();
350         }
351 }