/src/solidity/test/tools/ossfuzz/protoToYul.h
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 <cstdint> |
20 | | #include <cstddef> |
21 | | #include <string> |
22 | | #include <ostream> |
23 | | #include <sstream> |
24 | | #include <stack> |
25 | | #include <set> |
26 | | #include <vector> |
27 | | #include <tuple> |
28 | | |
29 | | #include <test/tools/ossfuzz/yulProto.pb.h> |
30 | | |
31 | | #include <libsolutil/Common.h> |
32 | | #include <libsolutil/FixedHash.h> |
33 | | #include <libsolutil/Whiskers.h> |
34 | | |
35 | | #include <liblangutil/EVMVersion.h> |
36 | | |
37 | | namespace solidity::yul::test::yul_fuzzer |
38 | | { |
39 | | class ProtoConverter |
40 | | { |
41 | | public: |
42 | | ProtoConverter( |
43 | | bool _filterStatefulInstructions = false, |
44 | | bool _filterUnboundedLoops = false |
45 | | ) |
46 | 58.4k | { |
47 | 58.4k | m_funcVars = std::vector<std::vector<std::vector<std::string>>>{}; |
48 | 58.4k | m_globalVars = std::vector<std::vector<std::string>>{}; |
49 | 58.4k | m_inForBodyScope = false; |
50 | 58.4k | m_inForInitScope = false; |
51 | 58.4k | m_inForCond = false; |
52 | 58.4k | m_numNestedForLoops = 0; |
53 | 58.4k | m_numForLoops = 0; |
54 | 58.4k | m_counter = 0; |
55 | 58.4k | m_inputSize = 0; |
56 | 58.4k | m_inFunctionDef = false; |
57 | 58.4k | m_objectId = 0; |
58 | 58.4k | m_isObject = false; |
59 | 58.4k | m_forInitScopeExtEnabled = true; |
60 | 58.4k | m_filterStatefulInstructions = _filterStatefulInstructions; |
61 | 58.4k | m_filterUnboundedLoops = _filterUnboundedLoops; |
62 | 58.4k | } |
63 | | ProtoConverter(ProtoConverter const&) = delete; |
64 | | ProtoConverter(ProtoConverter&&) = delete; |
65 | | std::string programToString(Program const& _input); |
66 | | |
67 | | /// Returns evm version |
68 | | solidity::langutil::EVMVersion version() |
69 | 50.0k | { |
70 | 50.0k | return m_evmVersion; |
71 | 50.0k | } |
72 | | private: |
73 | | void visit(BinaryOp const&); |
74 | | |
75 | | /// Visits a basic block optionally adding @a _funcParams to scope. |
76 | | /// @param _block Reference to a basic block of Yul statements. |
77 | | /// @param _funcParams List of function parameter names, defaults to |
78 | | /// an empty vector. |
79 | | void visit(Block const& _block); |
80 | | |
81 | | std::string visit(Literal const&); |
82 | | void visit(VarRef const&); |
83 | | void visit(Expression const&); |
84 | | void visit(VarDecl const&); |
85 | | void visit(MultiVarDecl const&); |
86 | | void visit(TypedVarDecl const&); |
87 | | void visit(UnaryOp const&); |
88 | | void visit(AssignmentStatement const&); |
89 | | void visit(IfStmt const&); |
90 | | void visit(StoreFunc const&); |
91 | | void visit(Statement const&); |
92 | | void visit(ForStmt const&); |
93 | | void visit(BoundedForStmt const&); |
94 | | void visit(CaseStmt const&); |
95 | | void visit(SwitchStmt const&); |
96 | | void visit(TernaryOp const&); |
97 | | void visit(NullaryOp const&); |
98 | | void visit(LogFunc const&); |
99 | | void visit(CopyFunc const&); |
100 | | void visit(ExtCodeCopy const&); |
101 | | void visit(StopInvalidStmt const&); |
102 | | void visit(RetRevStmt const&); |
103 | | void visit(SelfDestructStmt const&); |
104 | | void visit(TerminatingStmt const&); |
105 | | /// @param _f is the function call to be visited. |
106 | | /// @param _name is the name of the function called. |
107 | | /// @param _expression is a flag that is true if the function is called |
108 | | /// as a single-value expression, false otherwise. |
109 | | void visit( |
110 | | FunctionCall const& _f, |
111 | | std::string const& _name, |
112 | | bool _expression = false |
113 | | ); |
114 | | void visit(FunctionDef const&); |
115 | | void visit(PopStmt const&); |
116 | | void visit(LeaveStmt const&); |
117 | | void visit(LowLevelCall const&); |
118 | | void visit(Create const&); |
119 | | void visit(UnaryOpData const&); |
120 | | void visit(Object const&); |
121 | | void visit(Data const&); |
122 | | void visit(Code const&); |
123 | | void visit(Program const&); |
124 | | |
125 | | /// Creates a new block scope. |
126 | | void openBlockScope(); |
127 | | /// Creates a new function scope, and adds @a _funcParams to it if it |
128 | | /// is non-empty. |
129 | | void openFunctionScope(std::vector<std::string> const& _funcParams); |
130 | | /// Closes current block scope |
131 | | void closeBlockScope(); |
132 | | /// Closes current function scope |
133 | | void closeFunctionScope(); |
134 | | /// Adds @a _vars to current scope |
135 | | void addVarsToScope(std::vector<std::string> const& _vars); |
136 | | |
137 | | std::string createHex(std::string const& _hexBytes); |
138 | | |
139 | | /// Returns a new variable name. |
140 | | std::string newVarName() |
141 | 88.6k | { |
142 | 88.6k | return "x_" + std::to_string(counter()); |
143 | 88.6k | } |
144 | | |
145 | | /// Accepts an arbitrary string, removes all characters that are neither |
146 | | /// alphabets nor digits from it and returns the said string. |
147 | | static std::string createAlphaNum(std::string const& _strBytes); |
148 | | |
149 | | enum class NumFunctionReturns: unsigned |
150 | | { |
151 | | None = 0, |
152 | | Single, |
153 | | Multiple |
154 | | }; |
155 | | |
156 | | void visitFunctionInputParams(FunctionCall const&, unsigned); |
157 | | void createFunctionDefAndCall(FunctionDef const&, unsigned, unsigned); |
158 | | |
159 | | /// Convert function type to a string to be used while naming a |
160 | | /// function that is created by a function declaration statement. |
161 | | /// @param _type Type classified according to the number of |
162 | | /// values returned by function. |
163 | | /// @return A string as follows. If _type is |
164 | | /// None -> "n" |
165 | | /// Single -> "s" |
166 | | /// Multiple -> "m" |
167 | | static std::string functionTypeToString(NumFunctionReturns _type); |
168 | | |
169 | | /// Builds a single vector containing variables declared in |
170 | | /// function scope. |
171 | | void consolidateVarDeclsInFunctionDef(); |
172 | | |
173 | | /// Builds a single vector containing variables declared in |
174 | | /// global scope. |
175 | | void consolidateGlobalVarDecls(); |
176 | | |
177 | | /// Return true if at least one variable declaration is in scope, |
178 | | /// false otherwise. |
179 | | /// @return True in the following cases: |
180 | | /// - If we are inside a function that has already declared a variable |
181 | | /// - If there is at least one variable declaration that is |
182 | | /// in scope |
183 | | bool varDeclAvailable(); |
184 | | |
185 | | /// Converts protobuf function call to a Yul function call and appends |
186 | | /// it to output stream. |
187 | | /// @param _x Protobuf function call |
188 | | /// @param _name Function name |
189 | | /// @param _numInParams Number of input arguments accepted by function |
190 | | /// @param _newLine Flag that prints a new line to the output stream if |
191 | | /// true. Default value for the flag is true. |
192 | | void convertFunctionCall( |
193 | | FunctionCall const& _x, |
194 | | std::string const& _name, |
195 | | unsigned _numInParams, |
196 | | bool _newLine = true |
197 | | ); |
198 | | |
199 | | /// Prints a Yul formatted variable declaration statement to the output |
200 | | /// stream. |
201 | | /// Example 1: createVarDecls(0, 1, true) returns {"x_0"} and prints |
202 | | /// let x_0 := |
203 | | /// Example 2: createVarDecls(0, 2, false) returns {"x_0", "x_1"} and prints |
204 | | /// let x_0, x_1 |
205 | | /// @param _start Start index of variable (inclusive) |
206 | | /// @param _end End index of variable (exclusive) |
207 | | /// @param _isAssignment Flag indicating if variable declaration is also |
208 | | /// an assignment. If true, the string " := " follows the variable |
209 | | /// declaration. Otherwise, a new line is follows the variable |
210 | | /// declaration. |
211 | | /// @return A vector of strings containing the variable names used in |
212 | | /// the declaration statement. |
213 | | std::vector<std::string> createVarDecls(unsigned _start, unsigned _end, bool _isAssignment); |
214 | | |
215 | | /// Prints comma separated variable names to output stream and |
216 | | /// returns a vector containing the printed variable names. |
217 | | /// Example: createVars(0, 2) returns {"x_0", "x_1"} and prints |
218 | | /// x_0, x_1 |
219 | | /// @param _startIdx Start index of variable (inclusive) |
220 | | /// @param _endIdx End index of variable (exclusive) |
221 | | /// @return A vector of strings containing the printed variable names. |
222 | | std::vector<std::string> createVars(unsigned _startIdx, unsigned _endIdx); |
223 | | |
224 | | /// Manages scope of Yul variables |
225 | | /// @param _varNames is a list of Yul variable names whose scope needs |
226 | | /// to be tracked according to Yul scoping rules. |
227 | | void scopeVariables(std::vector<std::string> const& _varNames); |
228 | | |
229 | | /// Print the Yul syntax to make a call to a function named @a _funcName to |
230 | | /// the output stream. |
231 | | /// @param _funcName Name of the function to be called |
232 | | /// @param _numInParams Number of input parameters in function signature |
233 | | /// @param _numOutParams Number of output parameters in function signature |
234 | | void createFunctionCall(std::string const& _funcName, unsigned _numInParams, unsigned _numOutParams); |
235 | | |
236 | | /// Print the Yul syntax to pass input arguments to a function that has |
237 | | /// @a _numInParams number of input parameters to the output stream. |
238 | | /// The input arguments are pseudo-randomly chosen from calldata, memory, |
239 | | /// storage, or the Yul optimizer hex dictionary. |
240 | | /// @param _numInParams Number of input arguments to fill |
241 | | void fillFunctionCallInput(unsigned _numInParams); |
242 | | |
243 | | /// Print the Yul syntax to save values returned by a function call |
244 | | /// to the output stream. The values are either stored to memory or |
245 | | /// storage based on a simulated coin flip. The saved location is |
246 | | /// decided pseudo-randomly. |
247 | | /// @param _varsVec A vector of strings that reference variables |
248 | | /// holding the return values of a function call. |
249 | | void saveFunctionCallOutput(std::vector<std::string> const& _varsVec); |
250 | | |
251 | | /// Register a function declaration |
252 | | /// @param _f Pointer to a FunctionDef object |
253 | | void registerFunction(FunctionDef const* _f); |
254 | | |
255 | | /// Removes entry from m_functionMap and m_functionName |
256 | | void updateFunctionMaps(std::string const& _x); |
257 | | |
258 | | /// Build a tree of objects that contains the object/data |
259 | | /// identifiers that are in scope in a given object. |
260 | | /// @param _x root object of the Yul protobuf specification. |
261 | | void buildObjectScopeTree(Object const& _x); |
262 | | |
263 | | /// Returns a pseudo-random dictionary token. |
264 | | /// @param _p Enum that decides if the returned token is hex prefixed ("0x") or not |
265 | | /// @return Dictionary token at the index computed using a |
266 | | /// monotonically increasing counter as follows: |
267 | | /// index = (m_inputSize * m_inputSize + counter) % dictionarySize |
268 | | /// where m_inputSize is the size of the protobuf input and |
269 | | /// dictionarySize is the total number of entries in the dictionary. |
270 | | std::string dictionaryToken(util::HexPrefix _p = util::HexPrefix::Add); |
271 | | |
272 | | /// Returns an EVMVersion object corresponding to the protobuf |
273 | | /// enum of type Program_Version |
274 | | static solidity::langutil::EVMVersion evmVersionMapping(Program_Version const& _x); |
275 | | |
276 | | /// @returns name of Yul function with return type of @param _numReturns. |
277 | | std::optional<std::string> functionExists(NumFunctionReturns _numReturns); |
278 | | |
279 | | /// Returns a monotonically increasing counter that starts from zero. |
280 | | unsigned counter() |
281 | 3.16M | { |
282 | 3.16M | return m_counter++; |
283 | 3.16M | } |
284 | | |
285 | | /// Generate function name of the form "foo_<typeSuffix>_<counter>". |
286 | | /// @param _type Type classified according to the number of |
287 | | /// values returned by function. |
288 | | std::string functionName(NumFunctionReturns _type) |
289 | 147k | { |
290 | 147k | return "foo_" + functionTypeToString(_type) + "_" + std::to_string(counter()); |
291 | 147k | } |
292 | | |
293 | | /// Returns a pseudo-randomly chosen object identifier that is in the |
294 | | /// scope of the Yul object being visited. |
295 | | std::string getObjectIdentifier(unsigned _x); |
296 | | |
297 | | /// Return new object identifier as string. Identifier string |
298 | | /// is a template of the form "\"object<n>\"" where <n> is |
299 | | /// a monotonically increasing object ID counter. |
300 | | /// @param _decorate If true (default value), object ID is |
301 | | /// enclosed within double quotes. |
302 | | std::string newObjectId(bool _decorate = true) |
303 | 142k | { |
304 | 142k | return util::Whiskers(R"(<?decorate>"</decorate>object<id><?decorate>"</decorate>)") |
305 | 142k | ("decorate", _decorate) |
306 | 142k | ("id", std::to_string(m_objectId++)) |
307 | 142k | .render(); |
308 | 142k | } |
309 | | |
310 | | /// Returns the object counter value corresponding to the object |
311 | | /// being visited. |
312 | | unsigned currentObjectId() const |
313 | 11.5k | { |
314 | 11.5k | return m_objectId - 1; |
315 | 11.5k | } |
316 | | |
317 | | std::ostringstream m_output; |
318 | | /// Variables in all function definitions |
319 | | std::vector<std::vector<std::vector<std::string>>> m_funcVars; |
320 | | /// Variables in current function definition |
321 | | std::vector<std::string const*> m_currentFuncVars; |
322 | | /// Variables in global scope |
323 | | std::vector<std::string const*> m_currentGlobalVars; |
324 | | /// Functions in current scope |
325 | | std::vector<std::vector<std::string>> m_scopeFuncs; |
326 | | /// Global variables |
327 | | std::vector<std::vector<std::string>> m_globalVars; |
328 | | /// Variables declared in for loop init block that is in global scope |
329 | | std::vector<std::vector<std::string>> m_globalForLoopInitVars; |
330 | | /// Variables declared in for loop init block that is in function scope |
331 | | std::vector<std::vector<std::vector<std::string>>> m_funcForLoopInitVars; |
332 | | /// Vector of function names |
333 | | std::vector<std::string> m_functions; |
334 | | /// Maps FunctionDef object to its name |
335 | | std::map<FunctionDef const*, std::string> m_functionDefMap; |
336 | | // Set that is used for deduplicating switch case literals |
337 | | std::stack<std::set<u256>> m_switchLiteralSetPerScope; |
338 | | // Look-up table per function type that holds the number of input (output) function parameters |
339 | | std::map<std::string, std::pair<unsigned, unsigned>> m_functionSigMap; |
340 | | /// Map of object name to list of sub-object namespace(s) in scope |
341 | | std::map<std::string, std::vector<std::string>> m_objectScope; |
342 | | // mod input/output parameters impose an upper bound on the number of input/output parameters a function may have. |
343 | | static unsigned constexpr s_modInputParams = 5; |
344 | | static unsigned constexpr s_modOutputParams = 5; |
345 | | /// Hard-coded identifier for a Yul object's data block |
346 | | static auto constexpr s_dataIdentifier = "datablock"; |
347 | | /// Upper bound on memory writes is 64KB in order to |
348 | | /// preserve semantic equivalence in the presence of |
349 | | /// memory guard. Note that s_maxMemory must be much larger |
350 | | /// than s_maxSize to create tests without significant overlap |
351 | | /// of I/O memory regions. |
352 | | static unsigned constexpr s_maxMemory = 65536; |
353 | | /// Upper bound on size for range copy functions |
354 | | static unsigned constexpr s_maxSize = 32768; |
355 | | /// Predicate to keep track of for body scope. If false, break/continue |
356 | | /// statements can not be created. |
357 | | bool m_inForBodyScope; |
358 | | /// Maximum number of for loops that a test case may contain |
359 | | static auto constexpr s_maxForLoops = 2; |
360 | | // Index used for naming loop variable of bounded for loops |
361 | | unsigned m_numNestedForLoops; |
362 | | /// Counter for number of for loops |
363 | | unsigned m_numForLoops; |
364 | | /// Predicate to keep track of for loop init scope. If true, variable |
365 | | /// or function declarations can not be created. |
366 | | bool m_inForInitScope; |
367 | | /// Flag that is true while converting for loop condition, |
368 | | /// false otherwise. |
369 | | bool m_inForCond; |
370 | | /// Monotonically increasing counter |
371 | | unsigned m_counter; |
372 | | /// Size of protobuf input |
373 | | unsigned m_inputSize; |
374 | | /// Predicate that is true if inside function definition, false otherwise |
375 | | bool m_inFunctionDef; |
376 | | /// Index used for naming objects |
377 | | unsigned m_objectId; |
378 | | /// Flag to track whether program is an object (true) or a statement block |
379 | | /// (false: default value) |
380 | | bool m_isObject; |
381 | | /// Flag to track whether scope extension of variables defined in for-init |
382 | | /// block is enabled. |
383 | | bool m_forInitScopeExtEnabled; |
384 | | /// Object that holds the targeted evm version specified by protobuf input |
385 | | solidity::langutil::EVMVersion m_evmVersion; |
386 | | /// Flag that, if set, stops the converter from generating state changing |
387 | | /// opcodes. |
388 | | bool m_filterStatefulInstructions; |
389 | | /// Flat that, if set, stops the converter from generating potentially |
390 | | /// unbounded loops. |
391 | | bool m_filterUnboundedLoops; |
392 | | }; |
393 | | } |