Add random value generation.

This commit is contained in:
Bhargava Shastry 2021-03-22 14:13:03 +01:00
parent 08f4a98ea6
commit 98a452d0aa
3 changed files with 150 additions and 27 deletions

View File

@ -20,38 +20,167 @@
#include <liblangutil/Exceptions.h>
#include <libsolutil/Keccak256.h>
#include <boost/preprocessor.hpp>
#include <regex>
#include <iostream>
using namespace std;
/// Convenience macros
/// Returns a valid Solidity integer width w such that 8 <= w <= 256.
#define INTWIDTH(z, n, _ununsed) BOOST_PP_MUL(BOOST_PP_ADD(n, 1), 8)
/// Using declaration that aliases long boost multiprecision types with
/// s(u)<width> where <width> is a valid Solidity integer width and "s"
/// stands for "signed" and "u" for "unsigned".
#define USINGDECL(z, n, sign) \
using BOOST_PP_CAT(BOOST_PP_IF(sign, s, u), INTWIDTH(z, n,)) = \
boost::multiprecision::number< \
boost::multiprecision::cpp_int_backend< \
INTWIDTH(z, n,), \
INTWIDTH(z, n,), \
BOOST_PP_IF( \
sign, \
boost::multiprecision::signed_magnitude, \
boost::multiprecision::unsigned_magnitude \
), \
boost::multiprecision::unchecked, \
void \
> \
>;
/// Instantiate the using declarations for signed and unsigned integer types.
BOOST_PP_REPEAT(32, USINGDECL, 1)
BOOST_PP_REPEAT(32, USINGDECL, 0)
/// Case implementation that returns an integer value of the specified type.
/// For signed integers, we divide by two because the range for boost multiprecision
/// types is double that of Solidity integer types. Example, 8-bit signed boost
/// number range is [-255, 255] but Solidity `int8` range is [-128, 127]
#define CASEIMPL(z, n, sign) \
case INTWIDTH(z, n,): \
stream << BOOST_PP_IF( \
sign, \
integerValue< \
BOOST_PP_CAT( \
BOOST_PP_IF(sign, s, u), \
INTWIDTH(z, n,) \
)>(_counter) / 2, \
integerValue< \
BOOST_PP_CAT( \
BOOST_PP_IF(sign, s, u), \
INTWIDTH(z, n,) \
)>(_counter) \
); \
break;
/// Switch implementation that instantiates case statements for (un)signed
/// Solidity integer types.
#define SWITCHIMPL(sign) \
ostringstream stream; \
switch (_intWidth) \
{ \
BOOST_PP_REPEAT(32, CASEIMPL, sign) \
} \
return stream.str();
namespace
{
template<typename V>
V integerValue(size_t _counter)
{
V value = V(
u256(solidity::util::keccak256(solidity::util::h256(_counter))) %
u256(boost::math::tools::max_value<V>())
);
if (boost::multiprecision::is_signed_number<V>::value && value % 2 == 0)
return value * (-1);
else
return value;
}
string signedIntegerValue(size_t _counter, size_t _intWidth)
{
SWITCHIMPL(1)
}
string unsignedIntegerValue(size_t _counter, size_t _intWidth)
{
SWITCHIMPL(0)
}
string integerValue(size_t _counter, size_t _intWidth, bool _signed)
{
if (_signed)
return signedIntegerValue(_counter, _intWidth);
else
return unsignedIntegerValue(_counter, _intWidth);
}
string fixedBytes(
size_t _numBytes,
size_t _counter,
bool _isHexLiteral
)
{
solAssert(
_numBytes > 0 && _numBytes <= 32,
"Proto ABIv2 fuzzer: Too short or too long a cropped string"
);
// Number of masked nibbles is twice the number of bytes for a
// hex literal of _numBytes bytes. For a string literal, each nibble
// is treated as a character.
size_t numMaskNibbles = _isHexLiteral ? _numBytes * 2 : _numBytes;
// Start position of substring equals totalHexStringLength - numMaskNibbles
// totalHexStringLength = 64 + 2 = 66
// e.g., 0x12345678901234567890123456789012 is a total of 66 characters
// |---------------------^-----------|
// <--- start position---><--numMask->
// <-----------total length --------->
// Note: This assumes that maskUnsignedIntToHex() invokes toHex(..., HexPrefix::Add)
size_t startPos = 66 - numMaskNibbles;
// Extracts the least significant numMaskNibbles from the result
// of maskUnsignedIntToHex().
return solidity::util::toHex(
u256(solidity::util::keccak256(solidity::util::h256(_counter))) &
u256("0x" + std::string(numMaskNibbles, 'f')),
solidity::util::HexPrefix::Add
).substr(startPos, numMaskNibbles);
}
}
void ValueGenerator::initialiseType(TypeInfo& _t)
{
switch (_t.type)
{
case Type::Boolean:
_t.value += "true";
_t.value += m_bernoulli(m_rand) ? "true" : "false";
break;
case Type::Integer:
_t.value += "12";
_t.value += integerValue(m_rand(), static_cast<size_t>(_t.intType.width), true);
break;
case Type::UInteger:
_t.value += "23";
_t.value += integerValue(m_rand(), static_cast<size_t>(_t.intType.width), false);
break;
case Type::String:
_t.value += "0xdeadbeef";
break;
case Type::Bytes:
_t.value += "0xc0de";
{
// Bytes can contain between 1--32 bytes.
size_t bytesWidth = (m_rand() % 32) + 1;
_t.value += "0x" + fixedBytes(bytesWidth, m_rand(), true);
break;
}
case Type::FixedBytes:
_t.value += "0x" + std::string(static_cast<size_t>(_t.fixedByteWidth) * 2, 'a');
_t.value += "0x" + fixedBytes(static_cast<size_t>(_t.fixedByteWidth), m_rand(), true);
break;
case Type::Address:
_t.value += "0x" + std::string(static_cast<size_t>(FixedBytesWidth::Bytes20) * 2, 'a');
_t.value += "0x" + fixedBytes(static_cast<size_t>(FixedBytesWidth::Bytes20), m_rand(), true);
break;
case Type::Function:
_t.value += "0x" + std::string(static_cast<size_t>(FixedBytesWidth::Bytes24) * 2, 'a');
_t.value += "0x" + fixedBytes(static_cast<size_t>(FixedBytesWidth::Bytes24), m_rand(), true);
break;
default:
solAssert(false, "Value Generator: Invalid value type.");
@ -135,14 +264,6 @@ void ValueGenerator::initialiseArray(
}
}
void ValueGenerator::initialiseArrayOfTuple(
vector<ArrayInfo>&,
TypeInfo&
)
{
}
void ValueGenerator::typeHelper(Json::Value const& _type, TypeInfo& _typeInfo)
{
std::string jsonTypeString = _type["type"].asString();

View File

@ -159,21 +159,17 @@ public:
{
}
static void typeHelper(Json::Value const& _type, TypeInfo& _typeInfo);
static TypeInfo type(Json::Value const& _type);
static void tuple(Json::Value const& _tuple, TypeInfo& _typeInfo);
void typeHelper(Json::Value const& _type, TypeInfo& _typeInfo);
TypeInfo type(Json::Value const& _type);
void tuple(Json::Value const& _tuple, TypeInfo& _typeInfo);
std::pair<std::string, std::string> type();
static void initialiseTuple(TypeInfo& _tuple);
static void initialiseType(TypeInfo& _t);
static void initialiseArray(
void initialiseTuple(TypeInfo& _tuple);
void initialiseType(TypeInfo& _t);
void initialiseArray(
ArrayInfo& _arrayInfo,
TypeInfo& _typeInfo
);
static void initialiseArrayOfTuple(
std::vector<ArrayInfo>& _arrayInfo,
TypeInfo& _typeInfo
);
static void initialiseArray(
void initialiseArray(
std::vector<ArrayInfo>& _arrayInfo,
TypeInfo& _typeInfo
);

View File

@ -98,6 +98,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
bool encodeStatus;
string encodedData;
bool functionWithInputs = x.first != "()";
auto sig = r.value()["name"].asString() + x.first;
cout << sig << endl;
if (functionWithInputs)
{
abicoder::ABICoder coder(abiCoderHeapSize);
@ -111,7 +113,6 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
if (deployResult.status_code != EVMC_SUCCESS)
return 0;
auto sig = r.value()["name"].asString() + x.first;
auto methodSig = compilerOutput->methodIdentifiersInContract[sig].asString();
if (functionWithInputs)
methodSig += encodedData.substr(2, encodedData.size());
@ -121,7 +122,12 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
);
if (callResult.status_code != EVMC_SUCCESS)
{
cout << "Old code gen call failed with status code: "
<< callResult.status_code
<< endl;
return 0;
}
solidity::bytes result;
for (size_t i = 0; i < callResult.output_size; i++)