diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt
index 5840741db..1de27af84 100644
--- a/test/tools/ossfuzz/CMakeLists.txt
+++ b/test/tools/ossfuzz/CMakeLists.txt
@@ -2,6 +2,7 @@ add_custom_target(ossfuzz)
add_dependencies(ossfuzz
solc_ossfuzz
bytecode_optimizer_ossfuzz
+ solc_reverse_ossfuzz
solc_mutator_ossfuzz
const_opt_ossfuzz
strictasm_diff_ossfuzz
@@ -35,6 +36,22 @@ if (OSSFUZZ)
)
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})
+ target_include_directories(solc_ossfuzz PRIVATE
+ /home/bhargava/work/github/solidity/deps/libabicoder
+ )
+ set_target_properties(solc_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
+
+ add_executable(solc_reverse_ossfuzz
+ solc_reverse_ossfuzz.cpp
+ ValueGenerator.cpp
+ ../fuzzer_common.cpp
+ ../../TestCaseReader.cpp
+ ../../EVMHost.cpp
+ SolidityEvmoneInterface.cpp
+ ../../libsolidity/util/ContractABIUtils.cpp
+ )
+ target_link_libraries(solc_reverse_ossfuzz PRIVATE libsolc evmasm evmc evmone-standalone abicoder gmp.a)
+ set_target_properties(solc_reverse_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
add_executable(bytecode_optimizer_ossfuzz
BytecodeOptimizerFuzzer.cpp
diff --git a/test/tools/ossfuzz/solc_reverse_ossfuzz.cpp b/test/tools/ossfuzz/solc_reverse_ossfuzz.cpp
new file mode 100644
index 000000000..36013ad1b
--- /dev/null
+++ b/test/tools/ossfuzz/solc_reverse_ossfuzz.cpp
@@ -0,0 +1,298 @@
+/*
+ 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
+
+#include
+
+#include
+
+#include
+
+#include
+
+#include
+
+#include
+#include
+
+using namespace solidity::frontend::test;
+using namespace solidity::frontend;
+using namespace solidity::test::fuzzer;
+using namespace solidity::langutil;
+using namespace solidity::test;
+using namespace std;
+
+// Prototype as we can't use the FuzzerInterface.h header.
+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;
+
+namespace
+{
+string abiEncoding(
+ Json::Value const& _functionABI,
+ map> _addressSelectors,
+ Json::Value const& _methodIdentifiers
+)
+{
+ string abiTypeString;
+ string abiValueString;
+ tie(abiTypeString, abiValueString) = ValueGenerator{
+ _functionABI["inputs"],
+ 0,
+ std::move(_addressSelectors)
+ }.type();
+ string encodedData;
+ // A function with inputs must contain type names within
+ // parentheses.
+ bool functionWithInputs = abiTypeString != "()";
+ string functionSignature = _functionABI["name"].asString() + abiTypeString;
+ cout << functionSignature << endl;
+ string encoding = _methodIdentifiers[functionSignature].asString();
+ if (functionWithInputs)
+ {
+ abicoder::ABICoder coder(abiCoderHeapSize);
+ auto [status, data] = coder.encode(abiTypeString, abiValueString);
+ cout << abiTypeString << endl;
+ cout << abiValueString << endl;
+ solAssert(status, "Isabelle coder: failed.");
+ encoding += data.substr(2, data.size());
+ }
+ return encoding;
+}
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
+{
+ string input(reinterpret_cast(_data), _size);
+ regex re = regex("library\\s*(\\w+)\\s*\\{");
+ smatch matches;
+ std::string libraryName;
+ auto match = regex_search(input, matches, re);
+ if (match && matches[1].matched)
+ libraryName = matches[1].str();
+
+ map sourceCode;
+ try
+ {
+ EVMVersion version;
+ EVMHost hostContext(version, evmone);
+
+ TestCaseReader t = TestCaseReader(std::istringstream(input));
+ sourceCode = t.sources().sources;
+ string contractName;
+ string methodName;
+ auto compilerSetting = OptimiserSettings::standard();
+ CompilerInput cInput = {
+ version,
+ sourceCode,
+ contractName,
+ compilerSetting,
+ {},
+ true,
+ true
+ };
+ EvmoneUtility evmoneUtil(
+ hostContext,
+ cInput,
+ contractName,
+ libraryName,
+ methodName
+ );
+
+ map> addressSelector;
+ // Add pre-compiles
+ for (auto const& account: hostContext.accounts)
+ addressSelector[EVMHost::convertFromEVMC(account.first)] = {};
+
+ optional libraryInfo;
+ vector libraryFunctionSelectors;
+ if (!libraryName.empty()) {
+ cout << "Deploying library" << endl;
+ libraryInfo = evmoneUtil.compileAndDeployLibrary();
+ if (!libraryInfo.has_value() || get<0>(libraryInfo.value()) != EVMC_SUCCESS)
+ return 0;
+ // Add library functions to address selector
+ libraryFunctionSelectors = get<2>(libraryInfo.value());
+ if (!libraryFunctionSelectors.empty())
+ addressSelector[get<1>(libraryInfo.value())] = libraryFunctionSelectors;
+ cout << "Deployed" << endl;
+ }
+
+ auto compilerOutput = evmoneUtil.compileContract();
+ if (!compilerOutput.has_value())
+ return 0;
+
+ auto r = evmoneUtil.randomFunction(_size);
+ if (!r.has_value())
+ return 0;
+
+ auto deployResult = evmoneUtil.deployContract(compilerOutput->byteCode);
+ if (deployResult.status_code != EVMC_SUCCESS)
+ return 0;
+
+ // Record function selectors in contract
+ vector functionSelectors;
+ for (auto const& item: compilerOutput->methodIdentifiersInContract)
+ functionSelectors.push_back(item.asString());
+
+ // Add deployed contract to list of address literals.
+ addressSelector[EVMHost::convertFromEVMC(deployResult.create_address)] = functionSelectors;
+
+ string encodedData = abiEncoding(r.value(), addressSelector,
+ compilerOutput->methodIdentifiersInContract);
+ auto callResult = evmoneUtil.executeContract(
+ solidity::util::fromHex(encodedData),
+ deployResult.create_address
+ );
+
+ if (callResult.status_code != EVMC_SUCCESS) {
+ cout << "New 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++)
+ result.push_back(callResult.output_data[i]);
+
+ EVMHostPrinter p(hostContext, deployResult.create_address);
+ ostringstream oldCodeGen;
+ oldCodeGen << p.state();
+
+ // Reset call state post old code gen run
+ hostContext.reset();
+ addressSelector.clear();
+ bool libraryFunctions = !libraryFunctionSelectors.empty();
+ libraryFunctionSelectors.clear();
+ evmoneUtil.optSetting(compilerSetting);
+ evmoneUtil.viaIR(false);
+
+ // Add pre-compiles post reset
+ for (auto const& account: hostContext.accounts)
+ addressSelector[EVMHost::convertFromEVMC(account.first)] = {};
+
+ optional libraryInfoNew;
+ if (!libraryName.empty())
+ {
+ libraryInfoNew = evmoneUtil.compileAndDeployLibrary();
+ solAssert(
+ libraryInfoNew.has_value() && get<0>(libraryInfoNew.value()) == EVMC_SUCCESS,
+ "Deploying library failed for the second time"
+ );
+ libraryFunctionSelectors = get<2>(libraryInfoNew.value());
+ // Library functions in both runs should either be both empty or
+ // both non-empty.
+ solAssert(
+ (
+ (libraryFunctions && !libraryFunctionSelectors.empty()) ||
+ (!libraryFunctions && libraryFunctionSelectors.empty())
+ ),
+ "Library function mismatch."
+ );
+ if (!libraryFunctionSelectors.empty())
+ addressSelector[get<1>(libraryInfoNew.value())] = libraryFunctionSelectors;
+ }
+
+ auto compilerOutputOpt = evmoneUtil.compileContract();
+ solAssert(
+ compilerOutputOpt.has_value(),
+ "Contract could not be optimised via old code gen."
+ );
+
+ auto deployResultOpt = evmoneUtil.deployContract(compilerOutputOpt->byteCode);
+ solAssert(
+ deployResultOpt.status_code == EVMC_SUCCESS,
+ "Contract optimised via old code gen could not be deployed."
+ );
+
+ // Add address literal of contract compiled via Yul IR.
+ addressSelector[EVMHost::convertFromEVMC(deployResultOpt.create_address)] = functionSelectors;
+
+ string encodedDataIR = abiEncoding(r.value(), addressSelector,
+ compilerOutput->methodIdentifiersInContract);
+
+ auto callResultOpt = evmoneUtil.executeContract(
+ solidity::util::fromHex(encodedDataIR),
+ deployResultOpt.create_address
+ );
+ solAssert(
+ callResultOpt.status_code == EVMC_SUCCESS,
+ "Old code gen contract call failed."
+ );
+
+ solidity::bytes resultOpt;
+ for (size_t i = 0; i < callResultOpt.output_size; i++)
+ resultOpt.push_back(callResultOpt.output_data[i]);
+
+ if (result != resultOpt)
+ {
+ cout << solidity::util::toHex(result) << endl;
+ cout << solidity::util::toHex(resultOpt) << endl;
+ }
+ solAssert(
+ result == resultOpt,
+ "Old and new code gen call results do not match."
+ );
+
+ EVMHostPrinter pOpt(hostContext, deployResultOpt.create_address);
+ ostringstream newCodeGen;
+ newCodeGen << pOpt.state();
+
+ if (oldCodeGen.str() != newCodeGen.str())
+ {
+ cout << oldCodeGen.str() << endl;
+ cout << newCodeGen.str() << endl;
+ }
+ solAssert(
+ oldCodeGen.str() == newCodeGen.str(),
+ "Old and new code gen state do not match."
+ );
+ return 0;
+ }
+ catch (runtime_error const&)
+ {
+ cout << "Runtime error!" << endl;
+ return 0;
+ }
+ catch (solidity::langutil::UnimplementedFeatureError const&)
+ {
+ cout << "Unimplemented feature!" << endl;
+ return 0;
+ }
+ catch (solidity::langutil::CompilerError const& _e)
+ {
+ cout << "Compiler error!" << endl;
+ cout << _e.what() << endl;
+ return 0;
+ }
+ catch (solidity::yul::StackTooDeepError const&)
+ {
+ cout << "Stack too deep" << endl;
+ return 0;
+ }
+}