Coverage Report

Created: 2026-07-13 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/test/tools/ossfuzz/protoToSol.cpp
Line
Count
Source
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/protoToSol.h>
20
21
#include <liblangutil/Exceptions.h>
22
23
#include <libsolutil/Whiskers.h>
24
25
#include <sstream>
26
27
using namespace solidity::test::solprotofuzzer;
28
using namespace solidity::util;
29
30
std::string ProtoConverter::protoToSolidity(Program const& _p)
31
382
{
32
  // Create random number generator with fuzzer supplied
33
  // seed.
34
382
  m_randomGen = std::make_shared<SolRandomNumGenerator>(_p.seed());
35
382
  return visit(_p);
36
382
}
37
38
std::pair<std::string, std::string> ProtoConverter::generateTestCase(TestContract const& _testContract)
39
0
{
40
0
  std::ostringstream testCode;
41
0
  std::string usingLibDecl;
42
0
  switch (_testContract.type())
43
0
  {
44
0
  case TestContract::LIBRARY:
45
0
  {
46
0
    m_libraryTest = true;
47
0
    auto testTuple = pseudoRandomLibraryTest();
48
0
    m_libraryName = std::get<0>(testTuple);
49
0
    Whiskers u(R"(<ind>using <libraryName> for uint;)");
50
0
    u("ind", "\t");
51
0
    u("libraryName", std::get<0>(testTuple));
52
0
    usingLibDecl = u.render();
53
0
    Whiskers test(R"(<endl><ind><varDecl><endl><ind><ifStmt>)");
54
0
    test("endl", "\n");
55
0
    test("ind", "\t\t");
56
0
    test("varDecl", "uint x;");
57
0
    Whiskers ifStmt(R"(if (<cond>)<endl><ind>return 1;)");
58
0
    Whiskers ifCond(R"(x.<testFunction>() != <expectedOutput>)");
59
0
    ifCond("testFunction", std::get<1>(testTuple));
60
0
    ifCond("expectedOutput", std::get<2>(testTuple));
61
0
    ifStmt("cond", ifCond.render());
62
0
    ifStmt("endl", "\n");
63
0
    ifStmt("ind", "\t\t\t");
64
0
    test("ifStmt", ifStmt.render());
65
0
    break;
66
0
  }
67
0
  case TestContract::CONTRACT:
68
0
  {
69
0
    unsigned errorCode = 1;
70
0
    unsigned contractVarIndex = 0;
71
0
    for (auto const& testTuple: m_contractTests)
72
0
    {
73
      // Do this to avoid stack too deep errors
74
      // We require uint as a return var, so we
75
      // cannot have more than 16 variables without
76
      // running into stack too deep errors
77
0
      if (contractVarIndex >= s_maxVars)
78
0
        break;
79
0
      std::string contractName = testTuple.first;
80
0
      std::string contractVarName = "tc" + std::to_string(contractVarIndex);
81
0
      Whiskers init(R"(<endl><ind><contractName> <contractVarName> = new <contractName>();)");
82
0
      init("endl", "\n");
83
0
      init("ind", "\t\t");
84
0
      init("contractName", contractName);
85
0
      init("contractVarName", contractVarName);
86
0
      testCode << init.render();
87
0
      for (auto const& t: testTuple.second)
88
0
      {
89
0
        Whiskers tc(R"(<endl><ind><ifStmt>)");
90
0
        tc("endl", "\n");
91
0
        tc("ind", "\t\t");
92
0
        Whiskers ifStmt(R"(if (<cond>)<endl><ind>return <errorCode>;)");
93
0
        Whiskers ifCond(R"(<contractVarName>.<testFunction>() != <expectedOutput>)");
94
0
        ifCond("contractVarName", contractVarName);
95
0
        ifCond("testFunction", t.first);
96
0
        ifCond("expectedOutput", t.second);
97
0
        ifStmt("endl", "\n");
98
0
        ifStmt("cond", ifCond.render());
99
0
        ifStmt("ind", "\t\t\t");
100
0
        ifStmt("errorCode", std::to_string(errorCode));
101
0
        tc("ifStmt", ifStmt.render());
102
0
        testCode << tc.render();
103
0
        errorCode++;
104
0
      }
105
0
      contractVarIndex++;
106
0
    }
107
0
    break;
108
0
  }
109
0
  }
110
  // Expected return value when all tests pass
111
0
  testCode << Whiskers(R"(<endl><ind>return 0;)")("endl", "\n")("ind", "\t\t").render();
112
0
  return {usingLibDecl, testCode.str()};
113
0
}
114
115
std::string ProtoConverter::visit(TestContract const& _testContract)
116
382
{
117
382
  std::string testCode;
118
382
  std::string usingLibDecl;
119
382
  m_libraryTest = false;
120
121
  // Simply return valid uint (zero) if there are
122
  // no tests.
123
382
  if (emptyLibraryTests() || emptyContractTests())
124
382
    testCode = Whiskers(R"(<endl><ind>return 0;)")("endl", "\n")("ind", "\t\t").render();
125
0
  else
126
0
    tie(usingLibDecl, testCode) = generateTestCase(_testContract);
127
128
382
  Whiskers c(R"(<endl>contract C {<?isLibrary><usingDecl></isLibrary><endl><function><endl>})");
129
382
  c("endl", "\n");
130
382
  c("isLibrary", m_libraryTest);
131
382
  c("usingDecl", usingLibDecl);
132
382
  Whiskers f("<ind>function test() public returns (uint)<endl><ind>{<testCode><endl><ind>}");
133
382
  f("ind", "\t");
134
382
  f("endl", "\n");
135
382
  f("testCode", testCode);
136
382
  c("function", f.render());
137
382
  return c.render();
138
382
}
139
140
bool ProtoConverter::libraryTest() const
141
382
{
142
382
  return m_libraryTest;
143
382
}
144
145
std::string ProtoConverter::libraryName() const
146
0
{
147
0
  return m_libraryName;
148
0
}
149
150
std::string ProtoConverter::visit(Program const& _p)
151
382
{
152
382
  std::ostringstream program;
153
382
  std::ostringstream contracts;
154
155
382
  for (auto &contract: _p.contracts())
156
199k
    contracts << visit(contract);
157
158
382
  Whiskers p(R"(<endl>pragma solidity >=0.0;<endl><contracts><endl><test>)");
159
382
  p("endl", "\n");
160
382
  p("contracts", contracts.str());
161
382
  p("test", visit(_p.test()));
162
382
  return p.render();
163
382
}
164
165
std::string ProtoConverter::visit(ContractType const& _contractType)
166
199k
{
167
199k
  switch (_contractType.contract_type_oneof_case())
168
199k
  {
169
1.91k
  case ContractType::kC:
170
1.91k
    return visit(_contractType.c());
171
56.6k
  case ContractType::kL:
172
56.6k
    return visit(_contractType.l());
173
67.6k
  case ContractType::kI:
174
67.6k
    return visit(_contractType.i());
175
73.5k
  case ContractType::CONTRACT_TYPE_ONEOF_NOT_SET:
176
73.5k
    return "";
177
199k
  }
178
199k
}
179
180
std::string ProtoConverter::visit(Contract const& _contract)
181
1.91k
{
182
1.91k
  openProgramScope(&_contract);
183
1.91k
  return "";
184
1.91k
}
185
186
std::string ProtoConverter::visit(Interface const& _interface)
187
67.6k
{
188
67.6k
  openProgramScope(&_interface);
189
67.6k
  return "";
190
67.6k
}
191
192
std::string ProtoConverter::visit(Library const& _library)
193
56.6k
{
194
56.6k
  openProgramScope(&_library);
195
56.6k
  return "";
196
56.6k
}
197
198
std::tuple<std::string, std::string, std::string> ProtoConverter::pseudoRandomLibraryTest()
199
0
{
200
0
  solAssert(m_libraryTests.size() > 0, "Sol proto fuzzer: No library tests found");
201
0
  unsigned index = randomNumber() % m_libraryTests.size();
202
0
  return m_libraryTests[index];
203
0
}
204
205
void ProtoConverter::openProgramScope(CIL _program)
206
126k
{
207
126k
  std::string programNamePrefix;
208
126k
  if (std::holds_alternative<Contract const*>(_program))
209
1.91k
    programNamePrefix = "C";
210
124k
  else if (std::holds_alternative<Interface const*>(_program))
211
67.6k
    programNamePrefix = "I";
212
56.6k
  else
213
56.6k
    programNamePrefix = "L";
214
126k
  std::string programName = programNamePrefix + std::to_string(m_programNumericSuffix++);
215
126k
  m_programNameMap.emplace(_program, programName);
216
126k
}
217
218
std::string ProtoConverter::programName(CIL _program)
219
0
{
220
0
  solAssert(m_programNameMap.count(_program), "Sol proto fuzzer: Unregistered program");
221
0
  return m_programNameMap[_program];
222
0
}
223
224
unsigned ProtoConverter::randomNumber()
225
0
{
226
  solAssert(m_randomGen, "Sol proto fuzzer: Uninitialized random number generator");
227
0
  return m_randomGen->operator()();
228
0
}