From 24e3e8d1d2ea70ed3fe65590530d39b406abde3f Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Sun, 16 Apr 2023 08:44:06 +0200 Subject: [PATCH] Fix NREs on netstandard2.0 while reading empty strings. (#195) --- .../Netstandard2_0Extensions.cs | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/Tmds.DBus.Protocol/Netstandard2_0Extensions.cs b/src/Tmds.DBus.Protocol/Netstandard2_0Extensions.cs index a4dc2860..2946863f 100644 --- a/src/Tmds.DBus.Protocol/Netstandard2_0Extensions.cs +++ b/src/Tmds.DBus.Protocol/Netstandard2_0Extensions.cs @@ -18,8 +18,8 @@ public static bool Remove(this Dictionary dictionary public static unsafe int GetBytes(this Encoding encoding, ReadOnlySpan chars, Span bytes) { - fixed (char* pChars = chars) - fixed (byte* pBytes = bytes) + fixed (char* pChars = &GetNonNullPinnableReference(chars)) + fixed (byte* pBytes = &GetNonNullPinnableReference(bytes)) { return encoding.GetBytes(pChars, chars.Length, pBytes, bytes.Length); } @@ -27,8 +27,8 @@ public static unsafe int GetBytes(this Encoding encoding, ReadOnlySpan cha public static unsafe int GetChars(this Encoding encoding, ReadOnlySpan bytes, Span chars) { - fixed (char* pChars = chars) - fixed (byte* pBytes = bytes) + fixed (char* pChars = &GetNonNullPinnableReference(chars)) + fixed (byte* pBytes = &GetNonNullPinnableReference(bytes)) { return encoding.GetChars(pBytes, bytes.Length, pChars, chars.Length); } @@ -36,7 +36,7 @@ public static unsafe int GetChars(this Encoding encoding, ReadOnlySpan byt public static unsafe string GetString(this Encoding encoding, ReadOnlySpan bytes) { - fixed (byte* pBytes = bytes) + fixed (byte* pBytes = &GetNonNullPinnableReference(bytes)) { return encoding.GetString(pBytes, bytes.Length); } @@ -44,7 +44,7 @@ public static unsafe string GetString(this Encoding encoding, ReadOnlySpan public static unsafe int GetCharCount(this Encoding encoding, ReadOnlySpan bytes) { - fixed (byte* pBytes = bytes) + fixed (byte* pBytes = &GetNonNullPinnableReference(bytes)) { return encoding.GetCharCount(pBytes, bytes.Length); } @@ -52,7 +52,7 @@ public static unsafe int GetCharCount(this Encoding encoding, ReadOnlySpan public static unsafe int GetByteCount(this Encoding encoding, ReadOnlySpan chars) { - fixed (char* pChars = chars) + fixed (char* pChars = &GetNonNullPinnableReference(chars)) { return encoding.GetByteCount(pChars, chars.Length); } @@ -60,7 +60,7 @@ public static unsafe int GetByteCount(this Encoding encoding, ReadOnlySpan public static unsafe int GetByteCount(this Encoder encoder, ReadOnlySpan chars, bool flush) { - fixed (char* pChars = chars) + fixed (char* pChars = &GetNonNullPinnableReference(chars)) { return encoder.GetByteCount(pChars, chars.Length, flush); } @@ -68,8 +68,8 @@ public static unsafe int GetByteCount(this Encoder encoder, ReadOnlySpan c public static unsafe void Convert(this Encoder encoder, ReadOnlySpan chars, Span bytes, bool flush, out int charsUsed, out int bytesUsed, out bool completed) { - fixed (char* pChars = chars) - fixed (byte* pBytes = bytes) + fixed (char* pChars = &GetNonNullPinnableReference(chars)) + fixed (byte* pBytes = &GetNonNullPinnableReference(bytes)) { encoder.Convert(pChars, chars.Length, pBytes, bytes.Length, flush, out charsUsed, out bytesUsed, out completed); } @@ -109,7 +109,22 @@ public static async ValueTask SendAsync(this Socket socket, ReadOnlyMemory< throw new NotSupportedException(); } + + /// + /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to fake non-null pointer. Such a reference can be used + /// for pinning but must never be dereferenced. This is useful for interop with methods that do not accept null pointers for zero-sized buffers. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ref T GetNonNullPinnableReference(Span span) => ref (span.Length != 0) ? ref MemoryMarshal.GetReference(span) : ref Unsafe.AsRef((void*)1); + + /// + /// Returns a reference to the 0th element of the ReadOnlySpan. If the ReadOnlySpan is empty, returns a reference to fake non-null pointer. Such a reference + /// can be used for pinning but must never be dereferenced. This is useful for interop with methods that do not accept null pointers for zero-sized buffers. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ref T GetNonNullPinnableReference(ReadOnlySpan span) => ref (span.Length != 0) ? ref MemoryMarshal.GetReference(span) : ref Unsafe.AsRef((void*)1); } + internal sealed class UnixDomainSocketEndPoint : EndPoint { private const AddressFamily EndPointAddressFamily = AddressFamily.Unix;