X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2Fcorlib%2FSystem.Runtime.InteropServices%2FSafeHandle.cs;h=a01d526ff2588d5e1d32eea908836061f47b91c2;hb=f31d87482950f6729a09cd86f53b77f332d883b9;hp=c706ceed6184073e2a374ed306b1eaf70d0d5c16;hpb=ead5d3a4205da4ef5927e579719cc65e2190aad2;p=mono.git diff --git a/mcs/class/corlib/System.Runtime.InteropServices/SafeHandle.cs b/mcs/class/corlib/System.Runtime.InteropServices/SafeHandle.cs index c706ceed618..a01d526ff25 100644 --- a/mcs/class/corlib/System.Runtime.InteropServices/SafeHandle.cs +++ b/mcs/class/corlib/System.Runtime.InteropServices/SafeHandle.cs @@ -61,50 +61,8 @@ using System.Threading; namespace System.Runtime.InteropServices { [StructLayout (LayoutKind.Sequential)] - public abstract class SafeHandle : CriticalFinalizerObject, IDisposable + public abstract partial class SafeHandle { - /* Warning: the offset of handle is mapped inside the runtime - * if you move this, you must updated the runtime definition of - * MonoSafeHandle */ - protected IntPtr handle; - - /* - * To prevent handle recycling security attacks we must enforce the - * following invariant: we cannot successfully AddRef a handle on which - * we've committed to the process of releasing. - * - * We ensure this by never AddRef'ing a handle that is marked closed and - * never marking a handle as closed while the ref count is non-zero. For - * this to be thread safe we must perform inspection/updates of the two - * values as a single atomic operation. We achieve this by storing them both - * in a single aligned DWORD and modifying the entire state via interlocked - * compare exchange operations. - * - * Additionally we have to deal with the problem of the Dispose operation. - * We must assume that this operation is directly exposed to untrusted - * callers and that malicious callers will try and use what is basically a - * Release call to decrement the ref count to zero and free the handle while - * it's still in use (the other way a handle recycling attack can be - * mounted). We combat this by allowing only one Dispose to operate against - * a given safe handle (which balances the creation operation given that - * Dispose suppresses finalization). We record the fact that a Dispose has - * been requested in the same state field as the ref count and closed state. - * - * So the state field ends up looking like this: - * - * 31 2 1 0 - * +-----------------------------------------------------------+---+---+ - * | Ref count | D | C | - * +-----------------------------------------------------------+---+---+ - * - * Where D = 1 means a Dispose has been performed and C = 1 means the - * underlying handle has (or will be shortly) released. - */ - int state; - - bool owns_handle; - bool fully_initialized; - const int RefCount_Mask = 0x7ffffffc; const int RefCount_One = 0x4; @@ -113,49 +71,6 @@ namespace System.Runtime.InteropServices Disposed = 0x00000002, } -#if NET_2_1 - protected SafeHandle () - { - throw new NotImplementedException (); - } -#endif - - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] - protected SafeHandle (IntPtr invalidHandleValue, bool ownsHandle) - { - handle = invalidHandleValue; - state = RefCount_One; - owns_handle = ownsHandle; - - if (!owns_handle) - GC.SuppressFinalize (this); - - fully_initialized = true; - } - - ~SafeHandle () - { - Dispose (false); - } - - public bool IsClosed { - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] - get { - return (state & (int) State.Closed) != 0; - } - } - - public abstract bool IsInvalid { - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] - get; - } - - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] - protected void SetHandle (IntPtr handle) - { - this.handle = handle; - } - /* * This should only be called for cases when you know for a fact that * your handle is invalid and you want to record that information. @@ -165,36 +80,17 @@ namespace System.Runtime.InteropServices [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] public void SetHandleAsInvalid () { - int old_state, new_state; - - do { - old_state = state; - new_state = old_state | (int) State.Closed; - } while (Interlocked.CompareExchange (ref state, new_state, old_state) != old_state); + try {} + finally { + int old_state, new_state; - GC.SuppressFinalize (this); - } + do { + old_state = _state; + new_state = old_state | (int) State.Closed; + } while (Interlocked.CompareExchange (ref _state, new_state, old_state) != old_state); - /* - * This method is necessary for getting an IntPtr out of a SafeHandle. - * Used to tell whether a call to create the handle succeeded by comparing - * the handle against a known invalid value, and for backwards - * compatibility to support the handle properties returning IntPtrs on - * many of our Framework classes. - * Note that this method is dangerous for two reasons: - * 1) If the handle has been marked invalid with SetHandleasInvalid, - * DangerousGetHandle will still return the original handle value. - * 2) The handle returned may be recycled at any point. At best this means - * the handle might stop working suddenly. At worst, if the handle or - * the resource the handle represents is exposed to untrusted code in - * any way, this can lead to a handle recycling security attack (i.e. an - * untrusted caller can query data on the handle you've just returned - * and get back information for an entirely unrelated resource). - */ - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] - public IntPtr DangerousGetHandle () - { - return handle; + GC.SuppressFinalize (this); + } } /* @@ -215,21 +111,24 @@ namespace System.Runtime.InteropServices [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] public void DangerousAddRef (ref bool success) { - if (!fully_initialized) - throw new InvalidOperationException (); + try {} + finally { + if (!_fullyInitialized) + throw new InvalidOperationException (); - int old_state, new_state; + int old_state, new_state; - do { - old_state = state; + do { + old_state = _state; - if ((old_state & (int) State.Closed) != 0) - throw new ObjectDisposedException ("handle"); + if ((old_state & (int) State.Closed) != 0) + throw new ObjectDisposedException (null, "Safe handle has been closed"); - new_state = old_state + RefCount_One; - } while (Interlocked.CompareExchange (ref state, new_state, old_state) != old_state); + new_state = old_state + RefCount_One; + } while (Interlocked.CompareExchange (ref _state, new_state, old_state) != old_state); - success = true; + success = true; + } } /* @@ -249,103 +148,84 @@ namespace System.Runtime.InteropServices DangerousReleaseInternal (false); } - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] - public void Close () + void InternalDispose () { - Dispose (true); - } + if (!_fullyInitialized) + throw new InvalidOperationException (); - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] - public void Dispose () - { - Dispose (true); + DangerousReleaseInternal (true); GC.SuppressFinalize (this); } - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] - protected virtual void Dispose (bool disposing) - { - if (disposing) { - if (!fully_initialized) - throw new InvalidOperationException (); - DisposeInternal (); - } else { - if (fully_initialized) - DisposeInternal (); - } - } - - void DisposeInternal () + void InternalFinalize () { - DangerousReleaseInternal (true); - GC.SuppressFinalize (this); + if (_fullyInitialized) + DangerousReleaseInternal (true); } void DangerousReleaseInternal (bool dispose) { - if (!fully_initialized) - throw new InvalidOperationException (); - - int old_state, new_state; - - /* See AddRef above for the design of the synchronization here. Basically we - * will try to decrement the current ref count and, if that would take us to - * zero refs, set the closed state on the handle as well. */ - bool perform_release = false; - - do { - old_state = state; - - /* If this is a Dispose operation we have additional requirements (to - * ensure that Dispose happens at most once as the comments in AddRef - * detail). We must check that the dispose bit is not set in the old - * state and, in the case of successful state update, leave the disposed - * bit set. Silently do nothing if Dispose has already been called - * (because we advertise that as a semantic of Dispose). */ - if (dispose && (old_state & (int) State.Disposed) != 0) - return; - - /* We should never see a ref count of zero (that would imply we have - * unbalanced AddRef and Releases). (We might see a closed state before - * hitting zero though -- that can happen if SetHandleAsInvalid is - * used). */ - if ((old_state & RefCount_Mask) == 0) - throw new ObjectDisposedException ("handle"); - - perform_release = - (old_state & RefCount_Mask) == RefCount_One - && (old_state & (int) State.Closed) == 0 - && owns_handle; - - if (perform_release && IsInvalid) - perform_release = false; - - /* Attempt the update to the new state, fail and retry if the initial - * state has been modified in the meantime. Decrement the ref count by - * substracting SH_RefCountOne from the state then OR in the bits for - * Dispose (if that's the reason for the Release) and closed (if the - * initial ref count was 1). */ - new_state = - (old_state - RefCount_One) - | ((old_state & RefCount_Mask) == RefCount_One ? (int) State.Closed : 0) - | (dispose ? (int) State.Disposed : 0); - } while (Interlocked.CompareExchange (ref state, new_state, old_state) != old_state); + try {} + finally { + if (!_fullyInitialized) + throw new InvalidOperationException (); - if (perform_release) - ReleaseHandle (); + int old_state, new_state; + + /* See AddRef above for the design of the synchronization here. Basically we + * will try to decrement the current ref count and, if that would take us to + * zero refs, set the closed state on the handle as well. */ + bool perform_release = false; + + do { + old_state = _state; + + /* If this is a Dispose operation we have additional requirements (to + * ensure that Dispose happens at most once as the comments in AddRef + * detail). We must check that the dispose bit is not set in the old + * state and, in the case of successful state update, leave the disposed + * bit set. Silently do nothing if Dispose has already been called + * (because we advertise that as a semantic of Dispose). */ + if (dispose && (old_state & (int) State.Disposed) != 0) { + /* we cannot use `return` in a finally block, so we have to ensure + * that we are not releasing the handle */ + perform_release = false; + break; + } + + /* We should never see a ref count of zero (that would imply we have + * unbalanced AddRef and Releases). (We might see a closed state before + * hitting zero though -- that can happen if SetHandleAsInvalid is + * used). */ + if ((old_state & RefCount_Mask) == 0) + throw new ObjectDisposedException (null, "Safe handle has been closed"); + + if ((old_state & RefCount_Mask) != RefCount_One) + perform_release = false; + else if ((old_state & (int) State.Closed) != 0) + perform_release = false; + else if (!_ownsHandle) + perform_release = false; + else if (IsInvalid) + perform_release = false; + else + perform_release = true; + + /* Attempt the update to the new state, fail and retry if the initial + * state has been modified in the meantime. Decrement the ref count by + * substracting SH_RefCountOne from the state then OR in the bits for + * Dispose (if that's the reason for the Release) and closed (if the + * initial ref count was 1). */ + new_state = (old_state & RefCount_Mask) - RefCount_One; + if ((old_state & RefCount_Mask) == RefCount_One) + new_state |= (int) State.Closed; + if (dispose) + new_state |= (int) State.Disposed; + } while (Interlocked.CompareExchange (ref _state, new_state, old_state) != old_state); + + if (perform_release) + ReleaseHandle (); + } } - - /* - * Implement this abstract method in your derived class to specify how to - * free the handle. Be careful not write any code that's subject to faults - * in this method (the runtime will prepare the infrastructure for you so - * that no jit allocations etc. will occur, but don't allocate memory unless - * you can deal with the failure and still free the handle). - * The boolean returned should be true for success and false if the runtime - * should fire a SafeHandleCriticalFailure MDA (CustomerDebugProbe) if that - * MDA is enabled. - */ - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] - protected abstract bool ReleaseHandle (); } }