NetSharp

NetSharp.git
git clone git://git.lenczewski.org/NetSharp.git
Log | Files | Refs | README | LICENSE

commit 1a28643dd849667c1407dfca45f3988185f86c6b
parent 72246984b124e5cab9d9e092d190cbebee97badf
Author: Mikolaj Lenczewski <mikolaj.lenczewski308@gmail.com>
Date:   Thu, 17 Sep 2020 15:37:06 +0100

Still have memory corruption errors. Going to rewrite the whole thing to try to avoid any Use-After-Free issues with the ArrayPool, and to avoid any dodgy Memory and Span trickery

Diffstat:
MNetSharp/NetSharp/NetSharp.csproj | 7+++++--
MNetSharp/NetSharp/NetSharp.xml | 54------------------------------------------------------
MNetSharp/NetSharp/Raw/Datagram/RawDatagramNetworkReader.cs | 11++++++++++-
MNetSharp/NetSharp/Raw/RawNetworkConnectionBase.cs | 29+++++++++++++++--------------
MNetSharp/NetSharp/Raw/Stream/RawStreamNetworkReader.cs | 10+++-------
MNetSharp/NetSharp/Raw/Stream/RawStreamPacketHeader.cs | 11+++++------
DNetSharp/NetSharp/Utils/Conversion/EndianAwareBitConverter.cs | 165-------------------------------------------------------------------------------
MNetSharp/NetSharp/Utils/SlimObjectPool.cs | 7+------
8 files changed, 39 insertions(+), 255 deletions(-)

diff --git a/NetSharp/NetSharp/NetSharp.csproj b/NetSharp/NetSharp/NetSharp.csproj @@ -75,4 +75,8 @@ <LastGenOutput>Resources.Designer.cs</LastGenOutput> </EmbeddedResource> </ItemGroup> -</Project> -\ No newline at end of file + + <ItemGroup> + <Folder Include="Deprecated\" /> + </ItemGroup> +</Project> diff --git a/NetSharp/NetSharp/NetSharp.xml b/NetSharp/NetSharp/NetSharp.xml @@ -493,66 +493,12 @@ Wraps the <see cref="T:System.BitConverter" /> class to provide conversion that is endian-aware. </summary> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Boolean,System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.GetBytes(System.Boolean)" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Char,System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.GetBytes(System.Char)" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Double,System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.GetBytes(System.Double)" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Single,System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.GetBytes(System.Single)" /> - </member> <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Int32,System.Boolean)"> <inheritdoc cref="M:System.BitConverter.GetBytes(System.Int32)" /> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Int64,System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.GetBytes(System.Int64)" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.Int16,System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.GetBytes(System.Int16)" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.UInt32,System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.GetBytes(System.UInt32)" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.UInt64,System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.GetBytes(System.UInt64)" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.GetBytes(System.UInt16,System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.GetBytes(System.UInt16)" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToBoolean(System.Span{System.Byte},System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.ToBoolean(System.ReadOnlySpan{System.Byte})" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToChar(System.Span{System.Byte},System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.ToChar(System.ReadOnlySpan{System.Byte})" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToDouble(System.Span{System.Byte},System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.ToDouble(System.ReadOnlySpan{System.Byte})" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToInt16(System.Span{System.Byte},System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.ToInt16(System.ReadOnlySpan{System.Byte})" /> - </member> <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToInt32(System.Span{System.Byte},System.Boolean)"> <inheritdoc cref="M:System.BitConverter.ToInt32(System.ReadOnlySpan{System.Byte})" /> </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToInt64(System.Span{System.Byte},System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.ToInt64(System.ReadOnlySpan{System.Byte})" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToSingle(System.Span{System.Byte},System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.ToSingle(System.ReadOnlySpan{System.Byte})" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToUInt16(System.Byte[],System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.ToUInt16(System.ReadOnlySpan{System.Byte})" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToUInt32(System.Span{System.Byte},System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.ToUInt32(System.ReadOnlySpan{System.Byte})" /> - </member> - <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ToUInt64(System.Span{System.Byte},System.Boolean)"> - <inheritdoc cref="M:System.BitConverter.ToUInt64(System.ReadOnlySpan{System.Byte})" /> - </member> <member name="M:NetSharp.Utils.Conversion.EndianAwareBitConverter.ReverseAsNeeded(System.Span{System.Byte},System.Boolean)"> <summary> Reverses the given bytes if the endian-nes doesn't match. diff --git a/NetSharp/NetSharp/Raw/Datagram/RawDatagramNetworkReader.cs b/NetSharp/NetSharp/Raw/Datagram/RawDatagramNetworkReader.cs @@ -80,6 +80,8 @@ namespace NetSharp.Raw.Datagram SocketAsyncEventArgs instance = new SocketAsyncEventArgs { RemoteEndPoint = DefaultEndPoint }; instance.Completed += HandleIoCompleted; + instance.RemoteEndPoint = DefaultEndPoint; + return instance; } @@ -112,6 +114,8 @@ namespace NetSharp.Raw.Datagram switch (args.SocketError) { case SocketError.Success: + byte[] testBuffer = Array.Empty<byte>(); + byte[] responseBuffer = BufferPool.Rent(datagramSize); bool responseExists = requestHandler(args.RemoteEndPoint, receiveBuffer, args.BytesTransferred, responseBuffer); @@ -136,7 +140,12 @@ namespace NetSharp.Raw.Datagram private void CompleteSendTo(SocketAsyncEventArgs args) { - CleanupTransmissionBufferAndState(args); + switch (args.SocketError) + { + default: + CleanupTransmissionBufferAndState(args); + break; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/NetSharp/NetSharp/Raw/RawNetworkConnectionBase.cs b/NetSharp/NetSharp/Raw/RawNetworkConnectionBase.cs @@ -1,5 +1,6 @@ using System; using System.Buffers; +using System.Diagnostics; using System.Net; using System.Net.Sockets; using System.Runtime.CompilerServices; @@ -76,33 +77,33 @@ namespace NetSharp.Raw argsPool = new SlimObjectPool<SocketAsyncEventArgs>(CreateStateObject, ResetStateObject, DestroyStateObject, CanReuseStateObject); // TODO implement pooling in better way + /* for (uint i = 0; i < preallocatedStateObjects; i++) { -#pragma warning disable CA2214 // Nie wywołuj w konstruktorach metod, które można przesłaniać argsPool.Return(CreateStateObject()); -#pragma warning restore CA2214 // Nie wywołuj w konstruktorach metod, które można przesłaniać } + */ } /// <summary> /// The object pool to use to pool <see cref="SocketAsyncEventArgs" /> instances. /// </summary> - protected ref readonly SlimObjectPool<SocketAsyncEventArgs> ArgsPool => ref argsPool; + protected SlimObjectPool<SocketAsyncEventArgs> ArgsPool => argsPool; /// <summary> /// The object pool to use to pool byte buffer instance. /// </summary> - protected ref readonly ArrayPool<byte> BufferPool => ref bufferPool; + protected ArrayPool<byte> BufferPool => bufferPool; /// <summary> /// The underlying connection socket. /// </summary> - protected ref readonly Socket Connection => ref connection; + protected Socket Connection => connection; /// <summary> /// The default endpoint to use to represent remote clients. /// </summary> - protected ref readonly EndPoint DefaultEndPoint => ref defaultEndPoint; + protected EndPoint DefaultEndPoint => defaultEndPoint; /// <inheritdoc /> public void Dispose() @@ -124,16 +125,15 @@ namespace NetSharp.Raw [MethodImpl(MethodImplOptions.AggressiveInlining)] protected void CleanupTransmissionBufferAndState(SocketAsyncEventArgs args) { - if (args != default) - { - if (args.Buffer != default) - { - bufferPool.Return(args.Buffer, true); - args.SetBuffer(null, 0, 0); - } + Debug.Assert(args != default, "Attempted to cleanup \'null\' SocketAsyncEventArgs instance!"); - argsPool.Return(args); + if (args.Buffer != default) + { + bufferPool.Return(args.Buffer, true); + args.SetBuffer(Array.Empty<byte>(), 0, 0); } + + argsPool.Return(args); } /// <inheritdoc cref="SlimObjectPool{T}.CreateObjectDelegate" /> @@ -151,6 +151,7 @@ namespace NetSharp.Raw protected virtual void Dispose(bool disposing) { ConnectionDisposed = true; + Debug.WriteLine("Disposing of NetworkConnectionBase!"); if (disposing) { diff --git a/NetSharp/NetSharp/Raw/Stream/RawStreamNetworkReader.cs b/NetSharp/NetSharp/Raw/Stream/RawStreamNetworkReader.cs @@ -293,7 +293,7 @@ namespace NetSharp.Raw.Stream byte[] pendingPacketDataBuffer = BufferPool.Rent(receivedPacketHeader.DataSize); args.SetBuffer(pendingPacketDataBuffer, 0, receivedPacketHeader.DataSize); - args.UserToken = new PacketReadToken(receivedPacketHeader.DataSize, receivedPacketHeader); + args.UserToken = new PacketReadToken(receivedPacketHeader.DataSize); } private void ConfigureAsyncReceiveHeader(SocketAsyncEventArgs args) @@ -303,7 +303,7 @@ namespace NetSharp.Raw.Stream byte[] pendingPacketHeaderBuffer = BufferPool.Rent(RawStreamPacketHeader.TotalSize); args.SetBuffer(pendingPacketHeaderBuffer, 0, RawStreamPacketHeader.TotalSize); - args.UserToken = new PacketReadToken(RawStreamPacketHeader.TotalSize, null); + args.UserToken = new PacketReadToken(RawStreamPacketHeader.TotalSize); } private void ConfigureAsyncSendPacket( @@ -389,13 +389,9 @@ namespace NetSharp.Raw.Stream { public readonly int BytesToTransfer; - public readonly RawStreamPacketHeader? Header; - - public PacketReadToken(int bytesToTransfer, in RawStreamPacketHeader? header) + public PacketReadToken(int bytesToTransfer) { BytesToTransfer = bytesToTransfer; - - Header = header; } } diff --git a/NetSharp/NetSharp/Raw/Stream/RawStreamPacketHeader.cs b/NetSharp/NetSharp/Raw/Stream/RawStreamPacketHeader.cs @@ -1,7 +1,6 @@ using System; using System.Runtime.CompilerServices; - -using NetSharp.Utils.Conversion; +using System.Runtime.InteropServices; namespace NetSharp.Raw.Stream { @@ -43,8 +42,7 @@ namespace NetSharp.Raw.Stream [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static RawStreamPacketHeader Deserialise(in Memory<byte> buffer) { - Span<byte> serialisedDataSize = buffer.Slice(0, sizeof(int)).Span; - int dataSize = EndianAwareBitConverter.ToInt32(serialisedDataSize); + int dataSize = MemoryMarshal.Read<int>(buffer.Span.Slice(0, sizeof(int))); return new RawStreamPacketHeader(dataSize); } @@ -58,8 +56,9 @@ namespace NetSharp.Raw.Stream [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void Serialise(in Memory<byte> buffer) { - Span<byte> serialisedDataSize = EndianAwareBitConverter.GetBytes(DataSize); - serialisedDataSize.CopyTo(buffer.Slice(0, sizeof(int)).Span); + int dataSize = DataSize; + + MemoryMarshal.Write(buffer.Span.Slice(0, sizeof(int)), ref dataSize); } } } diff --git a/NetSharp/NetSharp/Utils/Conversion/EndianAwareBitConverter.cs b/NetSharp/NetSharp/Utils/Conversion/EndianAwareBitConverter.cs @@ -1,165 +0,0 @@ -using System; -using System.Runtime.CompilerServices; - -namespace NetSharp.Utils.Conversion -{ - /// <summary> - /// Wraps the <see cref="BitConverter" /> class to provide conversion that is endian-aware. - /// </summary> - internal static class EndianAwareBitConverter - { - /// <inheritdoc cref="BitConverter.GetBytes(bool)" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Span<byte> GetBytes(bool value, bool littleEndian = false) - { - return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); - } - - /// <inheritdoc cref="BitConverter.GetBytes(char)" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Span<byte> GetBytes(char value, bool littleEndian = false) - { - return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); - } - - /// <inheritdoc cref="BitConverter.GetBytes(double)" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Span<byte> GetBytes(double value, bool littleEndian = false) - { - return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); - } - - /// <inheritdoc cref="BitConverter.GetBytes(float)" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Span<byte> GetBytes(float value, bool littleEndian = false) - { - return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); - } - - /// <inheritdoc cref="BitConverter.GetBytes(int)" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Span<byte> GetBytes(int value, bool littleEndian = false) - { - return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); - } - - /// <inheritdoc cref="BitConverter.GetBytes(long)" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Span<byte> GetBytes(long value, bool littleEndian = false) - { - return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); - } - - /// <inheritdoc cref="BitConverter.GetBytes(short)" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Span<byte> GetBytes(short value, bool littleEndian = false) - { - return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); - } - - /// <inheritdoc cref="BitConverter.GetBytes(uint)" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Span<byte> GetBytes(uint value, bool littleEndian = false) - { - return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); - } - - /// <inheritdoc cref="BitConverter.GetBytes(ulong)" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Span<byte> GetBytes(ulong value, bool littleEndian = false) - { - return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); - } - - /// <inheritdoc cref="BitConverter.GetBytes(ushort)" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static Span<byte> GetBytes(ushort value, bool littleEndian = false) - { - return ReverseAsNeeded(BitConverter.GetBytes(value), littleEndian); - } - - /// <inheritdoc cref="BitConverter.ToBoolean(ReadOnlySpan{byte})" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool ToBoolean(Span<byte> bytes, bool littleEndian = false) - { - return BitConverter.ToBoolean(ReverseAsNeeded(bytes, littleEndian)); - } - - /// <inheritdoc cref="BitConverter.ToChar(ReadOnlySpan{byte})" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static char ToChar(Span<byte> bytes, bool littleEndian = false) - { - return BitConverter.ToChar(ReverseAsNeeded(bytes, littleEndian)); - } - - /// <inheritdoc cref="BitConverter.ToDouble(ReadOnlySpan{byte})" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static double ToDouble(Span<byte> bytes, bool littleEndian = false) - { - return BitConverter.ToDouble(ReverseAsNeeded(bytes, littleEndian)); - } - - /// <inheritdoc cref="BitConverter.ToInt16(ReadOnlySpan{byte})" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static short ToInt16(Span<byte> bytes, bool littleEndian = false) - { - return BitConverter.ToInt16(ReverseAsNeeded(bytes, littleEndian)); - } - - /// <inheritdoc cref="BitConverter.ToInt32(ReadOnlySpan{byte})" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static int ToInt32(Span<byte> bytes, bool littleEndian = false) - { - return BitConverter.ToInt32(ReverseAsNeeded(bytes, littleEndian)); - } - - /// <inheritdoc cref="BitConverter.ToInt64(ReadOnlySpan{byte})" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static long ToInt64(Span<byte> bytes, bool littleEndian = false) - { - return BitConverter.ToInt64(ReverseAsNeeded(bytes, littleEndian)); - } - - /// <inheritdoc cref="BitConverter.ToSingle(ReadOnlySpan{byte})" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static float ToSingle(Span<byte> bytes, bool littleEndian = false) - { - return BitConverter.ToSingle(ReverseAsNeeded(bytes, littleEndian)); - } - - /// <inheritdoc cref="BitConverter.ToUInt16(ReadOnlySpan{byte})" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ushort ToUInt16(byte[] bytes, bool littleEndian = false) - { - return BitConverter.ToUInt16(ReverseAsNeeded(bytes, littleEndian)); - } - - /// <inheritdoc cref="BitConverter.ToUInt32(ReadOnlySpan{byte})" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static uint ToUInt32(Span<byte> bytes, bool littleEndian = false) - { - return BitConverter.ToUInt32(ReverseAsNeeded(bytes, littleEndian)); - } - - /// <inheritdoc cref="BitConverter.ToUInt64(ReadOnlySpan{byte})" /> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ulong ToUInt64(Span<byte> bytes, bool littleEndian = false) - { - return BitConverter.ToUInt64(ReverseAsNeeded(bytes, littleEndian)); - } - - /// <summary> - /// Reverses the given bytes if the endian-nes doesn't match. - /// </summary> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Span<byte> ReverseAsNeeded(Span<byte> bytes, bool toLittleEndian) - { - if (toLittleEndian != BitConverter.IsLittleEndian) - { - bytes.Reverse(); - } - - return bytes; - } - } -} diff --git a/NetSharp/NetSharp/Utils/SlimObjectPool.cs b/NetSharp/NetSharp/Utils/SlimObjectPool.cs @@ -110,12 +110,7 @@ namespace NetSharp.Utils { bool successfullyRentedInstance = objectBuffer.TryTake(out T instance); - if (successfullyRentedInstance) - { - return instance; - } - - return createObjectDelegate(); + return successfullyRentedInstance ? instance : createObjectDelegate(); } /// <summary>