mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-10-26 05:59:37 +00:00
Validate collection sizes before allocating (#1848)
Validate cases where (in particular) a few bytes of server data determines the size of a collection allocated, and throw if it's not possible based on the size of the buffer that the collection is purported to be contained in.
This commit is contained in:
@@ -371,12 +371,9 @@ namespace Renci.SshNet.Common
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="length"/> is greater than the internal buffer size.</exception>
|
||||
internal byte[] ReadBytes(int length)
|
||||
{
|
||||
var data = new byte[length];
|
||||
var bytesRead = Read(data, 0, length);
|
||||
if (bytesRead < length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(length), string.Format(CultureInfo.InvariantCulture, "The requested length ({0}) is greater than the actual number of bytes read ({1}).", length, bytesRead));
|
||||
}
|
||||
var data = GetRemainingBuffer().Slice(0, length).ToArray();
|
||||
|
||||
Position += length;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
using Renci.SshNet.Common;
|
||||
|
||||
namespace Renci.SshNet.Sftp.Responses
|
||||
{
|
||||
internal sealed class SftpNameResponse : SftpResponse
|
||||
@@ -29,6 +32,13 @@ namespace Renci.SshNet.Sftp.Responses
|
||||
base.LoadData();
|
||||
|
||||
Count = ReadUInt32();
|
||||
|
||||
// Each entry is at least two uints in size.
|
||||
if (Count > (DataStream.Length - DataStream.Position) / 8)
|
||||
{
|
||||
throw new SshException(string.Format(CultureInfo.InvariantCulture, "Invalid SSH_FXP_NAME count: {0}.", Count));
|
||||
}
|
||||
|
||||
Files = new KeyValuePair<string, SftpFileAttributes>[Count];
|
||||
|
||||
for (var i = 0; i < Count; i++)
|
||||
|
||||
@@ -3,6 +3,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
@@ -820,8 +821,15 @@ namespace Renci.SshNet.Sftp
|
||||
|
||||
if ((flag & SSH_FILEXFER_ATTR_EXTENDED) == SSH_FILEXFER_ATTR_EXTENDED)
|
||||
{
|
||||
var extendedCount = (int)stream.ReadUInt32();
|
||||
extensions = new Dictionary<string, string>(extendedCount);
|
||||
var extendedCount = stream.ReadUInt32();
|
||||
|
||||
// Each extension is at least two string lengths.
|
||||
if (extendedCount > (stream.Length - stream.Position) / 8)
|
||||
{
|
||||
throw new SshException(string.Format(CultureInfo.InvariantCulture, "Invalid extended attribute count: {0}.", extendedCount));
|
||||
}
|
||||
|
||||
extensions = new Dictionary<string, string>((int)extendedCount);
|
||||
for (var i = 0; i < extendedCount; i++)
|
||||
{
|
||||
var extensionName = stream.ReadString(Encoding.UTF8);
|
||||
|
||||
@@ -102,23 +102,15 @@ namespace Renci.SshNet.Tests.Classes.Common
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReadBytes_Length_LengthIsGreaterThanNumberOfBytesAvailable()
|
||||
[DataRow(4)]
|
||||
[DataRow(int.MaxValue)]
|
||||
public void ReadBytes_Length_LengthIsGreaterThanNumberOfBytesAvailable(int length)
|
||||
{
|
||||
MySshData sshData = new MySshData();
|
||||
|
||||
sshData.Load(new byte[] { 0x01, 0x0d, 0x0a }, 0, 3);
|
||||
|
||||
try
|
||||
{
|
||||
sshData.ReadBytes(4);
|
||||
Assert.Fail();
|
||||
}
|
||||
catch (ArgumentOutOfRangeException ex)
|
||||
{
|
||||
Assert.AreEqual(typeof(ArgumentOutOfRangeException), ex.GetType());
|
||||
Assert.IsNull(ex.InnerException);
|
||||
Assert.AreEqual("length", ex.ParamName);
|
||||
}
|
||||
Assert.Throws<ArgumentException>(() => sshData.ReadBytes(length));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Text;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Renci.SshNet.Common;
|
||||
using Renci.SshNet.Sftp.Responses;
|
||||
|
||||
namespace Renci.SshNet.Tests.Classes.Sftp.Responses
|
||||
{
|
||||
[TestClass]
|
||||
public class SftpNameResponseTest
|
||||
{
|
||||
[TestMethod]
|
||||
public void Load()
|
||||
{
|
||||
var target = new SftpNameResponse(3, Encoding.UTF8);
|
||||
|
||||
target.Load([
|
||||
0, 0, 0, 7, // response id
|
||||
0, 0, 0, 1, // count
|
||||
0, 0, 0, 1, (byte)'a', // filename
|
||||
0, 0, 0, 0, // longname
|
||||
0, 0, 0, 0, // attribute flags
|
||||
]);
|
||||
|
||||
Assert.AreEqual(7u, target.ResponseId);
|
||||
Assert.HasCount(1, target.Files);
|
||||
Assert.AreEqual("a", target.Files[0].Key);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Load_CountExceedsData_Throws()
|
||||
{
|
||||
var target = new SftpNameResponse(3, Encoding.UTF8);
|
||||
|
||||
var ex = Assert.ThrowsExactly<SshException>(() => target.Load([
|
||||
0, 0, 0, 7, // response id
|
||||
0xFF, 0xFF, 0xFF, 0xFF, // count
|
||||
0, 0, 0, 1, (byte)'a', // filename
|
||||
0, 0, 0, 0, // longname
|
||||
0, 0, 0, 0, // attribute flags
|
||||
]));
|
||||
|
||||
Assert.AreEqual("Invalid SSH_FXP_NAME count: 4294967295.", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,19 @@ namespace Renci.SshNet.Tests.Classes.Sftp
|
||||
Assert.AreEqual(isNamedPipe, attributes.IsNamedPipe);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FromBytes_ExtendedCountExceedsData_Throws()
|
||||
{
|
||||
var ex = Assert.ThrowsExactly<SshException>(() => SftpFileAttributes.FromBytes([
|
||||
0x80, 0, 0, 0, // flags: extended
|
||||
0x7F, 0xFF, 0xFF, 0xFF, // extended count
|
||||
0, 0, 0, 1, (byte)'a', // extension name
|
||||
0, 0, 0, 1, (byte)'b', // extension data
|
||||
]));
|
||||
|
||||
Assert.AreEqual("Invalid extended attribute count: 2147483647.", ex.Message);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FromBytesGetBytes()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user