/src/solidity/test/tools/ossfuzz/SolidityEvmoneInterface.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
32 | | std::optional<CompilerOutput> SolidityCompilationFramework::compileContract() |
33 | 597 | { |
34 | 597 | m_compiler.setSources(m_compilerInput.sourceCode); |
35 | 597 | m_compiler.setLibraries(m_compilerInput.libraryAddresses); |
36 | 597 | m_compiler.setEVMVersion(m_compilerInput.evmVersion); |
37 | 597 | m_compiler.setOptimiserSettings(m_compilerInput.optimiserSettings); |
38 | 597 | m_compiler.setViaIR(m_compilerInput.viaIR); |
39 | 597 | if (!m_compiler.compile()) |
40 | 0 | { |
41 | 0 | if (m_compilerInput.debugFailure) |
42 | 0 | { |
43 | 0 | std::cerr << "Compiling contract failed" << std::endl; |
44 | 0 | std::cerr << SourceReferenceFormatter::formatErrorInformation( |
45 | 0 | m_compiler.errors(), |
46 | 0 | m_compiler |
47 | 0 | ); |
48 | 0 | } |
49 | 0 | return {}; |
50 | 0 | } |
51 | 597 | else |
52 | 597 | { |
53 | 597 | std::string contractName; |
54 | 597 | if (m_compilerInput.contractName.empty()) |
55 | 0 | contractName = m_compiler.lastContractName(); |
56 | 597 | else |
57 | 597 | contractName = m_compilerInput.contractName; |
58 | 597 | evmasm::LinkerObject obj = m_compiler.object(contractName); |
59 | 597 | Json methodIdentifiers = m_compiler.interfaceSymbols(contractName)["methods"]; |
60 | 597 | return CompilerOutput{obj.bytecode, methodIdentifiers}; |
61 | 597 | } |
62 | 597 | } |
63 | | |
64 | | bool EvmoneUtility::zeroWord(uint8_t const* _result, size_t _length) |
65 | 597 | { |
66 | 597 | return _length == 32 && |
67 | 597 | ranges::all_of( |
68 | 597 | ranges::span(_result, static_cast<long>(_length)), |
69 | 19.1k | [](uint8_t _v) { return _v == 0; }); |
70 | 597 | } |
71 | | |
72 | | evmc_message EvmoneUtility::initializeMessage(bytes const& _input) |
73 | 1.19k | { |
74 | | // Zero initialize all message fields |
75 | 1.19k | evmc_message msg = {}; |
76 | | // Gas available (value of type int64_t) is set to its maximum |
77 | | // value. |
78 | 1.19k | msg.gas = std::numeric_limits<int64_t>::max(); |
79 | 1.19k | msg.input_data = _input.data(); |
80 | 1.19k | msg.input_size = _input.size(); |
81 | 1.19k | return msg; |
82 | 1.19k | } |
83 | | |
84 | | evmc::Result EvmoneUtility::executeContract( |
85 | | bytes const& _functionHash, |
86 | | evmc_address _deployedAddress |
87 | | ) |
88 | 597 | { |
89 | 597 | evmc_message message = initializeMessage(_functionHash); |
90 | 597 | message.recipient = _deployedAddress; |
91 | 597 | message.code_address = _deployedAddress; |
92 | 597 | message.kind = EVMC_CALL; |
93 | 597 | return m_evmHost.call(message); |
94 | 597 | } |
95 | | |
96 | | evmc::Result EvmoneUtility::deployContract(bytes const& _code) |
97 | 597 | { |
98 | 597 | evmc_message message = initializeMessage(_code); |
99 | 597 | message.kind = EVMC_CREATE; |
100 | 597 | return m_evmHost.call(message); |
101 | 597 | } |
102 | | |
103 | | evmc::Result EvmoneUtility::deployAndExecute( |
104 | | bytes const& _byteCode, |
105 | | std::string const& _hexEncodedInput |
106 | | ) |
107 | 597 | { |
108 | | // Deploy contract and signal failure if deploy failed |
109 | 597 | evmc::Result createResult = deployContract(_byteCode); |
110 | 597 | solAssert( |
111 | 597 | createResult.status_code == EVMC_SUCCESS, |
112 | 597 | "SolidityEvmoneInterface: Contract creation failed" |
113 | 597 | ); |
114 | | |
115 | | // Execute test function and signal failure if EVM reverted or |
116 | | // did not return expected output on successful execution. |
117 | 597 | evmc::Result callResult = executeContract( |
118 | 597 | util::fromHex(_hexEncodedInput), |
119 | 597 | createResult.create_address |
120 | 597 | ); |
121 | | |
122 | | // We don't care about EVM One failures other than EVMC_REVERT |
123 | 597 | solAssert( |
124 | 597 | callResult.status_code != EVMC_REVERT, |
125 | 597 | "SolidityEvmoneInterface: EVM One reverted" |
126 | 597 | ); |
127 | 597 | return callResult; |
128 | 597 | } |
129 | | |
130 | | evmc::Result EvmoneUtility::compileDeployAndExecute(std::string _fuzzIsabelle) |
131 | 597 | { |
132 | 597 | std::map<std::string, h160> libraryAddressMap; |
133 | | // Stage 1: Compile and deploy library if present. |
134 | 597 | if (!m_libraryName.empty()) |
135 | 0 | { |
136 | 0 | m_compilationFramework.contractName(m_libraryName); |
137 | 0 | auto compilationOutput = m_compilationFramework.compileContract(); |
138 | 0 | solAssert(compilationOutput.has_value(), "Compiling library failed"); |
139 | 0 | CompilerOutput cOutput = compilationOutput.value(); |
140 | | // Deploy contract and signal failure if deploy failed |
141 | 0 | evmc::Result createResult = deployContract(cOutput.byteCode); |
142 | 0 | solAssert( |
143 | 0 | createResult.status_code == EVMC_SUCCESS, |
144 | 0 | "SolidityEvmoneInterface: Library deployment failed" |
145 | 0 | ); |
146 | 0 | libraryAddressMap[m_libraryName] = EVMHost::convertFromEVMC(createResult.create_address); |
147 | 0 | m_compilationFramework.libraryAddresses(libraryAddressMap); |
148 | 0 | } |
149 | | |
150 | | // Stage 2: Compile, deploy, and execute contract, optionally using library |
151 | | // address map. |
152 | 597 | m_compilationFramework.contractName(m_contractName); |
153 | 597 | auto cOutput = m_compilationFramework.compileContract(); |
154 | 597 | solAssert(cOutput.has_value(), "Compiling contract failed"); |
155 | 597 | solAssert( |
156 | 597 | !cOutput->byteCode.empty() && !cOutput->methodIdentifiersInContract.empty(), |
157 | 597 | "SolidityEvmoneInterface: Invalid compilation output." |
158 | 597 | ); |
159 | | |
160 | 597 | std::string methodName; |
161 | 597 | if (!_fuzzIsabelle.empty()) |
162 | | // TODO: Remove this once a cleaner solution is found for querying |
163 | | // isabelle test entry point. At the moment, we are sure that the |
164 | | // entry point is the second method in the contract (hence the ++) |
165 | | // but not its name. |
166 | 129 | methodName = (++cOutput->methodIdentifiersInContract.begin())->get<std::string>() + |
167 | 129 | _fuzzIsabelle.substr(2, _fuzzIsabelle.size()); |
168 | 468 | else |
169 | 468 | methodName = cOutput->methodIdentifiersInContract[m_methodName].get<std::string>(); |
170 | | |
171 | 597 | return deployAndExecute( |
172 | 597 | cOutput->byteCode, |
173 | 597 | methodName |
174 | 597 | ); |
175 | 597 | } |
176 | | |
177 | | std::optional<CompilerOutput> EvmoneUtility::compileContract() |
178 | 0 | { |
179 | 0 | try |
180 | 0 | { |
181 | 0 | return m_compilationFramework.compileContract(); |
182 | 0 | } |
183 | 0 | catch (evmasm::StackTooDeepException const&) |
184 | 0 | { |
185 | 0 | return {}; |
186 | 0 | } |
187 | 0 | } |