Coverage Report

Created: 2025-09-08 08:10

/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
}