diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt
index 83543b9f0..516bf9974 100644
--- a/test/tools/ossfuzz/CMakeLists.txt
+++ b/test/tools/ossfuzz/CMakeLists.txt
@@ -10,7 +10,7 @@ add_dependencies(ossfuzz
if (OSSFUZZ)
add_custom_target(ossfuzz_proto)
- add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz)
+ add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz sol_arith_proto_ossfuzz)
add_custom_target(ossfuzz_abiv2)
add_dependencies(ossfuzz_abiv2 abiv2_proto_ossfuzz)
@@ -78,6 +78,48 @@ if (OSSFUZZ)
protobuf.a
)
set_target_properties(abiv2_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
+
+ add_executable(sol_arith_proto_ossfuzz
+ solArithProtoFuzzer.cpp
+ solarithprotoToSol.cpp
+ solArith.pb.cc
+ abiV2FuzzerCommon.cpp
+ ../../EVMHost.cpp
+ )
+ target_include_directories(sol_arith_proto_ossfuzz PRIVATE
+ /usr/include/libprotobuf-mutator
+ )
+ target_link_libraries(sol_arith_proto_ossfuzz PRIVATE solidity libsolc
+ evmc
+ evmone-standalone
+ protobuf-mutator-libfuzzer.a
+ protobuf-mutator.a
+ protobuf.a
+ )
+ set_target_properties(sol_arith_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
+
+# add_executable(sol_proto_ossfuzz
+# solProtoFuzzer.cpp
+# protoToSol.cpp
+# solProto.pb.cc
+# abiV2FuzzerCommon.cpp
+# ../../EVMHost.cpp
+# SolProtoAdaptor.cpp
+# )
+# target_include_directories(sol_proto_ossfuzz PRIVATE
+# /usr/include/libprotobuf-mutator
+# /home/bhargava/work/github/solidity/deps/LPM/external.protobuf/include
+# /home/bhargava/work/github/solidity/deps/evmone/include
+# /home/bhargava/work/github/solidity/deps/libprotobuf-mutator
+# )
+# target_link_libraries(sol_proto_ossfuzz PRIVATE solidity libsolc
+# evmc
+# evmone-standalone
+# protobuf-mutator-libfuzzer.a
+# protobuf-mutator.a
+# protobuf.a
+# )
+# set_target_properties(sol_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
else()
add_library(solc_opt_ossfuzz
solc_opt_ossfuzz.cpp
diff --git a/test/tools/ossfuzz/solArith.proto b/test/tools/ossfuzz/solArith.proto
new file mode 100644
index 000000000..a64ec982f
--- /dev/null
+++ b/test/tools/ossfuzz/solArith.proto
@@ -0,0 +1,116 @@
+/*
+ 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 .
+*/
+
+syntax = "proto2";
+
+message Type {
+ enum Sign {
+ SIGNED = 0;
+ UNSIGNED = 1;
+ }
+ required Sign s = 1;
+ required uint32 bytewidth = 2;
+}
+
+message UnaryOpStmt {
+ enum Op {
+ POSTINC = 1;
+ POSTDEC = 2;
+ PREINC = 3;
+ PREDEC = 4;
+ }
+ required Op op = 1;
+ required VarRef v = 2;
+}
+
+message BinaryOpStmt {
+ enum Op {
+ ADDSELF = 0;
+ SUBSELF = 1;
+ MULSELF = 2;
+ DIVSELF = 3;
+ MODSELF = 4;
+ SHLSELF = 5;
+ SHRSELF = 6;
+ }
+ required Op op = 1;
+ required VarRef left = 2;
+ required Expression right = 3;
+}
+
+message BinaryOp {
+ enum Op {
+ ADD = 0;
+ SUB = 1;
+ MUL = 2;
+ DIV = 3;
+ MOD = 4;
+ EXP = 10;
+ SHL = 11;
+ SHR = 12;
+ }
+ required Op op = 1;
+ required Expression left = 2;
+ required Expression right = 3;
+}
+
+message VarDecl {
+ required Type t = 1;
+ required Expression value = 2;
+}
+
+message Assignment {
+ required VarRef id = 1;
+ required Expression value = 2;
+}
+
+message VarRef {
+ required uint32 id = 1;
+}
+
+message Expression {
+ oneof expr_oneof {
+ VarRef v = 1;
+ BinaryOp bop = 2;
+ }
+}
+
+message Return {
+ required Expression e = 1;
+}
+
+message Statement {
+ oneof stmt_oneof {
+ VarDecl vd = 1;
+ Assignment a = 2;
+ UnaryOpStmt u = 3;
+ }
+}
+
+message Block {
+ required VarDecl v = 1;
+ repeated Statement s = 2;
+ required Return r = 3;
+}
+
+message Program {
+ required Block b = 1;
+ required Type.Sign s = 2;
+ required uint64 seed = 3;
+}
+
+package solidity.test.solarithfuzzer;
\ No newline at end of file
diff --git a/test/tools/ossfuzz/solArithProtoFuzzer.cpp b/test/tools/ossfuzz/solArithProtoFuzzer.cpp
new file mode 100644
index 000000000..d71cec4e9
--- /dev/null
+++ b/test/tools/ossfuzz/solArithProtoFuzzer.cpp
@@ -0,0 +1,255 @@
+/*
+ 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 .
+*/
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+static evmc::VM evmone = evmc::VM{evmc_create_evmone()};
+
+using namespace solidity::test::abiv2fuzzer;
+using namespace solidity::test::solarithfuzzer;
+using namespace solidity;
+using namespace solidity::test;
+using namespace solidity::util;
+using namespace std;
+
+namespace
+{
+/// Test function returns a uint256 value
+//static size_t const expectedOutputLength = 32;
+///// 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, 0, 0
+//};
+
+/// Compares the contents of the memory address pointed to
+/// by `_result` of `_length` bytes to the expected output.
+/// Returns true if `_result` matches expected output, false
+/// otherwise.
+//bool isOutputExpected(evmc::result const& _run)
+//{
+// if (_run.output_size != expectedOutputLength)
+// return false;
+//
+// return (memcmp(_run.output_data, expectedOutput, expectedOutputLength) == 0);
+//}
+
+bool compareRuns(evmc::result const& _run1, evmc::result const& _run2)
+{
+ if (_run1.output_size != _run2.output_size)
+ return false;
+ return memcmp(_run1.output_data, _run2.output_data, _run1.output_size) == 0;
+}
+
+/// Accepts a reference to a user-specified input and returns an
+/// evmc_message with all of its fields zero initialized except
+/// gas and input fields.
+/// The gas field is set to the maximum permissible value so that we
+/// don't run into out of gas errors. The input field is copied from
+/// user input.
+evmc_message initializeMessage(bytes const& _input)
+{
+ // Zero initialize all message fields
+ evmc_message msg = {};
+ // Gas available (value of type int64_t) is set to its maximum
+ // value.
+ msg.gas = std::numeric_limits::max();
+ msg.input_data = _input.data();
+ msg.input_size = _input.size();
+ return msg;
+}
+
+/// Accepts host context implementation, and keccak256 hash of the function
+/// to be called at a specified address in the simulated blockchain as
+/// input and returns the result of the execution of the called function.
+evmc::result executeContract(
+ EVMHost& _hostContext,
+ bytes const& _functionHash,
+ evmc_address _deployedAddress
+)
+{
+ evmc_message message = initializeMessage(_functionHash);
+ message.destination = _deployedAddress;
+ message.kind = EVMC_CALL;
+ return _hostContext.call(message);
+}
+
+/// Accepts a reference to host context implementation and byte code
+/// as input and deploys it on the simulated blockchain. Returns the
+/// result of deployment.
+evmc::result deployContract(EVMHost& _hostContext, bytes const& _code)
+{
+ evmc_message message = initializeMessage(_code);
+ message.kind = EVMC_CREATE;
+ return _hostContext.call(message);
+}
+
+std::pair compileContract(
+ std::string _sourceCode,
+ std::string _contractName,
+ std::map const& _libraryAddresses = {},
+ frontend::OptimiserSettings _optimization = frontend::OptimiserSettings::minimal()
+)
+{
+ try
+ {
+ // Compile contract generated by the proto fuzzer
+ SolidityCompilationFramework solCompilationFramework;
+ return std::make_pair(
+ solCompilationFramework.compileContract(_sourceCode, _contractName, _libraryAddresses, _optimization),
+ solCompilationFramework.getMethodIdentifiers()
+ );
+ }
+ // Ignore stack too deep errors during compilation
+ catch (evmasm::StackTooDeepException const&)
+ {
+ return std::make_pair(bytes{}, Json::Value(0));
+ }
+}
+
+evmc::result deployAndExecute(EVMHost& _hostContext, bytes _byteCode, std::string _hexEncodedInput)
+{
+ // Deploy contract and signal failure if deploy failed
+ evmc::result createResult = deployContract(_hostContext, _byteCode);
+ solAssert(
+ createResult.status_code == EVMC_SUCCESS,
+ "Proto solc fuzzer: Contract creation failed"
+ );
+
+ // Execute test function and signal failure if EVM reverted or
+ // did not return expected output on successful execution.
+ evmc::result callResult = executeContract(
+ _hostContext,
+ fromHex(_hexEncodedInput),
+ createResult.create_address
+ );
+
+ // We don't care about EVM One failures other than EVMC_REVERT
+ solAssert(callResult.status_code != EVMC_REVERT, "Proto solc fuzzer: EVM One reverted");
+ return callResult;
+}
+
+evmc::result compileDeployAndExecute(
+ std::string _sourceCode,
+ std::string _contractName,
+ std::string _methodName,
+ frontend::OptimiserSettings _optimization,
+ std::string _libraryName = {}
+)
+{
+ bytes libraryBytecode;
+ Json::Value libIds;
+ // We target the default EVM which is the latest
+ langutil::EVMVersion version = {};
+ EVMHost hostContext(version, evmone);
+ std::map _libraryAddressMap;
+
+ // First deploy library
+ if (!_libraryName.empty())
+ {
+ tie(libraryBytecode, libIds) = compileContract(
+ _sourceCode,
+ _libraryName,
+ {},
+ _optimization
+ );
+ // Deploy contract and signal failure if deploy failed
+ evmc::result createResult = deployContract(hostContext, libraryBytecode);
+ solAssert(
+ createResult.status_code == EVMC_SUCCESS,
+ "Proto solc fuzzer: Library deployment failed"
+ );
+ _libraryAddressMap[_libraryName] = EVMHost::convertFromEVMC(createResult.create_address);
+ }
+
+ auto [bytecode, ids] = compileContract(
+ _sourceCode,
+ _contractName,
+ _libraryAddressMap,
+ _optimization
+ );
+
+ return deployAndExecute(
+ hostContext,
+ bytecode,
+ ids[_methodName].asString()
+ );
+}
+}
+
+DEFINE_PROTO_FUZZER(Program const& _input)
+{
+ ProtoConverter converter;
+ string sol_source = converter.programToString(_input);
+
+ if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH"))
+ {
+ // With libFuzzer binary run this to generate a YUL source file x.yul:
+ // PROTO_FUZZER_DUMP_PATH=x.yul ./a.out proto-input
+ ofstream of(dump_path);
+ of.write(sol_source.data(), sol_source.size());
+ }
+
+ if (const char* dump_path = getenv("SOL_DEBUG_FILE"))
+ {
+ sol_source.clear();
+ // With libFuzzer binary run this to generate a YUL source file x.yul:
+ // PROTO_FUZZER_LOAD_PATH=x.yul ./a.out proto-input
+ ifstream ifstr(dump_path);
+ sol_source = {
+ std::istreambuf_iterator(ifstr),
+ std::istreambuf_iterator()
+ };
+ std::cout << sol_source << std::endl;
+ }
+
+ auto minimalResult = compileDeployAndExecute(
+ sol_source,
+ ":C",
+ "test()",
+ frontend::OptimiserSettings::minimal(),
+ ""
+ );
+ bool successState = minimalResult.status_code == EVMC_SUCCESS;
+
+ auto optResult = compileDeployAndExecute(
+ sol_source,
+ ":C",
+ "test()",
+ frontend::OptimiserSettings::standard(),
+ ""
+ );
+ if (successState)
+ {
+ solAssert(
+ optResult.status_code == EVMC_SUCCESS,
+ "Sol arith fuzzer: Optimal code failed"
+ );
+ solAssert(
+ compareRuns(minimalResult, optResult),
+ "Sol arith fuzzer: Runs produced different result"
+ );
+ }
+}
\ No newline at end of file
diff --git a/test/tools/ossfuzz/solarithprotoToSol.cpp b/test/tools/ossfuzz/solarithprotoToSol.cpp
new file mode 100644
index 000000000..23b5e2d9a
--- /dev/null
+++ b/test/tools/ossfuzz/solarithprotoToSol.cpp
@@ -0,0 +1,199 @@
+#include
+
+#include
+
+#include
+
+using namespace solidity::test::solarithfuzzer;
+using namespace solidity;
+using namespace solidity::util;
+using namespace std;
+
+string ProtoConverter::programToString(Program const& _program)
+{
+ m_rand = make_unique(
+ SolRandomNumGenerator(_program.seed())
+ );
+ return visit(_program);
+}
+
+string ProtoConverter::visit(Program const& _program)
+{
+ Whiskers p(R"(pragma solidity >= 0.0.0;)");
+ Whiskers c(R"(contract C {)");
+ Whiskers t(R"(function test() public returns ())");
+ m_returnType = signString(_program.s());
+ t("type", m_returnType);
+ t("body", visit(_program.b()));
+ return p.render()
+ + '\n'
+ + c.render()
+ + '\n'
+ + '\t'
+ + t.render()
+ + '\n'
+ + '}';
+}
+
+string ProtoConverter::visit(Return const& _ret)
+{
+ Whiskers r(R"(return ();)");
+ r("expr", visit(_ret.e()));
+ r("type", m_returnType);
+ return "\t\t" + r.render() + '\n';
+}
+
+string ProtoConverter::visit(Block const& _block)
+{
+ ostringstream blockStr;
+ blockStr << '\n'
+ << '\t'
+ << '{'
+ << '\n';
+ blockStr << visit(_block.v());
+ for (auto const& s: _block.s())
+ blockStr << visit(s);
+ blockStr << visit(_block.r());
+ blockStr << '\n'
+ << '\t'
+ << '}';
+ return blockStr.str();
+}
+
+string ProtoConverter::visit(Statement const& _stmt)
+{
+ switch (_stmt.stmt_oneof_case())
+ {
+ case Statement::kVd:
+ return visit(_stmt.vd());
+ case Statement::kA:
+ return visit(_stmt.a());
+ case Statement::kU:
+ return visit(_stmt.u());
+ case Statement::STMT_ONEOF_NOT_SET:
+ return "";
+ }
+}
+
+string ProtoConverter::visit(VarDecl const& _vardecl)
+{
+ bool varExists = varAvailable();
+ Whiskers v(R"( = ();)");
+ string type = visit(_vardecl.t());
+ string varName = newVarName();
+ m_varTypeMap.emplace(varName, type);
+ v("type", type);
+ v("varName", varName);
+ v("value", varExists ? visit(_vardecl.value()) : to_string((*m_rand)()));
+ incrementVarCounter();
+ return "\t\t" + v.render() + '\n';
+}
+
+string ProtoConverter::visit(Type const& _type)
+{
+ return signString(_type.s()) + widthString(_type.bytewidth());
+}
+
+string ProtoConverter::visit(UnaryOpStmt const& _uop)
+{
+ switch (_uop.op())
+ {
+ case UnaryOpStmt_Op_POSTINC:
+ return "\t\t" + visit(_uop.v()) + "++;\n";
+ case UnaryOpStmt_Op_POSTDEC:
+ return "\t\t" + visit(_uop.v()) + "--;\n";
+ case UnaryOpStmt_Op_PREINC:
+ return "\t\t++" + visit(_uop.v()) + ";\n";
+ case UnaryOpStmt_Op_PREDEC:
+ return "\t\t--" + visit(_uop.v()) + ";\n";
+ }
+}
+
+string ProtoConverter::visit(BinaryOp const& _bop)
+{
+ string op{};
+ switch (_bop.op())
+ {
+ case BinaryOp_Op_ADD:
+ op = " + ";
+ break;
+ case BinaryOp_Op_SUB:
+ op = " - ";
+ break;
+ case BinaryOp_Op_MUL:
+ op = " * ";
+ break;
+ case BinaryOp_Op_DIV:
+ op = " / ";
+ break;
+ case BinaryOp_Op_MOD:
+ op = " % ";
+ break;
+// case BinaryOp_Op_ADDSELF:
+// op = " += ";
+// break;
+// case BinaryOp_Op_SUBSELF:
+// op = " -= ";
+// break;
+// case BinaryOp_Op_MULSELF:
+// op = " *= ";
+// break;
+// case BinaryOp_Op_DIVSELF:
+// op = " /= ";
+// break;
+// case BinaryOp_Op_MODSELF:
+// op = " %= ";
+// break;
+ case BinaryOp_Op_EXP:
+ op = " ** ";
+ break;
+ case BinaryOp_Op_SHL:
+ op = " << ";
+ break;
+ case BinaryOp_Op_SHR:
+ op = " >> ";
+ break;
+// case BinaryOp_Op_SHLSELF:
+// op = " <<= ";
+// break;
+// case BinaryOp_Op_SHRSELF:
+// op = " >>= ";
+// break;
+ }
+ return visit(_bop.left()) + op + visit(_bop.right());
+}
+
+string ProtoConverter::visit(Expression const& _expr)
+{
+ switch (_expr.expr_oneof_case())
+ {
+ case Expression::kV:
+ solAssert(varAvailable(), "Sol arith fuzzer: Varref unavaileble");
+ return visit(_expr.v());
+ case Expression::kBop:
+ return visit(_expr.bop());
+ case Expression::EXPR_ONEOF_NOT_SET:
+ return "v0";
+ }
+}
+
+string ProtoConverter::visit(VarRef const&)
+{
+ return randomVarName();
+}
+
+string ProtoConverter::visit(Assignment const& _assignment)
+{
+ if (varAvailable())
+ {
+ string varName = visit(_assignment.id());
+ solAssert(m_varTypeMap.count(varName), "Sol arith fuzzer: Invalid varname");
+ Whiskers a(R"( = ();)");
+ a("varName", varName);
+ a("type", m_varTypeMap[varName]);
+ a("expr", visit(_assignment.value()));
+ return "\t\t" + a.render() + '\n';
+ }
+ else
+ return "";
+}
\ No newline at end of file
diff --git a/test/tools/ossfuzz/solarithprotoToSol.h b/test/tools/ossfuzz/solarithprotoToSol.h
new file mode 100644
index 000000000..4326320a5
--- /dev/null
+++ b/test/tools/ossfuzz/solarithprotoToSol.h
@@ -0,0 +1,114 @@
+#include
+
+#include
+
+namespace solidity::test::solarithfuzzer
+{
+/// Random number generator that is seeded with a fuzzer
+/// supplied unsigned integer.
+struct SolRandomNumGenerator
+{
+ using RandomEngine = std::mt19937_64;
+
+ explicit SolRandomNumGenerator(unsigned _seed): m_random(RandomEngine(_seed)) {}
+
+ /// @returns a pseudo random unsigned integer
+ unsigned operator()()
+ {
+ return m_random();
+ }
+
+ RandomEngine m_random;
+};
+
+enum class Sign
+{
+ Signed,
+ Unsigned
+};
+
+struct SolVarRef
+{
+ SolVarRef(VarRef const& _varref);
+
+ std::string str();
+
+ Sign m_sign;
+};
+
+struct SolExpression
+{
+ SolExpression(Expression const& _e);
+
+ std::string str();
+
+ Sign m_sign;
+};
+
+struct SolBinop
+{
+ SolBinop(SolExpression const& _left, SolExpression const& _right);
+
+ std::string str();
+
+ Sign m_sign;
+};
+
+class ProtoConverter
+{
+public:
+ ProtoConverter()
+ {
+
+ }
+ ProtoConverter(ProtoConverter const&) = delete;
+ ProtoConverter(ProtoConverter&&) = delete;
+ std::string programToString(Program const& _input);
+private:
+ std::string visit(Type const& _type);
+ std::string visit(UnaryOpStmt const& _uop);
+ std::string visit(BinaryOp const& _bop);
+ std::string visit(VarDecl const& _decl);
+ std::string visit(Assignment const& _assignment);
+ std::string visit(Return const& _return);
+ std::string visit(VarRef const& _varref);
+ std::string visit(Expression const& _expr);
+ std::string visit(Statement const& _stmt);
+ std::string visit(Block const& _block);
+ std::string visit(Program const& _program);
+
+ std::string newVarName()
+ {
+ return "v" + std::to_string(m_varCounter);
+ }
+ void incrementVarCounter()
+ {
+ m_varCounter++;
+ }
+ std::string randomVarName()
+ {
+ return "v" + std::to_string((*m_rand)() % m_varCounter);
+ }
+ static std::string signString(Type::Sign _sign)
+ {
+ return _sign == Type::Sign::Type_Sign_SIGNED ? "uint" : "uint";
+ }
+ static std::string widthString(unsigned _width)
+ {
+ return std::to_string((_width % 32 + 1) * 8);
+ }
+ bool varAvailable()
+ {
+ return m_varCounter > 0;
+ }
+ Sign typeSign(Type const& _ts)
+ {
+ return _ts.s() == Type::SIGNED ? Sign::Signed : Sign::Unsigned;
+ }
+
+ unsigned m_varCounter = 0;
+ std::shared_ptr m_rand;
+ std::map m_varTypeMap;
+ std::string m_returnType;
+};
+}
\ No newline at end of file