Coverage Report

Created: 2025-06-24 07:59

/src/solidity/test/tools/ossfuzz/SolidityGenerator.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/SolidityGenerator.h>
20
21
#include <libsolutil/Whiskers.h>
22
#include <libsolutil/Visitor.h>
23
24
using namespace solidity::test::fuzzer::mutator;
25
using namespace solidity::util;
26
27
GeneratorBase::GeneratorBase(std::shared_ptr<SolidityGenerator> _mutator)
28
0
{
29
0
  mutator = std::move(_mutator);
30
0
  state = mutator->testState();
31
0
  uRandDist = mutator->uniformRandomDist();
32
0
}
33
34
std::string GeneratorBase::visitChildren()
35
0
{
36
0
  std::ostringstream os;
37
  // Randomise visit order
38
0
  std::vector<GeneratorPtr> randomisedChildren;
39
0
  for (auto const& child: generators)
40
0
    randomisedChildren.push_back(child);
41
0
  shuffle(randomisedChildren.begin(), randomisedChildren.end(), *uRandDist->randomEngine);
42
0
  for (auto child: randomisedChildren)
43
0
    os << std::visit(GenericVisitor{
44
0
      [&](auto const& _item) { return _item->generate(); }
Unexecuted instantiation: SolidityGenerator.cpp:auto solidity::test::fuzzer::mutator::GeneratorBase::visitChildren()::$_0::operator()<std::__1::shared_ptr<solidity::test::fuzzer::mutator::ImportGenerator> >(std::__1::shared_ptr<solidity::test::fuzzer::mutator::ImportGenerator> const&) const
Unexecuted instantiation: SolidityGenerator.cpp:auto solidity::test::fuzzer::mutator::GeneratorBase::visitChildren()::$_0::operator()<std::__1::shared_ptr<solidity::test::fuzzer::mutator::PragmaGenerator> >(std::__1::shared_ptr<solidity::test::fuzzer::mutator::PragmaGenerator> const&) const
Unexecuted instantiation: SolidityGenerator.cpp:auto solidity::test::fuzzer::mutator::GeneratorBase::visitChildren()::$_0::operator()<std::__1::shared_ptr<solidity::test::fuzzer::mutator::SourceUnitGenerator> >(std::__1::shared_ptr<solidity::test::fuzzer::mutator::SourceUnitGenerator> const&) const
Unexecuted instantiation: SolidityGenerator.cpp:auto solidity::test::fuzzer::mutator::GeneratorBase::visitChildren()::$_0::operator()<std::__1::shared_ptr<solidity::test::fuzzer::mutator::TestCaseGenerator> >(std::__1::shared_ptr<solidity::test::fuzzer::mutator::TestCaseGenerator> const&) const
45
0
    }, child);
46
0
  return os.str();
47
0
}
48
49
std::string TestState::randomPath(std::set<std::string> const& _sourceUnitPaths) const
50
0
{
51
0
  auto it = _sourceUnitPaths.begin();
52
  /// Advance iterator by n where 0 <= n <= sourceUnitPaths.size() - 1
53
0
  size_t increment = uRandDist->distributionOneToN(_sourceUnitPaths.size()) - 1;
54
0
  solAssert(
55
0
    increment >= 0 && increment < _sourceUnitPaths.size(),
56
0
    "Solc custom mutator: Invalid increment"
57
0
  );
58
0
  advance(it, increment);
59
0
  return *it;
60
0
}
61
62
std::string TestState::randomPath() const
63
0
{
64
0
  solAssert(!empty(), "Solc custom mutator: Null test state");
65
0
  return randomPath(sourceUnitPaths);
66
0
}
67
68
void TestState::print(std::ostream& _os) const
69
0
{
70
0
  _os << "Printing test state" << std::endl;
71
0
  for (auto const& item: sourceUnitPaths)
72
0
    _os << "Source path: " << item << std::endl;
73
0
}
74
75
std::string TestState::randomNonCurrentPath() const
76
0
{
77
  /// To obtain a source path that is not the currently visited
78
  /// source unit itself, we require at least one other source
79
  /// unit to be previously visited.
80
0
  solAssert(size() >= 2, "Solc custom mutator: Invalid test state");
81
82
0
  std::set<std::string> filteredSourcePaths;
83
0
  std::string currentPath = currentSourceUnitPath;
84
0
  std::copy_if(
85
0
    sourceUnitPaths.begin(),
86
0
    sourceUnitPaths.end(),
87
0
    inserter(filteredSourcePaths, filteredSourcePaths.begin()),
88
0
    [currentPath](std::string const& _item) {
89
0
      return _item != currentPath;
90
0
    }
91
0
  );
92
0
  return randomPath(filteredSourcePaths);
93
0
}
94
95
void TestCaseGenerator::setup()
96
0
{
97
0
  addGenerators({
98
0
    mutator->generator<SourceUnitGenerator>()
99
0
  });
100
0
}
101
102
std::string TestCaseGenerator::visit()
103
0
{
104
0
  std::ostringstream os;
105
0
  for (unsigned i = 0; i < uRandDist->distributionOneToN(s_maxSourceUnits); i++)
106
0
  {
107
0
    std::string sourcePath = path();
108
0
    os << "\n"
109
0
      << "==== Source: "
110
0
      << sourcePath
111
0
      << " ===="
112
0
          << "\n";
113
0
    updateSourcePath(sourcePath);
114
0
    m_numSourceUnits++;
115
0
    os << visitChildren();
116
0
  }
117
0
  return os.str();
118
0
}
119
120
void SourceUnitGenerator::setup()
121
0
{
122
0
  addGenerators({
123
0
    mutator->generator<ImportGenerator>(),
124
0
    mutator->generator<PragmaGenerator>()
125
0
  });
126
0
}
127
128
std::string SourceUnitGenerator::visit()
129
0
{
130
0
  return visitChildren();
131
0
}
132
133
std::string PragmaGenerator::visit()
134
0
{
135
0
  static constexpr const char* preamble = R"(
136
0
    pragma solidity >= 0.0.0;
137
0
    pragma experimental SMTChecker;
138
0
  )";
139
  // Choose equally at random from coder v1 and v2
140
0
  std::string abiPragma = "pragma abicoder v" +
141
0
    std::to_string(uRandDist->distributionOneToN(2)) +
142
0
    ";\n";
143
0
  return preamble + abiPragma;
144
0
}
145
146
std::string ImportGenerator::visit()
147
0
{
148
  /*
149
   * Case 1: No source units defined
150
   * Case 2: One source unit defined
151
   * Case 3: At least two source units defined
152
   */
153
0
  std::ostringstream os;
154
  // Self import with a small probability only if
155
  // there is one source unit present in test.
156
0
  if (state->size() == 1)
157
0
  {
158
0
    if (uRandDist->probable(s_selfImportInvProb))
159
0
      os << "import "
160
0
         << "\""
161
0
         << state->randomPath()
162
0
         << "\";";
163
0
  }
164
0
  else
165
0
  {
166
    // Import a different source unit if at least
167
    // two source units available.
168
0
    os << "import "
169
0
      << "\""
170
0
      << state->randomNonCurrentPath()
171
0
      << "\";";
172
0
  }
173
0
  return os.str();
174
0
}
175
176
template <typename T>
177
std::shared_ptr<T> SolidityGenerator::generator()
178
0
{
179
0
  for (auto& g: m_generators)
180
0
    if (std::holds_alternative<std::shared_ptr<T>>(g))
181
0
      return std::get<std::shared_ptr<T>>(g);
182
0
  solAssert(false, "");
183
0
}
Unexecuted instantiation: std::__1::shared_ptr<solidity::test::fuzzer::mutator::SourceUnitGenerator> solidity::test::fuzzer::mutator::SolidityGenerator::generator<solidity::test::fuzzer::mutator::SourceUnitGenerator>()
Unexecuted instantiation: std::__1::shared_ptr<solidity::test::fuzzer::mutator::ImportGenerator> solidity::test::fuzzer::mutator::SolidityGenerator::generator<solidity::test::fuzzer::mutator::ImportGenerator>()
Unexecuted instantiation: std::__1::shared_ptr<solidity::test::fuzzer::mutator::PragmaGenerator> solidity::test::fuzzer::mutator::SolidityGenerator::generator<solidity::test::fuzzer::mutator::PragmaGenerator>()
Unexecuted instantiation: std::__1::shared_ptr<solidity::test::fuzzer::mutator::TestCaseGenerator> solidity::test::fuzzer::mutator::SolidityGenerator::generator<solidity::test::fuzzer::mutator::TestCaseGenerator>()
184
185
SolidityGenerator::SolidityGenerator(unsigned _seed)
186
0
{
187
0
  m_generators = {};
188
0
  m_urd = std::make_shared<UniformRandomDistribution>(std::make_unique<RandomEngine>(_seed));
189
0
  m_state = std::make_shared<TestState>(m_urd);
190
0
}
191
192
template <size_t I>
193
void SolidityGenerator::createGenerators()
194
0
{
195
0
  if constexpr (I < std::variant_size_v<Generator>)
196
0
  {
197
0
    createGenerator<std::variant_alternative_t<I, Generator>>();
198
0
    createGenerators<I + 1>();
199
0
  }
200
0
}
Unexecuted instantiation: void solidity::test::fuzzer::mutator::SolidityGenerator::createGenerators<0ul>()
Unexecuted instantiation: void solidity::test::fuzzer::mutator::SolidityGenerator::createGenerators<1ul>()
Unexecuted instantiation: void solidity::test::fuzzer::mutator::SolidityGenerator::createGenerators<2ul>()
Unexecuted instantiation: void solidity::test::fuzzer::mutator::SolidityGenerator::createGenerators<3ul>()
Unexecuted instantiation: void solidity::test::fuzzer::mutator::SolidityGenerator::createGenerators<4ul>()
201
202
std::string SolidityGenerator::generateTestProgram()
203
0
{
204
0
  createGenerators();
205
0
  for (auto& g: m_generators)
206
0
    std::visit(GenericVisitor{
207
0
      [&](auto const& _item) { return _item->setup(); }
Unexecuted instantiation: SolidityGenerator.cpp:auto solidity::test::fuzzer::mutator::SolidityGenerator::generateTestProgram()::$_2::operator()<std::__1::shared_ptr<solidity::test::fuzzer::mutator::ImportGenerator> >(std::__1::shared_ptr<solidity::test::fuzzer::mutator::ImportGenerator> const&) const
Unexecuted instantiation: SolidityGenerator.cpp:auto solidity::test::fuzzer::mutator::SolidityGenerator::generateTestProgram()::$_2::operator()<std::__1::shared_ptr<solidity::test::fuzzer::mutator::PragmaGenerator> >(std::__1::shared_ptr<solidity::test::fuzzer::mutator::PragmaGenerator> const&) const
Unexecuted instantiation: SolidityGenerator.cpp:auto solidity::test::fuzzer::mutator::SolidityGenerator::generateTestProgram()::$_2::operator()<std::__1::shared_ptr<solidity::test::fuzzer::mutator::SourceUnitGenerator> >(std::__1::shared_ptr<solidity::test::fuzzer::mutator::SourceUnitGenerator> const&) const
Unexecuted instantiation: SolidityGenerator.cpp:auto solidity::test::fuzzer::mutator::SolidityGenerator::generateTestProgram()::$_2::operator()<std::__1::shared_ptr<solidity::test::fuzzer::mutator::TestCaseGenerator> >(std::__1::shared_ptr<solidity::test::fuzzer::mutator::TestCaseGenerator> const&) const
208
0
    }, g);
209
0
  std::string program = generator<TestCaseGenerator>()->generate();
210
0
  destroyGenerators();
211
0
  return program;
212
0
}