diff --git a/UnitTest/TestComponentCSharp_Tests.cs b/UnitTest/TestComponentCSharp_Tests.cs index d4cbeb5fc..22ee26720 100644 --- a/UnitTest/TestComponentCSharp_Tests.cs +++ b/UnitTest/TestComponentCSharp_Tests.cs @@ -45,6 +45,34 @@ public TestCSharp() TestObject = new Class(); } + [Fact] + public void TestBufferImproperReadCopyToOutOfBounds() + { + var array = new byte[] { 0x01, 0x02, 0x03 }; + var buffer = array.AsBuffer(); + var biggerBuffer = new Windows.Storage.Streams.Buffer(5); + buffer.CopyTo(biggerBuffer); + Assert.Throws(() => biggerBuffer.ToArray(4, 2)); + } + + [Fact] + public void TestBufferImproperReadCopyToStraddleBounds() + { + var array = new byte[] { 0x01, 0x02, 0x03 }; + var buffer = array.AsBuffer(); + var bufferBig = new Windows.Storage.Streams.Buffer(5); + buffer.CopyTo(bufferBig); + Assert.Throws(() => bufferBig.ToArray(2, 2)); + } + + [Fact] + public void TestBufferImproperReadGetByte() + { + var array = new byte[] { 0x01, 0x02, 0x03 }; + var buffer = array.AsBuffer(); + Assert.Throws(() => buffer.GetByte(4)); + } + [Fact] public void TestEmptyBufferToArray() { @@ -53,6 +81,83 @@ public void TestEmptyBufferToArray() Assert.True(array.Length == 0); } + [Fact] + public void TestArrayCopyToBufferEndToBeginning() + { + IBuffer buf = new Windows.Storage.Streams.Buffer(3); + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + arr.CopyTo(3, buf, 0, 0); + } + + [Fact] + public void TestArrayCopyToBufferEndToEnd2() + { + IBuffer buf = new Windows.Storage.Streams.Buffer(3); + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + arr.CopyTo(0, buf, 0, 3); + } + + [Fact] + public void TestArrayCopyToBufferEndToEnd() + { + IBuffer buf = new Windows.Storage.Streams.Buffer(3); + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + arr.CopyTo(3, buf, 3, 0); + } + + [Fact] + public void TestArrayCopyToBufferMidToMid() + { + IBuffer buf = new Windows.Storage.Streams.Buffer(3); + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + arr.CopyTo(1, buf, 1, 0); + } + + [Fact] + public void TestArrayCopyToBufferMidToEnd() + { + IBuffer buf = new Windows.Storage.Streams.Buffer(3); + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + arr.CopyTo(1, buf, 3, 0); + } + + [Fact] + public void TestBufferCopyToArrayEndToEnd() + { + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + var buf = arr.AsBuffer(); + var target = new byte[4]; + buf.CopyTo(3, target, 4, 0); + } + + [Fact] + public void BufferToArrayWithZeroCountAtEnd2() + { + byte[] array = { 0xA1, 0xA2, 0xA3 }; + var result = array.AsBuffer().ToArray(3, 0); + Assert.True(result != null); + Assert.True(0 == result.Length); + } + + [Fact] + public void BufferToArrayWithZeroCountAtEnd_WorksWithSpans() + { + byte[] array = { 0xA1, 0xA2, 0xA3 }; + var result = array.AsSpan().Slice(3, 0).ToArray(); + Assert.True(result != null); + Assert.True(0 == result.Length); + } + + [Fact] + public void TestWinRTBufferWithZeroLength() + { + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + MemoryStream stream = new MemoryStream(arr, 0, 3, false, true); + IBuffer buff = stream.GetWindowsRuntimeBuffer(3, 0); + Assert.True(buff != null); + Assert.True(buff.Length == 0); + } + [Fact] public void TestEmptyBufferCopyTo() { @@ -125,13 +230,16 @@ public void TestDynamicInterfaceCastingOnInvalidInterface() } [Fact] - public void TestBuffer() - { - var buffer = new Windows.Storage.Streams.Buffer(2); - var array = buffer.ToArray(0, 2); - Assert.Equal(2, array.Length); + public void TestBuffer() + { + var arr1 = new byte[] { 0x01, 0x02 }; + var buff = arr1.AsBuffer(); + var arr2 = buff.ToArray(0,2); + Assert.True(arr1[0] == arr2[0]); + Assert.True(arr1[1] == arr2[1]); } -#endif + + #endif async Task TestStorageFileAsync() { diff --git a/cswinrt/strings/additions/Windows.Storage.Streams/IBufferByteAccess.cs b/cswinrt/strings/additions/Windows.Storage.Streams/IBufferByteAccess.cs index 524367d22..9dac5eee2 100644 --- a/cswinrt/strings/additions/Windows.Storage.Streams/IBufferByteAccess.cs +++ b/cswinrt/strings/additions/Windows.Storage.Streams/IBufferByteAccess.cs @@ -17,48 +17,41 @@ namespace ABI.Windows.Storage.Streams using global::System.ComponentModel; #if NETSTANDARD2_0 - [global::WinRT.ObjectReferenceWrapper(nameof(_obj)), EditorBrowsable(EditorBrowsableState.Never)] + [global::WinRT.ObjectReferenceWrapper(nameof(_obj)), EditorBrowsable(EditorBrowsableState.Never)] [Guid("905a0fef-bc53-11df-8c49-001e4fc686da")] - internal class IBufferByteAccess : global::Windows.Storage.Streams.IBufferByteAccess + internal unsafe class IBufferByteAccess : global::Windows.Storage.Streams.IBufferByteAccess { [Guid("905a0fef-bc53-11df-8c49-001e4fc686da")] public struct Vftbl { - public delegate int _get_Buffer_0(IntPtr thisPtr, out IntPtr buffer); - internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - public _get_Buffer_0 get_Buffer_0; + private void* _get_Buffer_0; + public delegate* unmanaged[Stdcall] get_Buffer_0 { get => (delegate* unmanaged[Stdcall])_get_Buffer_0; set => _get_Buffer_0 = value; } + + public unsafe delegate int _get_Buffer_0_delegate(IntPtr thisPtr, IntPtr* result); + private static readonly _get_Buffer_0_delegate DelegateCache; static unsafe Vftbl() { AbiToProjectionVftable = new Vftbl { IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, - get_Buffer_0 = Do_Abi_get_Buffer_0 - }; - var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(Marshal.SizeOf() + sizeof(IntPtr) * 12); - Marshal.StructureToPtr(AbiToProjectionVftable.IUnknownVftbl, (IntPtr)nativeVftbl, false); - nativeVftbl[3] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.get_Buffer_0); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + _get_Buffer_0 = Marshal.GetFunctionPointerForDelegate(DelegateCache = Do_Abi_get_Buffer_0).ToPointer() + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); + Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; } public static readonly Vftbl AbiToProjectionVftable; public static readonly IntPtr AbiToProjectionVftablePtr; - internal unsafe Vftbl(IntPtr thisPtr) - { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IUnknownVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); - get_Buffer_0 = Marshal.GetDelegateForFunctionPointer<_get_Buffer_0>(vftbl[3]); - } - - private static int Do_Abi_get_Buffer_0(IntPtr thisPtr, out IntPtr buffer) + private static int Do_Abi_get_Buffer_0(IntPtr thisPtr, IntPtr* buffer) { - buffer = default; + *buffer = default; try { - buffer = ComWrappersSupport.FindObject(thisPtr).Buffer; + *buffer = ComWrappersSupport.FindObject(thisPtr).Buffer; } catch (Exception ex) { @@ -84,9 +77,10 @@ internal IBufferByteAccess(ObjectReference obj) public IntPtr Buffer { get - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.get_Buffer_0(ThisPtr, out IntPtr buffer)); - return buffer; + { + IntPtr __retval = default; + Marshal.ThrowExceptionForHR(_obj.Vftbl.get_Buffer_0(ThisPtr, &__retval)); + return __retval; } } } diff --git a/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs b/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs index 35c8407b1..bb1f62069 100644 --- a/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs +++ b/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs @@ -85,8 +85,9 @@ public static void CopyTo(this byte[] source, int sourceIndex, IBuffer destinati if (destination == null) throw new ArgumentNullException(nameof(destination)); if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); if (sourceIndex < 0) throw new ArgumentOutOfRangeException(nameof(sourceIndex)); - if (source.Length <= sourceIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_IndexOutOfArrayBounds, nameof(sourceIndex)); + if (source.Length < sourceIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_IndexOutOfArrayBounds, nameof(sourceIndex)); if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); + if (destination.Capacity < destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity); if (destination.Capacity - destinationIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer); // If destination is backed by a managed array, use the array instead of the pointer as it does not require pinning: @@ -118,10 +119,9 @@ public static byte[] ToArray(this IBuffer source) public static byte[] ToArray(this IBuffer source, uint sourceIndex, int count) { if (source == null) throw new ArgumentNullException(nameof(source)); - if (source.Length == 0 && count == 0) return Array.Empty(); if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); - if (source.Capacity <= sourceIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity); - if (source.Capacity - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer); + if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length."); + if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer); if (count == 0) return Array.Empty(); @@ -141,9 +141,6 @@ public static void CopyTo(this IBuffer source, byte[] destination) if (source == null) throw new ArgumentNullException(nameof(source)); if (destination == null) throw new ArgumentNullException(nameof(destination)); - // If buffer is empty, nothing to copy - if (source.Length == 0) return; - CopyTo(source, 0, destination, 0, checked((int)source.Length)); } @@ -154,10 +151,9 @@ public static void CopyTo(this IBuffer source, uint sourceIndex, byte[] destinat if (destination == null) throw new ArgumentNullException(nameof(destination)); if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); if (destinationIndex < 0) throw new ArgumentOutOfRangeException(nameof(destinationIndex)); - if (source.Length == 0 && count == 0) return; - if (source.Capacity <= sourceIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity); - if (source.Capacity - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer); - if (destination.Length <= destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_IndexOutOfArrayBounds); + if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length."); + if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer); + if (destination.Length < destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_IndexOutOfArrayBounds); if (destination.Length - destinationIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); // If source is backed by a managed array, use the array instead of the pointer as it does not require pinning: @@ -191,10 +187,9 @@ public static void CopyTo(this IBuffer source, uint sourceIndex, IBuffer destina { if (source == null) throw new ArgumentNullException(nameof(source)); if (destination == null) throw new ArgumentNullException(nameof(destination)); - if (count == 0) return; - if (source.Capacity <= sourceIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity); - if (source.Capacity - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer); - if (destination.Capacity <= destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity); + if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length."); + if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer); + if (destination.Capacity < destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity); if (destination.Capacity - destinationIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer); // If source are destination are backed by managed arrays, use the arrays instead of the pointers as it does not require pinning: @@ -381,7 +376,7 @@ public static IBuffer GetWindowsRuntimeBuffer(this MemoryStream underlyingStream if (length < 0) throw new ArgumentOutOfRangeException(nameof(length)); - if (underlyingStream.Capacity <= positionInStream) + if (underlyingStream.Length < positionInStream) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_StreamPositionBeyondEOS); ArraySegment streamData; @@ -426,7 +421,7 @@ public static Stream AsStream(this IBuffer source) public static byte GetByte(this IBuffer source, uint byteOffset) { if (source == null) throw new ArgumentNullException(nameof(source)); - if (source.Capacity <= byteOffset) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity, nameof(byteOffset)); + if (source.Length <= byteOffset) throw new ArgumentException("The specified buffer offset is not within the buffer length."); byte[] srcDataArr; int srcDataOffs;