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