diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index 95a78f2d4..5babc5198 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -32,7 +32,7 @@ if (OSSFUZZ) SolidityEvmoneInterface.cpp ../../libsolidity/util/ContractABIUtils.cpp ) - target_link_libraries(solc_ossfuzz PRIVATE libsolc evmasm evmc evmone-standalone) + target_link_libraries(solc_ossfuzz PRIVATE libsolc evmasm evmc evmone-standalone abicoder gmp.a) set_target_properties(solc_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) add_executable(solc_mutator_ossfuzz diff --git a/test/tools/ossfuzz/ValueGenerator.cpp b/test/tools/ossfuzz/ValueGenerator.cpp new file mode 100644 index 000000000..4c7229d49 --- /dev/null +++ b/test/tools/ossfuzz/ValueGenerator.cpp @@ -0,0 +1,326 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +#include +#include + +using namespace std; + +void ValueGenerator::initialiseType(TypeInfo& _t) +{ + switch (_t.type) + { + case Type::Boolean: + _t.value += "true"; + break; + case Type::Integer: + _t.value += "12"; + break; + case Type::UInteger: + _t.value += "23"; + break; + case Type::String: + _t.value += "0xdeadbeef"; + break; + case Type::Bytes: + _t.value += "0xc0de"; + break; + case Type::FixedBytes: + _t.value += "0x" + std::string(static_cast(_t.fixedByteWidth) * 2, 'a'); + break; + case Type::Address: + _t.value += "0x" + std::string(static_cast(FixedBytesWidth::Bytes20) * 2, 'a'); + break; + case Type::Function: + _t.value += "0x" + std::string(static_cast(FixedBytesWidth::Bytes24) * 2, 'a'); + break; + default: + solAssert(false, "Value Generator: Invalid value type."); + } +} + +void ValueGenerator::initialiseTuple(TypeInfo& _tuple) +{ + _tuple.value += "("; + std::string separator; + for (auto& c: _tuple.tupleInfo) + { + _tuple.value += separator + c.value; +// if (c.arrayInfo.empty()) +// { +// if (c.type == Type::Tuple) +// initialiseTuple(c); +// else +// initialiseType(c); +// } +// else +// { +// initialiseArray(c.arrayInfo, c); +// cout << c.arrayInfo.size() << endl; +// cout << c.arrayInfo.back().numElements << endl; +// cout << c.value << endl; +// } + if (separator.empty()) + separator = ","; + } + _tuple.value += ")"; +} + +void ValueGenerator::initialiseArray( + ArrayInfo& _arrayInfo, + TypeInfo& _typeInfo +) +{ + cout << "Init 1D array" << endl; + cout << _typeInfo.value << endl; + _typeInfo.value += "["; + std::string separator; + for (size_t j = 0; j < _arrayInfo.numElements; j++) + { + _typeInfo.value += separator; + if (_typeInfo.type == Type::Tuple) { + cout << "Tuple inside array" << endl; + initialiseTuple(_typeInfo); + } + else + initialiseType(_typeInfo); + if (separator.empty()) + separator = ","; + } + _typeInfo.value += "]"; + cout << _typeInfo.value << endl; +} + +void ValueGenerator::initialiseArray( + vector& _arrayInfo, + TypeInfo& _typeInfo +) +{ + if (_arrayInfo.size() == 1) + initialiseArray(_arrayInfo[0], _typeInfo); + else + { + vector copy = _arrayInfo; + auto k = copy.back(); + copy.pop_back(); + _typeInfo.value += "["; + std::string separator; + for (size_t i = 0; i < k.numElements; i++) + { + _typeInfo.value += separator; + initialiseArray(copy, _typeInfo); + if (separator.empty()) + separator = ","; + } + _typeInfo.value += "]"; + } +} + +void ValueGenerator::initialiseArrayOfTuple( + vector&, + TypeInfo& +) +{ + +} + +void ValueGenerator::typeHelper(Json::Value const& _type, TypeInfo& _typeInfo) +{ + std::string jsonTypeString = _type["type"].asString(); + /* + * Index | Match description + * 0 | Entire type string e.g., bool[1][2][] + * 1 | Type string e.g., bool, uint256, address etc. + * 2 | Base type e.g., uint, int, bytes + * 3 | Type width e.g., 256, 64, 1...32 + * 4 | First array bracket e.g., [1] in uint256[1][2][3] + * 5 | First array dimension e.g., 1 in uint256[1][2][3] + * 6 | Second array bracket e.g., [2] in uint256[1][2][3] + * 7 | Second array dimension e.g., 2 in uint256[1][2][3] + * 8 | Third array bracket e.g., [3] in uint256[1][2][3] + * 9 | Third array dimension e.g., 3 in uint256[1][2][3] + */ + regex r = regex( + "(bool|(uint|int|bytes)(\\d+)|address|bytes|string|function|tuple)" + "(\\[(\\d+)?\\])?(\\[(\\d+)?\\])?(\\[(\\d+)?\\])?" + ); + smatch matches; + auto match = regex_search(jsonTypeString, matches, r); + solAssert(match, "Value generator: Regex match failed."); + solAssert( + !matches[1].str().empty(), + "Value generator: Invalid type" + ); + auto typeString = matches[1].str(); + size_t width = 0; + if (matches[3].matched) + width = stoul(matches[3].str()); + + if (typeString.find("bool") != string::npos) + { + _typeInfo.type = Type::Boolean; + _typeInfo.name = "bool"; + } + else if (typeString.find("function") != string::npos) + { + _typeInfo.type = Type::Function; + _typeInfo.name = "function"; + } + else if (typeString.find("address") != string::npos) + { + _typeInfo.type = Type::Address; + _typeInfo.name = "address"; + } + else if (typeString.find("tuple") != string::npos) + { + _typeInfo.type = Type::Tuple; + tuple(_type["components"], _typeInfo); + } + else if (typeString.find("bytes") != string::npos) + { + if (matches[3].matched) + { + solAssert( + width >= 1 && width <= 32, + "Value generator: Invalid fixed bytes type." + ); + _typeInfo.type = Type::FixedBytes; + _typeInfo.fixedByteWidth = static_cast(width); + _typeInfo.name = "bytes" + matches[3].str(); + } + else + { + solAssert(width == 0, "Value generator: Invalid width."); + _typeInfo.type = Type::Bytes; + _typeInfo.name = "bytes"; + } + } + else if (typeString.find("string") != string::npos) + { + _typeInfo.type = Type::String; + _typeInfo.name = "string"; + } + else + { + std::string baseType = matches[2].str(); + solAssert( + baseType == "int" || baseType == "uint", + "Value generator: Invalid integer type." + ); + solAssert( + width >=8 && width <= 256 && (width % 8 == 0), + "Value generator: Invalid integer width." + ); + if (baseType == "int") + { + _typeInfo.type = Type::Integer; + _typeInfo.intType = {true, static_cast(width)}; + _typeInfo.name = "int" + matches[3].str(); + } + else + { + _typeInfo.type = Type::UInteger; + _typeInfo.intType = {false, static_cast(width)}; + _typeInfo.name = "uint" + matches[3].str(); + } + } + + for (unsigned i = 4; i < 10; i += 2) + { + if (matches[i].matched) + { + size_t arraySize; + if (matches[i + 1].matched) + { + std::string arraySizeString = matches[i + 1].str(); + arraySize = stoul(arraySizeString); + _typeInfo.arrayInfo.push_back({true, arraySize}); + _typeInfo.name += "[" + arraySizeString + "]"; + } + else + { + // TODO: Assign pseudo randomly chosen dynamic size. + arraySize = 2; + _typeInfo.arrayInfo.push_back({false, arraySize}); + _typeInfo.name += "[]"; + } + } + } + if (_typeInfo.arrayInfo.empty()) + { + if (_typeInfo.type == Type::Tuple) { + cout << "Init tuple" << endl; + initialiseTuple(_typeInfo); + } + else + initialiseType(_typeInfo); + } + else + { + cout << "Init array" << endl; + initialiseArray(_typeInfo.arrayInfo, _typeInfo); + } +} + +void ValueGenerator::tuple(Json::Value const& _type, TypeInfo& _typeInfo) +{ + _typeInfo.name += "("; + for (auto component = _type.begin(); component != _type.end();) + { + TypeInfo componentType; + typeHelper(*component, componentType); + _typeInfo.tupleInfo.emplace_back(componentType); + _typeInfo.name += _typeInfo.tupleInfo.back().name; + if (++component != _type.end()) + { + _typeInfo.name += ","; + } + } + _typeInfo.name += ")"; +} + +ValueGenerator::TypeInfo ValueGenerator::type(Json::Value const& _value) +{ + solAssert( + _value.isMember("type"), + "Value generator: Invalid function input parameter type." + ); + TypeInfo result; + typeHelper(_value, result); + return result; +} + +std::pair ValueGenerator::type() +{ + std::string typeString = "("; + std::string valueString = "("; + std::string separator = ""; + for (auto const& param: m_type) + { + auto l = type(param); + typeString += separator + l.name; + valueString += separator + l.value; + if (separator.empty()) + separator = ","; + } + return {typeString + ")", valueString + ")"}; +} diff --git a/test/tools/ossfuzz/ValueGenerator.h b/test/tools/ossfuzz/ValueGenerator.h new file mode 100644 index 000000000..fa45ccceb --- /dev/null +++ b/test/tools/ossfuzz/ValueGenerator.h @@ -0,0 +1,185 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +class ValueGenerator +{ +public: + enum class Type: size_t + { + Boolean = 0, + Integer, + UInteger, + FixedBytes, + Bytes, + String, + Address, + Function, + Tuple + }; + struct ArrayInfo + { + bool staticSize; + size_t numElements; + }; + enum class FixedBytesWidth: size_t + { + Bytes1 = 1, + Bytes2, + Bytes3, + Bytes4, + Bytes5, + Bytes6, + Bytes7, + Bytes8, + Bytes9, + Bytes10, + Bytes11, + Bytes12, + Bytes13, + Bytes14, + Bytes15, + Bytes16, + Bytes17, + Bytes18, + Bytes19, + Bytes20, + Bytes21, + Bytes22, + Bytes23, + Bytes24, + Bytes25, + Bytes26, + Bytes27, + Bytes28, + Bytes29, + Bytes30, + Bytes31, + Bytes32 + }; + enum class IntegerWidth: size_t + { + W8 = 8, + W16 = 16, + W24 = 24, + W32 = 32, + W40 = 40, + W48 = 48, + W56 = 56, + W64 = 64, + W72 = 72, + W80 = 80, + W88 = 88, + W96 = 96, + W104 = 104, + W112 = 112, + W120 = 120, + W128 = 128, + W136 = 136, + W144 = 144, + W152 = 152, + W160 = 160, + W168 = 168, + W176 = 176, + W184 = 184, + W192 = 192, + W200 = 200, + W208 = 208, + W216 = 216, + W224 = 224, + W232 = 232, + W240 = 240, + W248 = 248, + W256 = 256 + }; + struct IntegerType + { + bool sign; + IntegerWidth width; + }; + struct TypeInfo + { + Type type; + FixedBytesWidth fixedByteWidth; + IntegerType intType; + std::vector arrayInfo; + std::vector tupleInfo; + std::string name; + std::string value; + }; + explicit ValueGenerator(Json::Value const& _type, unsigned _seed): + m_rand(_seed), + m_type(_type), + m_bernoulli(0.5) + {} + void boolean() + { + m_stream << (m_bernoulli(m_rand) ? "true" : "false"); + } + void string() + { + m_stream << "hello"; + } + void bytes() + { + m_stream << "0x1234"; + } + void function(); + void fixedbytes() + { + + } + void address(); + void integer() + { + + } + 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); + std::pair type(); + static void initialiseTuple(TypeInfo& _tuple); + static void initialiseType(TypeInfo& _t); + static void initialiseArray( + ArrayInfo& _arrayInfo, + TypeInfo& _typeInfo + ); + static void initialiseArrayOfTuple( + std::vector& _arrayInfo, + TypeInfo& _typeInfo + ); + static void initialiseArray( + std::vector& _arrayInfo, + TypeInfo& _typeInfo + ); +private: + std::ostringstream m_stream; + std::minstd_rand m_rand; + Json::Value const& m_type; + std::bernoulli_distribution m_bernoulli; +}; diff --git a/test/tools/ossfuzz/solc_ossfuzz.cpp b/test/tools/ossfuzz/solc_ossfuzz.cpp index 21b2440d7..6c12d0a0b 100644 --- a/test/tools/ossfuzz/solc_ossfuzz.cpp +++ b/test/tools/ossfuzz/solc_ossfuzz.cpp @@ -32,6 +32,8 @@ #include +#include + #include using namespace solidity::frontend::test; @@ -45,10 +47,11 @@ using namespace std; extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size); static evmc::VM evmone = evmc::VM{evmc_create_evmone()}; +static constexpr size_t abiCoderHeapSize = 1024 * 512; extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) { - if (_size <= 600) +// if (_size <= 600) { string input(reinterpret_cast(_data), _size); // TODO: Cannot fuzz tests containing libraries yet. @@ -87,27 +90,33 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) if (!compilerOutput.has_value()) return 0; - optional noInputFunction = evmoneUtil.noInputFunction(); - if (auto r = evmoneUtil.randomFunction(); r.has_value()) + auto r = evmoneUtil.randomFunction(); + if (!r.has_value()) + return 0; + + auto x = ValueGenerator{r.value()["inputs"], 0}.type(); + bool encodeStatus; + string encodedData; + bool functionWithInputs = x.first != "()"; + if (functionWithInputs) { - cout << ContractABIUtils{}.functionSignatureFromABI(r.value()) << endl; - for (auto const& i: r.value()["inputs"]) - if (auto v = ValueGenerator{i["type"].asString(), 0}.type(); v.has_value()) - cout << v.value().name << endl; - return 0; + abicoder::ABICoder coder(abiCoderHeapSize); + auto s = coder.encode(x.first, x.second); + encodeStatus = s.first; + encodedData = s.second; + solAssert(encodeStatus, "Isabelle coder: failed."); } - if (noInputFunction.has_value()) - evmoneUtil.methodName(noInputFunction.value()); - else - return 0; auto deployResult = evmoneUtil.deployContract(compilerOutput->byteCode); if (deployResult.status_code != EVMC_SUCCESS) return 0; - auto methodSig = solidity::util::fromHex(compilerOutput->methodIdentifiersInContract[noInputFunction.value()].asString()); + auto sig = r.value()["name"].asString() + x.first; + auto methodSig = compilerOutput->methodIdentifiersInContract[sig].asString(); + if (functionWithInputs) + methodSig += encodedData.substr(2, encodedData.size()); auto callResult = evmoneUtil.executeContract( - methodSig, + solidity::util::fromHex(methodSig), deployResult.create_address ); @@ -137,7 +146,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) solAssert(deployResultOpt.status_code == EVMC_SUCCESS, "Contract compiled via new code gen could not be deployed."); auto callResultOpt = evmoneUtil.executeContract( - methodSig, + solidity::util::fromHex(methodSig), deployResultOpt.create_address ); solAssert(callResultOpt.status_code == EVMC_SUCCESS, "New code gen contract call failed."); @@ -181,5 +190,4 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) return 0; } } - return 0; }