Skip to content

Commit

Permalink
fix memory guard and slice (#6396)
Browse files Browse the repository at this point in the history
* fix memory guard and slice

* Trim or pad variable length bytes (as Geth does)

* fix ArgumentException

* whitespace
  • Loading branch information
LukaszRozmej committed Dec 28, 2023
1 parent ada8cc9 commit 2402d5a
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 31 deletions.
21 changes: 21 additions & 0 deletions src/Nethermind/Nethermind.Core.Test/AddressTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System.Collections;
using FluentAssertions;
using Nethermind.Blockchain;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
Expand Down Expand Up @@ -183,6 +184,26 @@ public void Of_contract(long nonce, string expectedAddress)
public bool Is_PointEvaluationPrecompile_properly_activated(IReleaseSpec spec) =>
Address.FromNumber(0x0a).IsPrecompile(spec);

[TestCase(Address.SystemUserHex, false)]
[TestCase("2" + Address.SystemUserHex, false)]
[TestCase("2" + Address.SystemUserHex, true)]
public void Parse_variable_length(string addressHex, bool allowOverflow)
{
var result = Address.TryParseVariableLength(addressHex, out Address? address, allowOverflow);
result.Should().Be(addressHex.Length <= Address.SystemUserHex.Length || allowOverflow);
if (result)
{
address.Should().Be(Address.SystemUser);
}
}

[Test]
public void Parse_variable_length_too_short()
{
Address.TryParseVariableLength("1", out Address? address).Should().Be(true);
address.Should().Be(new Address("0000000000000000000000000000000000000001"));
}

public static IEnumerable PointEvaluationPrecompileTestCases
{
get
Expand Down
26 changes: 20 additions & 6 deletions src/Nethermind/Nethermind.Core/Address.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public class Address : IEquatable<Address>, IComparable<Address>
private const int PrefixedHexCharsCount = 2 + HexCharsCount; // 0x5a4eab120fb44eb6684e5e32785702ff45ea344d

public static Address Zero { get; } = new(new byte[Size]);
public static Address SystemUser { get; } = new("0xfffffffffffffffffffffffffffffffffffffffe");
public const string SystemUserHex = "0xfffffffffffffffffffffffffffffffffffffffe";
public static Address SystemUser { get; } = new(SystemUserHex);

public byte[] Bytes { get; }

Expand Down Expand Up @@ -91,18 +92,31 @@ public static bool TryParse(string? value, out Address? address)
/// <summary>
/// Parses string value to Address. String can be shorter than 20 bytes long, it is padded with leading 0's then.
/// </summary>
public static bool TryParseVariableLength(string? value, out Address? address)
public static bool TryParseVariableLength(string? value, out Address? address, bool allowOverflow = false)
{
if (value is not null)
{
try
const int size = Size << 1;

int start = value is ['0', 'x', ..] ? 2 : 0;
ReadOnlySpan<char> span = value.AsSpan(start);
if (span.Length > size)
{
address = new Address(Extensions.Bytes.FromHexString(value, Size));
return true;
if (allowOverflow)
{
span = span.Slice(value.Length - size);
}
else
{
goto False;
}
}
catch (IndexOutOfRangeException) { }

address = new Address(Extensions.Bytes.FromHexString(span, Size));
return true;
}

False:
address = default;
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Core/Extensions/Bytes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1018,7 +1018,7 @@ private static void ThrowFormatException_IncorrectHexString()
hexString is null ? throw new ArgumentNullException(nameof(hexString)) : FromHexString(hexString.AsSpan(), length);

[DebuggerStepThrough]
private static byte[] FromHexString(ReadOnlySpan<char> hexString, int length)
public static byte[] FromHexString(ReadOnlySpan<char> hexString, int length)
{
int start = hexString is ['0', 'x', ..] ? 2 : 0;
ReadOnlySpan<char> chars = hexString.Slice(start);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,11 @@ public Engine(IReleaseSpec spec)
private ITypedArray<byte> Slice(object input, long start, long end)
{
ArgumentNullException.ThrowIfNull(input);
var bytes = input.ToBytes();

if (start < 0 || end < start || end > Array.MaxLength)
{
throw new ArgumentOutOfRangeException(nameof(start), $"tracer accessed out of bound memory: offset {start}, end {end}");
}

return input.ToBytes().Slice((int)start, (int)(end - start)).ToTypedScriptArray();
return start < 0 || end < start || end > bytes.Length
? throw new ArgumentOutOfRangeException(nameof(start), $"tracer accessed out of bound memory: available {bytes.Length}, offset {start}, size {end - start}")
: bytes.Slice((int)start, (int)(end - start)).ToTypedScriptArray();
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static class JavaScriptConverter

public static Address ToAddress(this object address) => address switch
{
string hexString => Address.TryParseVariableLength(hexString, out Address parsedAddress)
string hexString => Address.TryParseVariableLength(hexString, out Address parsedAddress, true)
? parsedAddress
: throw new ArgumentException("Not correct address", nameof(address)),
_ => new Address(address.ToBytes())
Expand Down
31 changes: 14 additions & 17 deletions src/Nethermind/Nethermind.Evm/Tracing/TraceMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public TraceMemory(ulong size, ReadOnlyMemory<byte> memory)

public string[] ToHexWordList()
{
string[] memory = new string[((int)Size / EvmPooledMemory.WordSize) + ((Size % EvmPooledMemory.WordSize == 0) ? 0 : 1)];
string[] memory = new string[(int)Size / EvmPooledMemory.WordSize + (Size % EvmPooledMemory.WordSize == 0 ? 0 : 1)];
int traceLocation = 0;

int i = 0;
Expand All @@ -45,21 +45,23 @@ public string[] ToHexWordList()
return memory;
}

private const int MemoryPadLimit = 1024 * 1024;
public ReadOnlySpan<byte> Slice(int start, int length)
{
if ((ulong)start + (ulong)length > Size)
{
throw new IndexOutOfRangeException("Requested memory range is out of bounds.");
}
ArgumentOutOfRangeException.ThrowIfNegative(start, nameof(start));
ArgumentOutOfRangeException.ThrowIfNegative(length, nameof(length));

ReadOnlySpan<byte> span = _memory.Span;

if (start + length > _memory.Length)
if (start + length > span.Length)
{
int paddingNeeded = start + length - span.Length;
if (paddingNeeded > MemoryPadLimit) throw new InvalidOperationException($"reached limit for padding memory slice: {paddingNeeded}");
byte[] result = new byte[length];
for (int i = 0, index = start; index < _memory.Length; i++, index++)
int overlap = span.Length - start;
if (overlap > 0)
{
result[i] = span[index];
span.Slice(start, overlap).CopyTo(result.AsSpan(0, overlap));
}

return result;
Expand All @@ -68,13 +70,8 @@ public ReadOnlySpan<byte> Slice(int start, int length)
return span.Slice(start, length);
}

public BigInteger GetUint(int offset)
{
if (offset < 0 || (ulong)(offset + EvmPooledMemory.WordSize) > Size)
{
throw new ArgumentOutOfRangeException(nameof(offset), $"tracer accessed out of bound memory: available {Size}, offset {offset}, size {EvmPooledMemory.WordSize}");
}

return new BigInteger(Slice(offset, EvmPooledMemory.WordSize), true, true);
}
public BigInteger GetUint(int offset) =>
offset < 0 || (ulong)(offset + EvmPooledMemory.WordSize) > Size
? throw new ArgumentOutOfRangeException(nameof(offset), $"tracer accessed out of bound memory: available {Size}, offset {offset}, size {EvmPooledMemory.WordSize}")
: new BigInteger(Slice(offset, EvmPooledMemory.WordSize), true, true);
}

0 comments on commit 2402d5a

Please sign in to comment.