Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WaitHandle and MMF security in fullfx + CreateOrOpen #65

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 58 additions & 2 deletions SharedMemory/BufferWithLocks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
#if NETFULL
using System.Security.AccessControl;
using System.Security.Permissions;
using System.Security.Principal;
#endif
using System.Text;
using System.Threading;

Expand Down Expand Up @@ -59,6 +63,18 @@ public abstract class BufferWithLocks : SharedBuffer
/// </summary>
protected EventWaitHandle ReadWaitEvent { get; private set; }

#if NETFULL
private static EventWaitHandleSecurity CreateDefaultWaitHandleSecurity()
{
EventWaitHandleSecurity eventWaitHandleSecurity = new EventWaitHandleSecurity();
eventWaitHandleSecurity.AddAccessRule(new EventWaitHandleAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null),
EventWaitHandleRights.FullControl, AccessControlType.Allow));
return eventWaitHandleSecurity;
}

private static readonly EventWaitHandleSecurity waitHandleSecurity = CreateDefaultWaitHandleSecurity();
#endif

#region Constructors

/// <summary>
Expand All @@ -70,8 +86,48 @@ public abstract class BufferWithLocks : SharedBuffer
protected BufferWithLocks(string name, long bufferSize, bool ownsSharedMemory)
: base(name, bufferSize, ownsSharedMemory)
{
WriteWaitEvent = new EventWaitHandle(true, EventResetMode.ManualReset, Name + "_evt_write");
ReadWaitEvent = new EventWaitHandle(true, EventResetMode.ManualReset, Name + "_evt_read");
#if NET45
EventWaitHandle writeWaitEvent;
if (EventWaitHandle.TryOpenExisting(Name + "_evt_write", out writeWaitEvent))
{
WriteWaitEvent = writeWaitEvent;
}
else
{
WriteWaitEvent = new EventWaitHandle(true, EventResetMode.ManualReset, Name + "_evt_write", out _, waitHandleSecurity);
}
EventWaitHandle readWaitEvent;
if (EventWaitHandle.TryOpenExisting(Name + "_evt_read", out readWaitEvent))
{
ReadWaitEvent = readWaitEvent;
}
else
{
ReadWaitEvent = new EventWaitHandle(true, EventResetMode.ManualReset, Name + "_evt_read", out _, waitHandleSecurity);
}
#elif NET40 || NET35
WriteWaitEvent = new EventWaitHandle(true, EventResetMode.ManualReset, Name + "_evt_write", out _, waitHandleSecurity);
ReadWaitEvent = new EventWaitHandle(true, EventResetMode.ManualReset, Name + "_evt_read", out _, waitHandleSecurity);
#elif NETCORE
EventWaitHandle writeWaitEvent;
if (EventWaitHandle.TryOpenExisting(Name + "_evt_write", out writeWaitEvent))
{
WriteWaitEvent = writeWaitEvent;
}
else
{
WriteWaitEvent = new EventWaitHandle(true, EventResetMode.ManualReset, Name + "_evt_write");
}
EventWaitHandle readWaitEvent;
if (EventWaitHandle.TryOpenExisting(Name + "_evt_read", out readWaitEvent))
{
ReadWaitEvent = readWaitEvent;
}
else
{
ReadWaitEvent = new EventWaitHandle(true, EventResetMode.ManualReset, Name + "_evt_read");
}
#endif
}

#endregion
Expand Down
55 changes: 39 additions & 16 deletions SharedMemory/CircularBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ namespace SharedMemory
public unsafe class CircularBuffer : SharedBuffer
{
#region Public/Protected properties

/// <summary>
/// The number of nodes within the circular linked-list
/// </summary>
public int NodeCount { get; private set; }

/// <summary>
/// The buffer size of each node
/// </summary>
public int NodeBufferSize { get; private set; }

/// <summary>
/// Event signaled when data has been written if the reading index has caught up to the writing index
/// </summary>
Expand All @@ -76,7 +76,7 @@ protected virtual long NodeHeaderOffset
return 0;
}
}

/// <summary>
/// Where the linked-list nodes are located within the buffer
/// </summary>
Expand Down Expand Up @@ -120,7 +120,7 @@ protected virtual Node* this[int i]
#region Private field members

private NodeHeader* _nodeHeader = null;

#endregion

#region Structures
Expand Down Expand Up @@ -192,7 +192,7 @@ public struct Node
/// Represents the offset relative to <see cref="SharedBuffer.BufferStartPtr"/> where the data for this node can be found.
/// </summary>
public long Offset;

/// <summary>
/// Represents the index of the current node.
/// </summary>
Expand All @@ -214,6 +214,9 @@ public struct Node
/// <param name="name">The name of the shared memory to be created</param>
/// <param name="nodeCount">The number of nodes within the circular linked-list (minimum of 2)</param>
/// <param name="nodeBufferSize">The buffer size per node in bytes. The total shared memory size will be <code>Marshal.SizeOf(SharedMemory.SharedHeader) + Marshal.SizeOf(CircularBuffer.NodeHeader) + (Marshal.SizeOf(CircularBuffer.Node) * nodeCount) + (bufferSize * nodeCount)</code></param>
#if !(NET35)
/// <param name="openExisting">Indicates whether to allow opening an existing if already exists.</param>
#endif
/// <remarks>
/// <para>The maximum total shared memory size is dependent upon the system and current memory fragmentation.</para>
/// <para>The shared memory layout on 32-bit and 64-bit architectures is:<br />
Expand All @@ -225,23 +228,43 @@ public struct Node
/// </code>
/// </para>
/// </remarks>
public CircularBuffer(string name, int nodeCount, int nodeBufferSize)
: this(name, nodeCount, nodeBufferSize, true)
public CircularBuffer(string name, int nodeCount, int nodeBufferSize
#if !(NET35)
, bool openExisting = false
#endif
)
: this(name, nodeCount, nodeBufferSize, true
#if !(NET35)
, openExisting
#endif
)
{
Open();
Open(
#if !(NET35)
openExisting
#endif
);
}

/// <summary>
/// Opens an existing <see cref="CircularBuffer"/> with the specified name.
/// </summary>
/// <param name="name">The name of an existing <see cref="CircularBuffer"/> previously created with <see cref="SharedBuffer.IsOwnerOfSharedMemory"/>=true</param>
public CircularBuffer(string name)
: this(name, 0, 0, false)
: this(name, 0, 0, false
#if !(NET35)
, false
#endif
)
{
Open();
}

private CircularBuffer(string name, int nodeCount, int nodeBufferSize, bool ownsSharedMemory)
private CircularBuffer(string name, int nodeCount, int nodeBufferSize, bool ownsSharedMemory
#if !(NET35)
, bool openExisting = false
#endif
)
: base(name, Marshal.SizeOf(typeof(NodeHeader)) + (Marshal.SizeOf(typeof(Node)) * nodeCount) + (nodeCount * (long)nodeBufferSize), ownsSharedMemory)
{
#region Argument validation
Expand Down Expand Up @@ -451,10 +474,10 @@ public virtual int Write(byte[] source, int startIndex = 0, int timeout = 1000)

// Copy the data
int amount = Math.Min(source.Length - startIndex, NodeBufferSize);

Marshal.Copy(source, startIndex, new IntPtr(BufferStartPtr + node->Offset), amount);
node->AmountWritten = amount;


// Writing is complete, make readable
PostNode(node);
Expand Down Expand Up @@ -645,9 +668,9 @@ protected virtual void ReturnNode(Node* node)
Interlocked.CompareExchange(ref _nodeHeader->ReadEnd, node->Next, blockIndex);
#pragma warning restore 0420

// If a writer thread is waiting on "node available" signal the event
// If a writer thread is waiting on "node available" signal the event
if (node->Prev == _nodeHeader->WriteStart)
NodeAvailable.Set();
NodeAvailable.Set();
}
}

Expand Down Expand Up @@ -710,7 +733,7 @@ public virtual int Read<T>(T[] destination, int startIndex = 0, int timeout = 10
/// <returns>The number of bytes read</returns>
/// <exception cref="ArgumentOutOfRangeException">If the size of <typeparamref name="T"/> is larger than <see cref="NodeBufferSize"/>.</exception>
public virtual int Read<T>(out T destination, int timeout = 1000)
where T: struct
where T : struct
{
int structSize = Marshal.SizeOf(typeof(T));
if (structSize > NodeBufferSize)
Expand Down
15 changes: 13 additions & 2 deletions SharedMemory/SharedArray.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,24 @@ public T this[int index]
/// </summary>
/// <param name="name">The name of the shared memory array to be created.</param>
/// <param name="length">The number of elements to make room for within the shared memory array.</param>
public SharedArray(string name, int length)
#if !(NET35)
/// <param name="openExisting">Indicates whether to allow opening an existing if already exists.</param>
#endif
public SharedArray(string name, int length
#if !(NET35)
, bool openExisting = false
#endif
)
: base(name, Marshal.SizeOf(typeof(T)) * length, true)
{
Length = length;
_elementSize = Marshal.SizeOf(typeof(T));

Open();
Open(
#if !(NET35)
openExisting
#endif
);
}

/// <summary>
Expand Down
55 changes: 53 additions & 2 deletions SharedMemory/SharedBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Runtime.InteropServices;
#if NETFULL
using System.IO;
using System.Security.AccessControl;
using System.Security.Permissions;
using System.Security.Principal;
#endif
using System.Text;
using System.Threading;

Expand Down Expand Up @@ -191,6 +196,28 @@ protected SharedBuffer(string name, long bufferSize, bool ownsSharedMemory)

#region Open / Close

#if NETFULL
private static readonly WellKnownSidType[] defaultAllowedList = {
WellKnownSidType.CreatorOwnerSid,
WellKnownSidType.BuiltinAdministratorsSid,
WellKnownSidType.LocalSystemSid,
WellKnownSidType.LocalServiceSid,
WellKnownSidType.NetworkServiceSid,
WellKnownSidType.WorldSid
};

private FileSecurity BuildSecurityDescriptor(IEnumerable<WellKnownSidType> allowedList)
{
FileSecurity fileSecurity = new FileSecurity();
foreach (WellKnownSidType allowed in allowedList)
{
SecurityIdentifier identity = new SecurityIdentifier(allowed, null);
fileSecurity.AddAccessRule(new FileSystemAccessRule(identity, FileSystemRights.FullControl, AccessControlType.Allow));
}
return fileSecurity;
}
#endif

/// <summary>
/// Creates a new or opens an existing shared memory buffer with the name of <see cref="Name"/> depending on the value of <see cref="IsOwnerOfSharedMemory"/>.
/// </summary>
Expand All @@ -200,7 +227,11 @@ protected SharedBuffer(string name, long bufferSize, bool ownsSharedMemory)
/// <exception cref="System.IO.FileNotFoundException">If trying to open a new shared memory buffer that does not exist as a consumer of existing buffer.</exception>
/// <exception cref="System.ArgumentOutOfRangeException">If trying to create a new shared memory buffer with a size larger than the logical addressable space.</exception>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
protected bool Open()
protected bool Open(
#if !(NET35)
bool openExisting = false
#endif
)
{
Close();

Expand All @@ -210,8 +241,28 @@ protected bool Open()
if (IsOwnerOfSharedMemory)
{
// Create a new shared memory mapping
#if NETFULL && !(NET35)
MemoryMappedFileSecurity memoryMappedFileSecurity = new MemoryMappedFileSecurity();
memoryMappedFileSecurity.SetSecurityDescriptorBinaryForm(BuildSecurityDescriptor(defaultAllowedList).GetSecurityDescriptorBinaryForm());
if (openExisting) {
Mmf = MemoryMappedFile.CreateOrOpen(Name, SharedMemorySize, MemoryMappedFileAccess.ReadWrite,
MemoryMappedFileOptions.DelayAllocatePages, memoryMappedFileSecurity, HandleInheritability.Inheritable);
} else {
Mmf = MemoryMappedFile.CreateNew(Name, SharedMemorySize, MemoryMappedFileAccess.ReadWrite,
MemoryMappedFileOptions.DelayAllocatePages, memoryMappedFileSecurity, HandleInheritability.Inheritable);
}
#elif NET35
Mmf = MemoryMappedFile.CreateNew(Name, SharedMemorySize);

#else
if (openExisting)
{
Mmf = MemoryMappedFile.CreateOrOpen(Name, SharedMemorySize);
}
else
{
Mmf = MemoryMappedFile.CreateNew(Name, SharedMemorySize);
}
#endif
// Create a view to the entire region of the shared memory
View = Mmf.CreateViewAccessor(0, SharedMemorySize, MemoryMappedFileAccess.ReadWrite);
View.SafeMemoryMappedViewHandle.AcquirePointer(ref ViewPtr);
Expand Down