Explorar o código

Add a Stream buffer validation helper (#1605)

* Add a Stream buffer validation helper

* add ThrowHelper.ThrowIfNegative
Rob Hague hai 7 meses
pai
achega
484afbdf9d

+ 4 - 11
src/Renci.SshNet/Common/ChannelInputStream.cs

@@ -101,17 +101,10 @@ namespace Renci.SshNet.Common
         /// <exception cref="ArgumentOutOfRangeException">offset or count is negative.</exception>
         public override void Write(byte[] buffer, int offset, int count)
         {
-            ThrowHelper.ThrowIfNull(buffer);
-
-            if (offset + count > buffer.Length)
-            {
-                throw new ArgumentException("The sum of offset and count is greater than the buffer length.");
-            }
-
-            if (offset < 0 || count < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(offset), "offset or count is negative.");
-            }
+#if !NET
+            ThrowHelper.
+#endif
+            ValidateBufferArguments(buffer, offset, count);
 
             ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
 

+ 1 - 5
src/Renci.SshNet/Common/PacketDump.cs

@@ -15,11 +15,7 @@ namespace Renci.SshNet.Common
         public static string Create(byte[] data, int indentLevel)
         {
             ThrowHelper.ThrowIfNull(data);
-
-            if (indentLevel < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(indentLevel), "Cannot be less than zero.");
-            }
+            ThrowHelper.ThrowIfNegative(indentLevel);
 
             const int lineWidth = 16;
 

+ 10 - 0
src/Renci.SshNet/Common/PipeStream.cs

@@ -64,6 +64,11 @@ namespace Renci.SshNet.Common
         /// <inheritdoc/>
         public override int Read(byte[] buffer, int offset, int count)
         {
+#if !NET
+            ThrowHelper.
+#endif
+            ValidateBufferArguments(buffer, offset, count);
+
             lock (_sync)
             {
                 while (_head == _tail && !_disposed)
@@ -88,6 +93,11 @@ namespace Renci.SshNet.Common
         /// <inheritdoc/>
         public override void Write(byte[] buffer, int offset, int count)
         {
+#if !NET
+            ThrowHelper.
+#endif
+            ValidateBufferArguments(buffer, offset, count);
+
             lock (_sync)
             {
                 ThrowHelper.ThrowObjectDisposedIf(_disposed, this);

+ 42 - 0
src/Renci.SshNet/Common/ThrowHelper.cs

@@ -77,6 +77,48 @@ namespace Renci.SshNet.Common
                     throw new ArgumentException("The value cannot be an empty string.", paramName);
                 }
             }
+#endif
+        }
+
+#if !NET
+        // A rough copy of
+        // https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs#L960C13-L974C10
+        // for lower targets.
+        public static void ValidateBufferArguments(byte[] buffer, int offset, int count)
+        {
+            ThrowIfNull(buffer);
+            ThrowIfNegative(offset);
+
+            if ((uint)count > buffer.Length - offset)
+            {
+                Throw();
+
+                [DoesNotReturn]
+                static void Throw()
+                {
+                    throw new ArgumentOutOfRangeException(
+                        nameof(count),
+                        "Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.");
+                }
+            }
+        }
+#endif
+
+        public static void ThrowIfNegative(long value, [CallerArgumentExpression(nameof(value))] string? paramName = null)
+        {
+#if NET
+            ArgumentOutOfRangeException.ThrowIfNegative(value, paramName);
+#else
+            if (value < 0)
+            {
+                Throw(value, paramName);
+
+                [DoesNotReturn]
+                static void Throw(long value, string? paramName)
+                {
+                    throw new ArgumentOutOfRangeException(paramName, value, "Value must be non-negative.");
+                }
+            }
 #endif
         }
     }

+ 20 - 87
src/Renci.SshNet/Sftp/SftpFileStream.cs

@@ -511,28 +511,12 @@ namespace Renci.SshNet.Sftp
         /// </remarks>
         public override int Read(byte[] buffer, int offset, int count)
         {
-            var readLen = 0;
-
-            ThrowHelper.ThrowIfNull(buffer);
-
-#if NET
-            ArgumentOutOfRangeException.ThrowIfNegative(offset);
-            ArgumentOutOfRangeException.ThrowIfNegative(count);
-#else
-            if (offset < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(offset));
-            }
-
-            if (count < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(count));
-            }
+#if !NET
+            ThrowHelper.
 #endif
-            if ((buffer.Length - offset) < count)
-            {
-                throw new ArgumentException("Invalid array range.");
-            }
+            ValidateBufferArguments(buffer, offset, count);
+
+            var readLen = 0;
 
             // Lock down the file stream while we do this.
             lock (_lock)
@@ -653,28 +637,14 @@ namespace Renci.SshNet.Sftp
         /// <returns>A <see cref="Task" /> that represents the asynchronous read operation.</returns>
         public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
         {
-            var readLen = 0;
-
-            ThrowHelper.ThrowIfNull(buffer);
+#if !NET
+            ThrowHelper.
+#endif
+            ValidateBufferArguments(buffer, offset, count);
 
-#if NET
-            ArgumentOutOfRangeException.ThrowIfNegative(offset);
-            ArgumentOutOfRangeException.ThrowIfNegative(count);
-#else
-            if (offset < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(offset));
-            }
+            cancellationToken.ThrowIfCancellationRequested();
 
-            if (count < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(count));
-            }
-#endif
-            if ((buffer.Length - offset) < count)
-            {
-                throw new ArgumentException("Invalid array range.");
-            }
+            var readLen = 0;
 
             CheckSessionIsOpen();
 
@@ -952,14 +922,7 @@ namespace Renci.SshNet.Sftp
         /// </remarks>
         public override void SetLength(long value)
         {
-#if NET
-            ArgumentOutOfRangeException.ThrowIfNegative(value);
-#else
-            if (value < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(value));
-            }
-#endif
+            ThrowHelper.ThrowIfNegative(value);
 
             // Lock down the file stream while we do this.
             lock (_lock)
@@ -1005,26 +968,10 @@ namespace Renci.SshNet.Sftp
         /// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
         public override void Write(byte[] buffer, int offset, int count)
         {
-            ThrowHelper.ThrowIfNull(buffer);
-
-#if NET
-            ArgumentOutOfRangeException.ThrowIfNegative(offset);
-            ArgumentOutOfRangeException.ThrowIfNegative(count);
-#else
-            if (offset < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(offset));
-            }
-
-            if (count < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(count));
-            }
+#if !NET
+            ThrowHelper.
 #endif
-            if ((buffer.Length - offset) < count)
-            {
-                throw new ArgumentException("Invalid array range.");
-            }
+            ValidateBufferArguments(buffer, offset, count);
 
             // Lock down the file stream while we do this.
             lock (_lock)
@@ -1105,26 +1052,12 @@ namespace Renci.SshNet.Sftp
         /// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
         public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
         {
-            ThrowHelper.ThrowIfNull(buffer);
-
-#if NET
-            ArgumentOutOfRangeException.ThrowIfNegative(offset);
-            ArgumentOutOfRangeException.ThrowIfNegative(count);
-#else
-            if (offset < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(offset));
-            }
-
-            if (count < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(count));
-            }
+#if !NET
+            ThrowHelper.
 #endif
-            if ((buffer.Length - offset) < count)
-            {
-                throw new ArgumentException("Invalid array range.");
-            }
+            ValidateBufferArguments(buffer, offset, count);
+
+            cancellationToken.ThrowIfCancellationRequested();
 
             CheckSessionIsOpen();
 

+ 10 - 0
src/Renci.SshNet/ShellStream.cs

@@ -789,6 +789,11 @@ namespace Renci.SshNet
         /// <inheritdoc/>
         public override int Read(byte[] buffer, int offset, int count)
         {
+#if !NET
+            ThrowHelper.
+#endif
+            ValidateBufferArguments(buffer, offset, count);
+
             lock (_sync)
             {
                 while (_readHead == _readTail && !_disposed)
@@ -835,6 +840,11 @@ namespace Renci.SshNet
         /// <inheritdoc/>
         public override void Write(byte[] buffer, int offset, int count)
         {
+#if !NET
+            ThrowHelper.
+#endif
+            ValidateBufferArguments(buffer, offset, count);
+
             ThrowHelper.ThrowObjectDisposedIf(_disposed, this);
 
             while (count > 0)

+ 0 - 3
test/Renci.SshNet.Tests/Classes/Common/PacketDumpTest.cs

@@ -3,7 +3,6 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 
 using Renci.SshNet.Common;
-using Renci.SshNet.Tests.Common;
 
 namespace Renci.SshNet.Tests.Classes.Common
 {
@@ -41,8 +40,6 @@ namespace Renci.SshNet.Tests.Classes.Common
             {
                 Assert.IsNull(ex.InnerException);
 
-                ArgumentExceptionAssert.MessageEquals("Cannot be less than zero.", ex);
-
                 Assert.AreEqual("indentLevel", ex.ParamName);
             }
         }