mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7177 from ethereum/abiv2-test-decoding-of-invalid-encode
Test low level calls with correct and incorrect encodings
This commit is contained in:
commit
efa2648771
@ -102,6 +102,8 @@ message VarDecl {
|
||||
|
||||
message TestFunction {
|
||||
required VarDecl local_vars = 1;
|
||||
// Length of invalid encoding
|
||||
required uint64 invalid_encoding_length = 2;
|
||||
}
|
||||
|
||||
message Contract {
|
||||
|
@ -35,10 +35,10 @@ namespace
|
||||
{
|
||||
/// Test function returns a uint256 value
|
||||
static size_t const expectedOutputLength = 32;
|
||||
/// Expected output value is decimal 1000 or hex 03E8
|
||||
/// Expected output value is decimal 0
|
||||
static uint8_t const expectedOutput[expectedOutputLength] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, u'\x03', u'\xe8'
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
/// Compares the contents of the memory address pointed to
|
||||
@ -123,11 +123,16 @@ DEFINE_PROTO_FUZZER(Contract const& _input)
|
||||
// We always call the function test() that is defined in proto converter template
|
||||
hexEncodedInput = methodIdentifiers["test()"].asString();
|
||||
}
|
||||
// Ignore compilation failures
|
||||
catch (Exception const&)
|
||||
// Ignore stack too deep errors during compilation
|
||||
catch (eth::StackTooDeepException const&)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Do not ignore other compilation failures
|
||||
catch (Exception const&)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_CODE"))
|
||||
{
|
||||
|
@ -690,10 +690,25 @@ void ProtoConverter::visit(TestFunction const& _x)
|
||||
uint returnVal = this.coder_public(<parameter_names>);
|
||||
if (returnVal != 0)
|
||||
return returnVal;
|
||||
return (uint(1000) + this.coder_external(<parameter_names>));
|
||||
|
||||
returnVal = this.coder_external(<parameter_names>);
|
||||
if (returnVal != 0)
|
||||
return uint(200000) + returnVal;
|
||||
|
||||
bytes memory argumentEncoding = abi.encode(<parameter_names>);
|
||||
|
||||
returnVal = checkEncodedCall(this.coder_public.selector, argumentEncoding, <invalidLengthFuzz>);
|
||||
if (returnVal != 0)
|
||||
return returnVal;
|
||||
|
||||
returnVal = checkEncodedCall(this.coder_external.selector, argumentEncoding, <invalidLengthFuzz>);
|
||||
if (returnVal != 0)
|
||||
return uint(200000) + returnVal;
|
||||
return 0;
|
||||
}
|
||||
)")
|
||||
("parameter_names", dev::suffixedVariableNameList(s_varNamePrefix, 0, m_varCounter))
|
||||
("invalidLengthFuzz", std::to_string(_x.invalid_encoding_length()))
|
||||
.render();
|
||||
}
|
||||
|
||||
@ -708,6 +723,42 @@ void ProtoConverter::writeHelperFunctions()
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Accepts function selector, correct argument encoding, and length of invalid encoding and returns
|
||||
/// the correct and incorrect abi encoding for calling the function specified by the function selector.
|
||||
function createEncoding(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
|
||||
internal pure returns (bytes memory, bytes memory)
|
||||
{
|
||||
bytes memory validEncoding = new bytes(4 + argumentEncoding.length);
|
||||
// Ensure that invalidEncoding crops at least one and at most all bytes from correct encoding.
|
||||
uint invalidLength = invalidLengthFuzz % argumentEncoding.length;
|
||||
bytes memory invalidEncoding = new bytes(4 + invalidLength);
|
||||
for (uint i = 0; i < 4; i++)
|
||||
validEncoding[i] = invalidEncoding[i] = funcSelector[i];
|
||||
for (uint i = 0; i < argumentEncoding.length; i++)
|
||||
validEncoding[i+4] = argumentEncoding[i];
|
||||
for (uint i = 0; i < invalidLength; i++)
|
||||
invalidEncoding[i+4] = argumentEncoding[i];
|
||||
return (validEncoding, invalidEncoding);
|
||||
}
|
||||
|
||||
/// Accepts function selector, correct argument encoding, and an invalid encoding length as input.
|
||||
/// Returns a non-zero value if either call with correct encoding fails or call with incorrect encoding
|
||||
/// succeeds. Returns zero if both calls meet expectation.
|
||||
function checkEncodedCall(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz) public returns (uint)
|
||||
{
|
||||
(bytes memory validEncoding, bytes memory invalidEncoding) = createEncoding(funcSelector, argumentEncoding, invalidLengthFuzz);
|
||||
(bool success, bytes memory returnVal) = address(this).call(validEncoding);
|
||||
uint returnCode = abi.decode(returnVal, (uint));
|
||||
// Return non-zero value if call fails for correct encoding
|
||||
if (success == false || returnCode != 0)
|
||||
return 400000;
|
||||
(success, ) = address(this).call(invalidEncoding);
|
||||
// Return non-zero value if call succeeds for incorrect encoding
|
||||
if (success == true)
|
||||
return 400001;
|
||||
return 0;
|
||||
}
|
||||
)";
|
||||
|
||||
// These are callee functions that encode from storage, decode to
|
||||
|
@ -26,11 +26,42 @@
|
||||
* if (returnVal != 0)
|
||||
* return returnVal;
|
||||
* // Since the return codes in the public and external coder functions are identical
|
||||
* // we offset error code by a fixed amount (1000) for differentiation.
|
||||
* return (uint(1000) + this.coder_external(x_0, x_1));
|
||||
* // we offset error code by a fixed amount (200000) for differentiation.
|
||||
* returnVal = this.coder_external(x_0, x_1);
|
||||
* if (returnVal != 0)
|
||||
* return 200000 + returnVal;
|
||||
* // Encode parameters
|
||||
* bytes memory argumentEncoding = abi.encode(<parameter_names>);
|
||||
* returnVal = checkEncodedCall(this.coder_public.selector, argumentEncoding, <invalidLengthFuzz>);
|
||||
* // Check if calls to coder_public meet expectations for correctly/incorrectly encoded data.
|
||||
* if (returnVal != 0)
|
||||
* return returnVal;
|
||||
*
|
||||
* returnVal = checkEncodedCall(this.coder_external.selector, argumentEncoding, <invalidLengthFuzz>);
|
||||
* // Check if calls to coder_external meet expectations for correctly/incorrectly encoded data.
|
||||
* // Offset return value to distinguish between failures originating from coder_public and coder_external.
|
||||
* if (returnVal != 0)
|
||||
* return uint(200000) + returnVal;
|
||||
* // Return zero if all checks pass.
|
||||
* return 0;
|
||||
* }
|
||||
*
|
||||
* // Utility functions
|
||||
* /// Accepts function selector, correct argument encoding, and an invalid encoding length as input.
|
||||
* /// Returns a non-zero value if either call with correct encoding fails or call with incorrect encoding
|
||||
* /// succeeds. Returns zero if both calls meet expectation.
|
||||
* function checkEncodedCall(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
|
||||
* public returns (uint) {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* /// Accepts function selector, correct argument encoding, and length of invalid encoding and returns
|
||||
* /// the correct and incorrect abi encoding for calling the function specified by the function selector.
|
||||
* function createEncoding(bytes4 funcSelector, bytes memory argumentEncoding, uint invalidLengthFuzz)
|
||||
* internal pure returns (bytes memory, bytes memory) {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* /// Compares two dynamically sized bytes arrays for equality.
|
||||
* function bytesCompare(bytes memory a, bytes memory b) internal pure returns (bool) {
|
||||
* ...
|
||||
* }
|
||||
@ -48,7 +79,7 @@
|
||||
* // External function that is called by test() function. Accepts one or more arguments and returns
|
||||
* // a uint value (zero if abi en/decoding was successful, non-zero otherwise)
|
||||
* function coder_external(string calldata c_0, bytes calldata c_1) external pure returns (uint) {
|
||||
* if (!stringCompare(c_0, "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
|
||||
* if (!bytesCompare(bytes(c_0), "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"))
|
||||
* return 1;
|
||||
* if (!bytesCompare(c_1, "1"))
|
||||
* return 2;
|
||||
@ -375,7 +406,7 @@ private:
|
||||
/// Monotonically increasing return value for error reporting
|
||||
unsigned m_returnValue;
|
||||
static unsigned constexpr s_maxArrayLength = 4;
|
||||
static unsigned constexpr s_maxArrayDimensions = 10;
|
||||
static unsigned constexpr s_maxArrayDimensions = 4;
|
||||
/// Prefixes for declared and parameterized variable names
|
||||
static auto constexpr s_varNamePrefix = "x_";
|
||||
static auto constexpr s_paramNamePrefix = "c_";
|
||||
|
Loading…
Reference in New Issue
Block a user