Coverage Report

Created: 2026-06-30 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}