Skip to content

Commit a1a371d

Browse files
added real bounds checking to ByteString.Slice (#6709)
* added real bounds checking to `ByteString.Slice` Fixed a very weird bit of code that could result in byte buffers constantly allocating despite being of illegal size. * Fix zero length slice * Fix index out of bound bug --------- Co-authored-by: Gregorius Soedharmo <[email protected]>
1 parent 9676cc1 commit a1a371d

File tree

3 files changed

+33
-28
lines changed

3 files changed

+33
-28
lines changed

src/core/Akka.Streams.Tests/IO/InputStreamSinkSpec.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,11 @@ await this.AssertAllStagesStoppedAsync(() => {
168168

169169
while (!bytes.IsEmpty)
170170
{
171-
var expected = bytes.Slice(0, 3);
172-
bytes = bytes.Slice(3);
171+
var max = Math.Min(bytes.Count, 3);
172+
var expected = bytes.Slice(0, max);
173+
bytes = bytes.Slice(max);
173174

174-
var result = ReadN(inputStream, 3);
175+
var result = ReadN(inputStream, max);
175176
result.Item1.Should().Be(expected.Count);
176177
result.Item2.Should().BeEquivalentTo(expected);
177178
}

src/core/Akka.Tests/Util/ByteStringSpec.cs

+15-12
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Linq;
99
using System.Text;
1010
using Akka.IO;
11+
using FluentAssertions;
1112
using FsCheck;
1213
using Xunit;
1314

@@ -44,8 +45,10 @@ public void A_ByteString_must_have_correct_size_when_concatenating()
4445
[Fact]
4546
public void A_ByteString_must_have_correct_size_when_slicing_from_index()
4647
{
47-
Prop.ForAll((ByteString a, ByteString b) => (a + b).Slice(b.Count).Count == a.Count)
48-
.QuickCheckThrowOnFailure();
48+
var a = ByteString.FromBytes(new byte[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9} );
49+
var b = ByteString.FromBytes(new byte[] { 10, 11, 12, 13, 14, 15, 16, 17, 18 });
50+
51+
(a + b).Slice(b.Count).Count.Should().Be(a.Count);
4952
}
5053

5154
[Fact]
@@ -57,8 +60,10 @@ public void A_ByteString_must_be_sequential_when_slicing_from_start()
5760
[Fact]
5861
public void A_ByteString_must_be_sequential_when_slicing_from_index()
5962
{
60-
Prop.ForAll((ByteString a, ByteString b) => (a + b).Slice(a.Count).SequenceEqual(b))
61-
.QuickCheckThrowOnFailure();
63+
var a = ByteString.FromBytes(new byte[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9} );
64+
var b = ByteString.FromBytes(new byte[] { 10, 11, 12, 13, 14, 15, 16, 17, 18 });
65+
66+
(a + b).Slice(a.Count).Should().BeEquivalentTo(b);
6267
}
6368

6469
[Fact]
@@ -74,14 +79,12 @@ public void A_ByteString_must_be_equal_to_the_original_when_compacting()
7479
[Fact]
7580
public void A_ByteString_must_be_equal_to_the_original_when_recombining()
7681
{
77-
Prop.ForAll((ByteString xs, int until) =>
78-
{
79-
var tmp1 = xs.Slice(0, until);
80-
var tmp2 = xs.Slice(until);
81-
var tmp11 = tmp1.Slice(0, until);
82-
var tmp12 = tmp1.Slice(until);
83-
return (tmp11 + tmp12 + tmp2).SequenceEqual(xs);
84-
}).QuickCheckThrowOnFailure();
82+
var xs = ByteString.FromBytes(new byte[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9} );
83+
var tmp1 = xs.Slice(0, xs.Count / 2);
84+
var tmp2 = xs.Slice(xs.Count / 2);
85+
var tmp11 = tmp1.Slice(0, tmp1.Count / 2);
86+
var tmp12 = tmp1.Slice(tmp1.Count / 2);
87+
(tmp11 + tmp12 + tmp2).Should().BeEquivalentTo(xs);
8588
}
8689

8790
[Fact]

src/core/Akka/Util/ByteString.cs

+14-13
Original file line numberDiff line numberDiff line change
@@ -332,21 +332,22 @@ public ByteString Compact()
332332
/// </summary>
333333
/// <param name="index">index inside current <see cref="ByteString"/>, from which slicing should start</param>
334334
/// <param name="count">Number of bytes to fit into new <see cref="ByteString"/>.</param>
335-
/// <returns></returns>
335+
/// <exception cref="ArgumentOutOfRangeException">If index or count result in an invalid <see cref="ByteString"/>.</exception>
336336
public ByteString Slice(int index, int count)
337337
{
338-
//TODO: this is really stupid, but previous impl didn't throw if arguments
339-
// were out of range. We either have to round them to valid bounds or
340-
// (future version, provide negative-arg slicing like i.e. Python).
341-
if (index < 0) index = 0;
342-
if (index >= _count) index = Math.Max(0, _count - 1);
343-
if (count > _count - index) count = _count - index;
344-
if (count <= 0) return Empty;
345-
346-
if (index == 0 && count == _count) return this;
338+
if(index < 0)
339+
throw new ArgumentOutOfRangeException(nameof(index), "Index must be positive number");
340+
if (count < 0)
341+
throw new ArgumentOutOfRangeException(nameof(count), "Count must be positive number");
342+
if (count == 0) return Empty;
343+
if(index > _count)
344+
throw new ArgumentOutOfRangeException(nameof(index), "Index is outside of the bounds of the ByteString");
345+
if(index + count > _count)
346+
throw new ArgumentOutOfRangeException(nameof(count), "Index + count is outside of the bounds of the ByteString");
347347

348-
int j;
349-
var i = GetBufferFittingIndex(index, out j);
348+
if (index == 0 && count == _count) return this;
349+
350+
var i = GetBufferFittingIndex(index, out var j);
350351
var init = _buffers[i];
351352

352353
var copied = Math.Min(init.Count - j, count);
@@ -503,7 +504,7 @@ public byte[] ToArray()
503504
return Array.Empty<byte>();
504505

505506
var copy = new byte[_count];
506-
this.CopyTo(copy, 0, _count);
507+
CopyTo(copy, 0, _count);
507508
return copy;
508509
}
509510

0 commit comments

Comments
 (0)