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