Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions src/Renci.SshNet/Common/SshDataStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -371,12 +371,9 @@ public override byte[] ToArray()
/// <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;
}
Expand Down
10 changes: 10 additions & 0 deletions src/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -29,6 +32,13 @@ protected override void LoadData()
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++)
Expand Down
12 changes: 10 additions & 2 deletions src/Renci.SshNet/Sftp/SftpFileAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Text;

Expand Down Expand Up @@ -820,8 +821,15 @@ internal static SftpFileAttributes FromBytes(SshDataStream stream)

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);
Expand Down
16 changes: 4 additions & 12 deletions test/Renci.SshNet.Tests/Classes/Common/SshDataTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,23 +102,15 @@ public void ReadBytes_Length_LengthEqualsNumberOfBytesAvailable()
}

[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]
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
13 changes: 13 additions & 0 deletions test/Renci.SshNet.Tests/Classes/Sftp/SftpFileAttributesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ public void FileTypePropertiesAreMutuallyExclusive(
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()
{
Expand Down