Create bytes/string values of shorter than 32 bytes and adjust invalid encoding length accordingly

This commit is contained in:
Bhargava Shastry 2019-08-06 14:21:10 +02:00
parent 3c963eb00e
commit 3963d0ca02
2 changed files with 61 additions and 18 deletions

View File

@ -204,12 +204,20 @@ std::string ProtoConverter::addressValueAsString(unsigned _counter)
.render();
}
std::string ProtoConverter::fixedByteValueAsString(unsigned _width, unsigned _counter)
/// Returns a hex literal if _isHexLiteral is true, a string literal otherwise.
std::string ProtoConverter::hexValueAsString(
unsigned _width,
unsigned _counter,
bool _isHexLiteral
)
{
solAssert(
(_width >= 1 && _width <= 32),
"Proto ABIv2 Fuzzer: Fixed byte width is not between 1--32"
);
// If _width is zero (possible via a call from ...), then simply return an
// empty (hex) string
if (_width == 0)
return Whiskers(R"(<?addHexPrefix>hex</addHexPrefix>"")")
("addHexPrefix", _isHexLiteral)
.render();
// Masked value must contain twice the number of nibble "f"'s as _width
unsigned numMaskNibbles = _width * 2;
// Start position of substring equals totalHexStringLength - numMaskNibbles
@ -225,11 +233,21 @@ std::string ProtoConverter::fixedByteValueAsString(unsigned _width, unsigned _co
// and replaces "0x" with "hex\"...\"" string.
// This is needed because solidity interprets a 20-byte 0x prefixed hex literal as an address
// payable type.
return Whiskers(R"(hex"<value>")")
return Whiskers(R"(<?addHexPrefix>hex</addHexPrefix>"<value>")")
("addHexPrefix", _isHexLiteral)
("value", maskUnsignedIntToHex(_counter, numMaskNibbles).substr(startPos, numMaskNibbles))
.render();
}
std::string ProtoConverter::fixedByteValueAsString(unsigned _width, unsigned _counter)
{
solAssert(
(_width >= 1 && _width <= 32),
"Proto ABIv2 Fuzzer: Fixed byte width is not between 1--32"
);
return hexValueAsString(_width, _counter, /*isHexLiteral=*/true);
}
std::string ProtoConverter::integerValueAsString(bool _sign, unsigned _width, unsigned _counter)
{
if (_sign)
@ -314,10 +332,14 @@ void ProtoConverter::visit(ValueType const& _x)
void ProtoConverter::visit(DynamicByteArrayType const& _x)
{
bool isBytes = _x.type() == DynamicByteArrayType::BYTES;
visitType(
(_x.type() == DynamicByteArrayType::BYTES) ? DataType::BYTES : DataType::STRING,
isBytes ? DataType::BYTES : DataType::STRING,
bytesArrayTypeAsString(_x),
bytesArrayValueAsString(getNextCounter())
bytesArrayValueAsString(
getNextCounter(),
isBytes
)
);
}
@ -367,7 +389,10 @@ std::string ProtoConverter::getValueByBaseType(ArrayType const& _x)
case ArrayType::kBoolty:
return boolValueAsString(getNextCounter());
case ArrayType::kDynbytesty:
return bytesArrayValueAsString(getNextCounter());
return bytesArrayValueAsString(
getNextCounter(),
_x.dynbytesty().type() == DynamicByteArrayType::BYTES
);
// TODO: Implement structs.
case ArrayType::kStty:
case ArrayType::BASE_TYPE_ONEOF_NOT_SET:
@ -695,6 +720,7 @@ void ProtoConverter::visit(TestFunction const& _x)
if (returnVal != 0)
return uint(200000) + returnVal;
<?atLeastOneVar>
bytes memory argumentEncoding = abi.encode(<parameter_names>);
returnVal = checkEncodedCall(this.coder_public.selector, argumentEncoding, <invalidLengthFuzz>);
@ -704,11 +730,13 @@ void ProtoConverter::visit(TestFunction const& _x)
returnVal = checkEncodedCall(this.coder_external.selector, argumentEncoding, <invalidLengthFuzz>);
if (returnVal != 0)
return uint(200000) + returnVal;
</atLeastOneVar>
return 0;
}
)")
("parameter_names", dev::suffixedVariableNameList(s_varNamePrefix, 0, m_varCounter))
("invalidLengthFuzz", std::to_string(_x.invalid_encoding_length()))
("atLeastOneVar", m_varCounter > 0)
.render();
}
@ -730,8 +758,12 @@ void ProtoConverter::writeHelperFunctions()
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;
// Ensure that invalidEncoding crops at least 32 bytes (padding length
// is at most 31 bytes) since shorter bytes/string values can lead to
// successful decoding when fewer than 32 bytes have been cropped in
// the worst case. In other words,
// 0 <= invalidLength <= argumentEncoding.length - 32
uint invalidLength = invalidLengthFuzz % (argumentEncoding.length - 31);
bytes memory invalidEncoding = new bytes(4 + invalidLength);
for (uint i = 0; i < 4; i++)
validEncoding[i] = invalidEncoding[i] = funcSelector[i];

View File

@ -268,13 +268,6 @@ private:
);
}
// String and bytes literals are derived by hashing a monotonically increasing
// counter and enclosing the said hash inside double quotes.
std::string bytesArrayValueAsString(unsigned _counter)
{
return "\"" + toHex(hashUnsignedInt(_counter), HexPrefix::DontAdd) + "\"";
}
std::string getQualifier(DataType _dataType)
{
return ((isValueType(_dataType) || m_isStateVar) ? "" : "memory");
@ -288,6 +281,11 @@ private:
static std::string integerValueAsString(bool _sign, unsigned _width, unsigned _counter);
static std::string addressValueAsString(unsigned _counter);
static std::string fixedByteValueAsString(unsigned _width, unsigned _counter);
static std::string hexValueAsString(
unsigned _width,
unsigned _counter,
bool _isHexLiteral
);
static std::vector<std::pair<bool, unsigned>> arrayDimensionsAsPairVector(ArrayType const& _x);
static std::string arrayDimInfoAsString(ArrayDimensionInfo const& _x);
static void arrayDimensionsAsStringVector(
@ -389,6 +387,19 @@ private:
);
}
// String and bytes literals are derived by hashing a monotonically increasing
// counter and enclosing the (potentially cropped) hash inside double quotes.
// Cropping is achieved by masking out higher order bits.
// TODO: Test invalid encoding of bytes/string arguments that hold values of over 32 bytes.
// See https://github.com/ethereum/solidity/issues/7180
static std::string bytesArrayValueAsString(unsigned _counter, bool _isHexLiteral)
{
// We use _counter to not only create a value but to crop it
// to a length (l) such that 0 <= l <= 32 (hence the use of 33 as
// the modulo constant)
return hexValueAsString(_counter % 33, _counter, _isHexLiteral);
}
/// Contains the test program
std::ostringstream m_output;
/// Temporary storage for state variable definitions