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;
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.
int old_state, new_state;
do {
- old_state = state;
+ old_state = _state;
new_state = old_state | (int) State.Closed;
- } while (Interlocked.CompareExchange (ref state, new_state, old_state) != old_state);
+ } while (Interlocked.CompareExchange (ref _state, new_state, old_state) != old_state);
GC.SuppressFinalize (this);
}
- /*
- * 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;
- }
-
/*
* Add a reason why this handle should not be relinquished (i.e. have
* ReleaseHandle called on it). This method has dangerous in the name since
[ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
public void DangerousAddRef (ref bool success)
{
- if (!fully_initialized)
+ if (!_fullyInitialized)
throw new InvalidOperationException ();
int old_state, new_state;
do {
- old_state = state;
+ old_state = _state;
if ((old_state & (int) State.Closed) != 0)
throw new ObjectDisposedException ("handle");
new_state = old_state + RefCount_One;
- } while (Interlocked.CompareExchange (ref state, new_state, old_state) != old_state);
+ } while (Interlocked.CompareExchange (ref _state, new_state, old_state) != old_state);
success = true;
}
DangerousReleaseInternal (false);
}
- [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
- public void Close ()
+ void InternalDispose ()
{
- Dispose (true);
- }
-
- [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
- public void Dispose ()
- {
- Dispose (true);
- GC.SuppressFinalize (this);
+ if (!_fullyInitialized)
+ throw new InvalidOperationException ();
+ DisposeInternal ();
}
- [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
- protected virtual void Dispose (bool disposing)
+ void InternalFinalize ()
{
- if (disposing) {
- if (!fully_initialized)
- throw new InvalidOperationException ();
+ if (_fullyInitialized)
DisposeInternal ();
- } else {
- if (fully_initialized)
- DisposeInternal ();
- }
}
void DisposeInternal ()
void DangerousReleaseInternal (bool dispose)
{
- if (!fully_initialized)
+ if (!_fullyInitialized)
throw new InvalidOperationException ();
int old_state, new_state;
bool perform_release = false;
do {
- old_state = state;
+ 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
perform_release =
(old_state & RefCount_Mask) == RefCount_One
&& (old_state & (int) State.Closed) == 0
- && owns_handle;
+ && _ownsHandle;
if (perform_release && IsInvalid)
perform_release = false;
(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);
+ } while (Interlocked.CompareExchange (ref _state, new_state, old_state) != old_state);
if (perform_release)
ReleaseHandle ();