1 /*
2 This file is part of solidity.
3
4 solidity is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 solidity is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with solidity. If not, see <http://www.gnu.org/licenses/>.
16 */
17 // SPDX-License-Identifier: GPL-3.0
18
19 #include <test/tools/ossfuzz/SolidityEvmoneInterface.h>
20
21 #include <liblangutil/Exceptions.h>
22 #include <liblangutil/SourceReferenceFormatter.h>
23
24 #include <range/v3/algorithm/all_of.hpp>
25 #include <range/v3/span.hpp>
26
27 using namespace solidity::test::fuzzer;
28 using namespace solidity::frontend;
29 using namespace solidity::langutil;
30 using namespace solidity::util;
31 using namespace std;
32
compileContract()33 optional<CompilerOutput> SolidityCompilationFramework::compileContract()
34 {
35 m_compiler.setSources(m_compilerInput.sourceCode);
36 m_compiler.setLibraries(m_compilerInput.libraryAddresses);
37 m_compiler.setEVMVersion(m_compilerInput.evmVersion);
38 m_compiler.setOptimiserSettings(m_compilerInput.optimiserSettings);
39 m_compiler.setViaIR(m_compilerInput.viaIR);
40 if (!m_compiler.compile())
41 {
42 if (m_compilerInput.debugFailure)
43 {
44 cerr << "Compiling contract failed" << endl;
45 for (auto const& error: m_compiler.errors())
46 cerr << SourceReferenceFormatter::formatErrorInformation(
47 *error,
48 m_compiler
49 );
50 }
51 return {};
52 }
53 else
54 {
55 string contractName;
56 if (m_compilerInput.contractName.empty())
57 contractName = m_compiler.lastContractName();
58 else
59 contractName = m_compilerInput.contractName;
60 evmasm::LinkerObject obj = m_compiler.object(contractName);
61 Json::Value methodIdentifiers = m_compiler.methodIdentifiers(contractName);
62 return CompilerOutput{obj.bytecode, methodIdentifiers};
63 }
64 }
65
zeroWord(uint8_t const * _result,size_t _length)66 bool EvmoneUtility::zeroWord(uint8_t const* _result, size_t _length)
67 {
68 return _length == 32 &&
69 ranges::all_of(
70 ranges::span(_result, static_cast<long>(_length)),
71 [](uint8_t _v) { return _v == 0; });
72 }
73
initializeMessage(bytes const & _input)74 evmc_message EvmoneUtility::initializeMessage(bytes const& _input)
75 {
76 // Zero initialize all message fields
77 evmc_message msg = {};
78 // Gas available (value of type int64_t) is set to its maximum
79 // value.
80 msg.gas = std::numeric_limits<int64_t>::max();
81 msg.input_data = _input.data();
82 msg.input_size = _input.size();
83 return msg;
84 }
85
executeContract(bytes const & _functionHash,evmc_address _deployedAddress)86 evmc::result EvmoneUtility::executeContract(
87 bytes const& _functionHash,
88 evmc_address _deployedAddress
89 )
90 {
91 evmc_message message = initializeMessage(_functionHash);
92 message.destination = _deployedAddress;
93 message.kind = EVMC_CALL;
94 return m_evmHost.call(message);
95 }
96
deployContract(bytes const & _code)97 evmc::result EvmoneUtility::deployContract(bytes const& _code)
98 {
99 evmc_message message = initializeMessage(_code);
100 message.kind = EVMC_CREATE;
101 return m_evmHost.call(message);
102 }
103
deployAndExecute(bytes const & _byteCode,string const & _hexEncodedInput)104 evmc::result EvmoneUtility::deployAndExecute(
105 bytes const& _byteCode,
106 string const& _hexEncodedInput
107 )
108 {
109 // Deploy contract and signal failure if deploy failed
110 evmc::result createResult = deployContract(_byteCode);
111 solAssert(
112 createResult.status_code == EVMC_SUCCESS,
113 "SolidityEvmoneInterface: Contract creation failed"
114 );
115
116 // Execute test function and signal failure if EVM reverted or
117 // did not return expected output on successful execution.
118 evmc::result callResult = executeContract(
119 util::fromHex(_hexEncodedInput),
120 createResult.create_address
121 );
122
123 // We don't care about EVM One failures other than EVMC_REVERT
124 solAssert(
125 callResult.status_code != EVMC_REVERT,
126 "SolidityEvmoneInterface: EVM One reverted"
127 );
128 return callResult;
129 }
130
compileDeployAndExecute(string _fuzzIsabelle)131 optional<evmc::result> EvmoneUtility::compileDeployAndExecute(string _fuzzIsabelle)
132 {
133 map<string, h160> libraryAddressMap;
134 // Stage 1: Compile and deploy library if present.
135 if (!m_libraryName.empty())
136 {
137 m_compilationFramework.contractName(m_libraryName);
138 auto compilationOutput = m_compilationFramework.compileContract();
139 if (compilationOutput.has_value())
140 {
141 CompilerOutput cOutput = compilationOutput.value();
142 // Deploy contract and signal failure if deploy failed
143 evmc::result createResult = deployContract(cOutput.byteCode);
144 solAssert(
145 createResult.status_code == EVMC_SUCCESS,
146 "SolidityEvmoneInterface: Library deployment failed"
147 );
148 libraryAddressMap[m_libraryName] = EVMHost::convertFromEVMC(createResult.create_address);
149 m_compilationFramework.libraryAddresses(libraryAddressMap);
150 }
151 else
152 return {};
153 }
154
155 // Stage 2: Compile, deploy, and execute contract, optionally using library
156 // address map.
157 m_compilationFramework.contractName(m_contractName);
158 auto cOutput = m_compilationFramework.compileContract();
159 if (cOutput.has_value())
160 {
161 solAssert(
162 !cOutput->byteCode.empty() && !cOutput->methodIdentifiersInContract.empty(),
163 "SolidityEvmoneInterface: Invalid compilation output."
164 );
165
166 string methodName;
167 if (!_fuzzIsabelle.empty())
168 // TODO: Remove this once a cleaner solution is found for querying
169 // isabelle test entry point. At the moment, we are sure that the
170 // entry point is the second method in the contract (hence the ++)
171 // but not its name.
172 methodName = (++cOutput->methodIdentifiersInContract.begin())->asString() +
173 _fuzzIsabelle.substr(2, _fuzzIsabelle.size());
174 else
175 methodName = cOutput->methodIdentifiersInContract[m_methodName].asString();
176
177 return deployAndExecute(
178 cOutput->byteCode,
179 methodName
180 );
181 }
182 else
183 return {};
184 }
185
compileContract()186 optional<CompilerOutput> EvmoneUtility::compileContract()
187 {
188 try
189 {
190 return m_compilationFramework.compileContract();
191 }
192 catch (evmasm::StackTooDeepException const&)
193 {
194 return {};
195 }
196 }
197