Randomize calldataload and storage slots and use dictionary tokens as function argument

This commit is contained in:
Bhargava Shastry 2019-09-03 15:14:17 +02:00
parent d066ba71a4
commit fce65ec811
2 changed files with 78 additions and 15 deletions

View File

@ -1078,17 +1078,67 @@ void ProtoConverter::registerFunction(FunctionDef const* _x)
m_functionDefMap.emplace(make_pair(_x, funcName)); m_functionDefMap.emplace(make_pair(_x, funcName));
} }
void ProtoConverter::fillFunctionCallInput(unsigned _numInParams)
{
for (unsigned i = 0; i < _numInParams; i++)
{
// Throw a 4-sided dice to choose whether to populate function input
// argument from a pseudo-randomly chosen slot in one of the following
// locations: calldata, memory, storage, or yul optimizer dictionary.
unsigned diceValue = counter() % 4;
// Pseudo-randomly choose one of the first ten 32-byte
// aligned slots.
string slot = to_string((counter() % 10) * 32);
switch (diceValue)
{
case 0:
m_output << "calldataload(" << slot << ")";
break;
case 1:
m_output << "mload(" << slot << ")";
break;
case 2:
m_output << "sload(" << slot << ")";
break;
case 3:
// Call to dictionaryToken() automatically picks a token
// at a pseudo-random location.
m_output << dictionaryToken();
break;
}
if (i < _numInParams - 1)
m_output << ",";
}
}
void ProtoConverter::saveFunctionCallOutput(vector<string> const& _varsVec)
{
for (auto const& var: _varsVec)
{
// Flip a dice to choose whether to save output values
// in storage or memory.
bool coinFlip = counter() % 2 == 0;
// Pseudo-randomly choose one of the first ten 32-byte
// aligned slots.
string slot = to_string((counter() % 10) * 32);
if (coinFlip)
m_output << "sstore(" << slot << ", " << var << ")\n";
else
m_output << "mstore(" << slot << ", " << var << ")\n";
}
}
void ProtoConverter::createFunctionCall( void ProtoConverter::createFunctionCall(
string _funcName, string _funcName,
unsigned _numInParams, unsigned _numInParams,
unsigned _numOutParams unsigned _numOutParams
) )
{ {
// Prints the following to output stream "let x_i,...,x_n := "
unsigned startIdx = counter();
vector<string> varsVec{}; vector<string> varsVec{};
if (_numOutParams > 0) if (_numOutParams > 0)
{ {
unsigned startIdx = counter();
// Prints the following to output stream "let x_i,...,x_n := "
varsVec = createVarDecls( varsVec = createVarDecls(
startIdx, startIdx,
startIdx + _numOutParams, startIdx + _numOutParams,
@ -1096,25 +1146,23 @@ void ProtoConverter::createFunctionCall(
); );
} }
// Call the function with the correct number of input parameters via calls to calldataload with // Call the function with the correct number of input parameters
// incremental addresses.
m_output << _funcName << "("; m_output << _funcName << "(";
for (unsigned i = 0; i < _numInParams; i++) if (_numInParams > 0)
{ fillFunctionCallInput(_numInParams);
m_output << "calldataload(" << std::to_string(i*32) << ")";
if (i < _numInParams - 1)
m_output << ",";
}
m_output << ")\n"; m_output << ")\n";
// Save output values to storage
for (unsigned i = 0; i < varsVec.size(); i++)
m_output << "sstore(" << std::to_string(i*32) << ", " << varsVec[i] << ")\n";
// Add newly minted vars to current scope
if (!varsVec.empty()) if (!varsVec.empty())
{
// Save values returned by function so that they are reflected
// in the interpreter trace.
saveFunctionCallOutput(varsVec);
// Add newly minted vars to current scope
addVarsToScope(varsVec); addVarsToScope(varsVec);
} }
else
yulAssert(_numOutParams == 0, "Proto fuzzer: Function return value not saved");
}
void ProtoConverter::createFunctionDefAndCall( void ProtoConverter::createFunctionDefAndCall(
FunctionDef const& _x, FunctionDef const& _x,

View File

@ -208,6 +208,21 @@ private:
/// @param _numOutParams Number of output parameters in function signature /// @param _numOutParams Number of output parameters in function signature
void createFunctionCall(std::string _funcName, unsigned _numInParams, unsigned _numOutParams); void createFunctionCall(std::string _funcName, unsigned _numInParams, unsigned _numOutParams);
/// Print the yul syntax to pass input arguments to a function that has
/// @a _numInParams number of input parameters to the output stream.
/// The input arguments are pseudo-randomly chosen from calldata, memory,
/// storage, or the yul optimizer hex dictionary.
/// @param _numInParams Number of input arguments to fill
void fillFunctionCallInput(unsigned _numInParams);
/// Print the yul syntax to save values returned by a function call
/// to the output stream. The values are either stored to memory or
/// storage based on a simulated coin flip. The saved location is
/// decided pseudo-randomly.
/// @param _varsVec A vector of strings that reference variables
/// holding the return values of a function call.
void saveFunctionCallOutput(std::vector<std::string> const& _varsVec);
/// Register a function declaration /// Register a function declaration
/// @param _f Pointer to a FunctionDef object /// @param _f Pointer to a FunctionDef object
void registerFunction(FunctionDef const* _f); void registerFunction(FunctionDef const* _f);