Skip to content

Commit 484afbd

Browse files
authored
Add a Stream buffer validation helper (#1605)
* Add a Stream buffer validation helper * add ThrowHelper.ThrowIfNegative
1 parent bb3beeb commit 484afbd

File tree

7 files changed

+87
-106
lines changed

7 files changed

+87
-106
lines changed

src/Renci.SshNet/Common/ChannelInputStream.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,10 @@ public override int Read(byte[] buffer, int offset, int count)
101101
/// <exception cref="ArgumentOutOfRangeException">offset or count is negative.</exception>
102102
public override void Write(byte[] buffer, int offset, int count)
103103
{
104-
ThrowHelper.ThrowIfNull(buffer);
105-
106-
if (offset + count > buffer.Length)
107-
{
108-
throw new ArgumentException("The sum of offset and count is greater than the buffer length.");
109-
}
110-
111-
if (offset < 0 || count < 0)
112-
{
113-
throw new ArgumentOutOfRangeException(nameof(offset), "offset or count is negative.");
114-
}
104+
#if !NET
105+
ThrowHelper.
106+
#endif
107+
ValidateBufferArguments(buffer, offset, count);
115108

116109
ThrowHelper.ThrowObjectDisposedIf(_isDisposed, this);
117110

src/Renci.SshNet/Common/PacketDump.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,7 @@ public static string Create(List<byte> data, int indentLevel)
1515
public static string Create(byte[] data, int indentLevel)
1616
{
1717
ThrowHelper.ThrowIfNull(data);
18-
19-
if (indentLevel < 0)
20-
{
21-
throw new ArgumentOutOfRangeException(nameof(indentLevel), "Cannot be less than zero.");
22-
}
18+
ThrowHelper.ThrowIfNegative(indentLevel);
2319

2420
const int lineWidth = 16;
2521

src/Renci.SshNet/Common/PipeStream.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ public override void SetLength(long value)
6464
/// <inheritdoc/>
6565
public override int Read(byte[] buffer, int offset, int count)
6666
{
67+
#if !NET
68+
ThrowHelper.
69+
#endif
70+
ValidateBufferArguments(buffer, offset, count);
71+
6772
lock (_sync)
6873
{
6974
while (_head == _tail && !_disposed)
@@ -88,6 +93,11 @@ public override int Read(byte[] buffer, int offset, int count)
8893
/// <inheritdoc/>
8994
public override void Write(byte[] buffer, int offset, int count)
9095
{
96+
#if !NET
97+
ThrowHelper.
98+
#endif
99+
ValidateBufferArguments(buffer, offset, count);
100+
91101
lock (_sync)
92102
{
93103
ThrowHelper.ThrowObjectDisposedIf(_disposed, this);

src/Renci.SshNet/Common/ThrowHelper.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,48 @@ static void Throw(string? argument, string? paramName)
7777
throw new ArgumentException("The value cannot be an empty string.", paramName);
7878
}
7979
}
80+
#endif
81+
}
82+
83+
#if !NET
84+
// A rough copy of
85+
// https://github.com/dotnet/runtime/blob/1d1bf92fcf43aa6981804dc53c5174445069c9e4/src/libraries/System.Private.CoreLib/src/System/IO/Stream.cs#L960C13-L974C10
86+
// for lower targets.
87+
public static void ValidateBufferArguments(byte[] buffer, int offset, int count)
88+
{
89+
ThrowIfNull(buffer);
90+
ThrowIfNegative(offset);
91+
92+
if ((uint)count > buffer.Length - offset)
93+
{
94+
Throw();
95+
96+
[DoesNotReturn]
97+
static void Throw()
98+
{
99+
throw new ArgumentOutOfRangeException(
100+
nameof(count),
101+
"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.");
102+
}
103+
}
104+
}
105+
#endif
106+
107+
public static void ThrowIfNegative(long value, [CallerArgumentExpression(nameof(value))] string? paramName = null)
108+
{
109+
#if NET
110+
ArgumentOutOfRangeException.ThrowIfNegative(value, paramName);
111+
#else
112+
if (value < 0)
113+
{
114+
Throw(value, paramName);
115+
116+
[DoesNotReturn]
117+
static void Throw(long value, string? paramName)
118+
{
119+
throw new ArgumentOutOfRangeException(paramName, value, "Value must be non-negative.");
120+
}
121+
}
80122
#endif
81123
}
82124
}

src/Renci.SshNet/Sftp/SftpFileStream.cs

Lines changed: 20 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -511,28 +511,12 @@ public override Task FlushAsync(CancellationToken cancellationToken)
511511
/// </remarks>
512512
public override int Read(byte[] buffer, int offset, int count)
513513
{
514-
var readLen = 0;
515-
516-
ThrowHelper.ThrowIfNull(buffer);
517-
518-
#if NET
519-
ArgumentOutOfRangeException.ThrowIfNegative(offset);
520-
ArgumentOutOfRangeException.ThrowIfNegative(count);
521-
#else
522-
if (offset < 0)
523-
{
524-
throw new ArgumentOutOfRangeException(nameof(offset));
525-
}
526-
527-
if (count < 0)
528-
{
529-
throw new ArgumentOutOfRangeException(nameof(count));
530-
}
514+
#if !NET
515+
ThrowHelper.
531516
#endif
532-
if ((buffer.Length - offset) < count)
533-
{
534-
throw new ArgumentException("Invalid array range.");
535-
}
517+
ValidateBufferArguments(buffer, offset, count);
518+
519+
var readLen = 0;
536520

537521
// Lock down the file stream while we do this.
538522
lock (_lock)
@@ -653,28 +637,14 @@ public override int Read(byte[] buffer, int offset, int count)
653637
/// <returns>A <see cref="Task" /> that represents the asynchronous read operation.</returns>
654638
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
655639
{
656-
var readLen = 0;
657-
658-
ThrowHelper.ThrowIfNull(buffer);
640+
#if !NET
641+
ThrowHelper.
642+
#endif
643+
ValidateBufferArguments(buffer, offset, count);
659644

660-
#if NET
661-
ArgumentOutOfRangeException.ThrowIfNegative(offset);
662-
ArgumentOutOfRangeException.ThrowIfNegative(count);
663-
#else
664-
if (offset < 0)
665-
{
666-
throw new ArgumentOutOfRangeException(nameof(offset));
667-
}
645+
cancellationToken.ThrowIfCancellationRequested();
668646

669-
if (count < 0)
670-
{
671-
throw new ArgumentOutOfRangeException(nameof(count));
672-
}
673-
#endif
674-
if ((buffer.Length - offset) < count)
675-
{
676-
throw new ArgumentException("Invalid array range.");
677-
}
647+
var readLen = 0;
678648

679649
CheckSessionIsOpen();
680650

@@ -952,14 +922,7 @@ public override long Seek(long offset, SeekOrigin origin)
952922
/// </remarks>
953923
public override void SetLength(long value)
954924
{
955-
#if NET
956-
ArgumentOutOfRangeException.ThrowIfNegative(value);
957-
#else
958-
if (value < 0)
959-
{
960-
throw new ArgumentOutOfRangeException(nameof(value));
961-
}
962-
#endif
925+
ThrowHelper.ThrowIfNegative(value);
963926

964927
// Lock down the file stream while we do this.
965928
lock (_lock)
@@ -1005,26 +968,10 @@ public override void SetLength(long value)
1005968
/// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
1006969
public override void Write(byte[] buffer, int offset, int count)
1007970
{
1008-
ThrowHelper.ThrowIfNull(buffer);
1009-
1010-
#if NET
1011-
ArgumentOutOfRangeException.ThrowIfNegative(offset);
1012-
ArgumentOutOfRangeException.ThrowIfNegative(count);
1013-
#else
1014-
if (offset < 0)
1015-
{
1016-
throw new ArgumentOutOfRangeException(nameof(offset));
1017-
}
1018-
1019-
if (count < 0)
1020-
{
1021-
throw new ArgumentOutOfRangeException(nameof(count));
1022-
}
971+
#if !NET
972+
ThrowHelper.
1023973
#endif
1024-
if ((buffer.Length - offset) < count)
1025-
{
1026-
throw new ArgumentException("Invalid array range.");
1027-
}
974+
ValidateBufferArguments(buffer, offset, count);
1028975

1029976
// Lock down the file stream while we do this.
1030977
lock (_lock)
@@ -1105,26 +1052,12 @@ public override void Write(byte[] buffer, int offset, int count)
11051052
/// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
11061053
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
11071054
{
1108-
ThrowHelper.ThrowIfNull(buffer);
1109-
1110-
#if NET
1111-
ArgumentOutOfRangeException.ThrowIfNegative(offset);
1112-
ArgumentOutOfRangeException.ThrowIfNegative(count);
1113-
#else
1114-
if (offset < 0)
1115-
{
1116-
throw new ArgumentOutOfRangeException(nameof(offset));
1117-
}
1118-
1119-
if (count < 0)
1120-
{
1121-
throw new ArgumentOutOfRangeException(nameof(count));
1122-
}
1055+
#if !NET
1056+
ThrowHelper.
11231057
#endif
1124-
if ((buffer.Length - offset) < count)
1125-
{
1126-
throw new ArgumentException("Invalid array range.");
1127-
}
1058+
ValidateBufferArguments(buffer, offset, count);
1059+
1060+
cancellationToken.ThrowIfCancellationRequested();
11281061

11291062
CheckSessionIsOpen();
11301063

src/Renci.SshNet/ShellStream.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,11 @@ public string Read()
789789
/// <inheritdoc/>
790790
public override int Read(byte[] buffer, int offset, int count)
791791
{
792+
#if !NET
793+
ThrowHelper.
794+
#endif
795+
ValidateBufferArguments(buffer, offset, count);
796+
792797
lock (_sync)
793798
{
794799
while (_readHead == _readTail && !_disposed)
@@ -835,6 +840,11 @@ public void Write(string? text)
835840
/// <inheritdoc/>
836841
public override void Write(byte[] buffer, int offset, int count)
837842
{
843+
#if !NET
844+
ThrowHelper.
845+
#endif
846+
ValidateBufferArguments(buffer, offset, count);
847+
838848
ThrowHelper.ThrowObjectDisposedIf(_disposed, this);
839849

840850
while (count > 0)

test/Renci.SshNet.Tests/Classes/Common/PacketDumpTest.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using Microsoft.VisualStudio.TestTools.UnitTesting;
44

55
using Renci.SshNet.Common;
6-
using Renci.SshNet.Tests.Common;
76

87
namespace Renci.SshNet.Tests.Classes.Common
98
{
@@ -41,8 +40,6 @@ public void Create_ByteArrayAndIndentLevel_IndentLevelLessThanZero()
4140
{
4241
Assert.IsNull(ex.InnerException);
4342

44-
ArgumentExceptionAssert.MessageEquals("Cannot be less than zero.", ex);
45-
4643
Assert.AreEqual("indentLevel", ex.ParamName);
4744
}
4845
}

0 commit comments

Comments
 (0)