Merge pull request #12958 from ethereum/yul-fuzzer-bound-memory-accesses

Yul grammar generator: Bound memory accesses.
This commit is contained in:
Daniel 2023-02-07 15:47:20 +01:00 committed by GitHub
commit 665bf29a84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 310 additions and 36 deletions

View File

@ -6,8 +6,8 @@
} }
// ---- // ----
// Trace: // Trace:
// CREATE(0, 0xffffffffffffffffffffffffffffffffffffffff, 0) // CREATE(0, 0, 0)
// CREATE(0, 0xffffffffffffffffffffffffffffffffffffffff, 0) // CREATE(0, 0, 0)
// Memory dump: // Memory dump:
// 0: 0000000000000000000000000000000000000000000000000000000000000001 // 0: 0000000000000000000000000000000000000000000000000000000000000001
// Storage dump: // Storage dump:

View File

@ -8,8 +8,8 @@
// EVMVersion: >=constantinople // EVMVersion: >=constantinople
// ---- // ----
// Trace: // Trace:
// CREATE2(0, 0xffffffffffffffffffffffffffffffffffffffff, 0, 0) // CREATE2(0, 0, 0, 0)
// CREATE2(0, 0xffffffffffffffffffffffffffffffffffffffff, 0, 0) // CREATE2(0, 0, 0, 0)
// Memory dump: // Memory dump:
// 0: 0000000000000000000000000000000000000000000000000000000000000001 // 0: 0000000000000000000000000000000000000000000000000000000000000001
// Storage dump: // Storage dump:

View File

@ -0,0 +1,40 @@
{
returndatacopy(1, 1, 0)
calldatacopy(1, 1, 0)
extcodecopy(1, 1, 1, 0)
codecopy(1, 1, 0)
log0(1, 0)
log1(1, 0, 1)
log2(1, 0, 1, 1)
log3(1, 0, 1, 1, 1)
log4(1, 0, 1, 1, 1, 1)
pop(create(1, 1, 0))
pop(create2(1, 1, 0, 1))
pop(call(1, 1, 1, 1, 0, 1, 0))
pop(callcode(1, 1, 1, 1, 0, 1, 0))
pop(delegatecall(1, 1, 1, 0, 1, 0))
pop(staticcall(1, 1, 1, 0, 1, 0))
return(1, 0)
}
// ====
// EVMVersion: >=constantinople
// ----
// Trace:
// RETURNDATACOPY(0, 1, 0)
// CALLDATACOPY(0, 1, 0)
// EXTCODECOPY(1, 0, 1, 0)
// CODECOPY(0, 1, 0)
// LOG0(0, 0)
// LOG1(0, 0, 1)
// LOG2(0, 0, 1, 1)
// LOG3(0, 0, 1, 1, 1)
// LOG4(0, 0, 1, 1, 1, 1)
// CREATE(1, 0, 0)
// CREATE2(1, 0, 0, 1)
// CALL(1, 1, 1, 0, 0, 1, 0)
// CALLCODE(1, 1, 1, 0, 0, 1, 0)
// DELEGATECALL(1, 1, 0, 0, 1, 0)
// STATICCALL(1, 1, 0, 0, 1, 0)
// RETURN(0, 0)
// Memory dump:
// Storage dump:

View File

@ -0,0 +1,40 @@
{
returndatacopy(1, 1, 0)
calldatacopy(1, 1, 0)
extcodecopy(1, 1, 1, 0)
codecopy(1, 1, 0)
log0(1, 0)
log1(1, 0, 1)
log2(1, 0, 1, 1)
log3(1, 0, 1, 1, 1)
log4(1, 0, 1, 1, 1, 1)
pop(create(1, 1, 0))
pop(create2(1, 1, 0, 1))
pop(call(1, 1, 1, 1, 0, 1, 0))
pop(callcode(1, 1, 1, 1, 0, 1, 0))
pop(delegatecall(1, 1, 1, 0, 1, 0))
pop(staticcall(1, 1, 1, 0, 1, 0))
revert(1, 0)
}
// ====
// EVMVersion: >=constantinople
// ----
// Trace:
// RETURNDATACOPY(0, 1, 0)
// CALLDATACOPY(0, 1, 0)
// EXTCODECOPY(1, 0, 1, 0)
// CODECOPY(0, 1, 0)
// LOG0(0, 0)
// LOG1(0, 0, 1)
// LOG2(0, 0, 1, 1)
// LOG3(0, 0, 1, 1, 1)
// LOG4(0, 0, 1, 1, 1, 1)
// CREATE(1, 0, 0)
// CREATE2(1, 0, 0, 1)
// CALL(1, 1, 1, 0, 0, 1, 0)
// CALLCODE(1, 1, 1, 0, 0, 1, 0)
// DELEGATECALL(1, 1, 0, 0, 1, 0)
// STATICCALL(1, 1, 0, 0, 1, 0)
// REVERT(0, 0)
// Memory dump:
// Storage dump:

View File

@ -5,6 +5,6 @@
} }
// ---- // ----
// Trace: // Trace:
// CALLDATACOPY(32, 0, 0) // CALLDATACOPY(0, 0, 0)
// Memory dump: // Memory dump:
// Storage dump: // Storage dump:

View File

@ -0,0 +1,45 @@
{
returndatacopy(1, 1, 0)
calldatacopy(1, 1, 0)
extcodecopy(1, 1, 1, 0)
codecopy(1, 1, 0)
log0(1, 0)
log1(1, 0, 1)
log2(1, 0, 1, 1)
log3(1, 0, 1, 1, 1)
log4(1, 0, 1, 1, 1, 1)
pop(create(1, 1, 0))
pop(create2(1, 1, 0, 1))
pop(call(1, 1, 1, 1, 0, 1, 0))
pop(callcode(1, 1, 1, 1, 0, 1, 0))
pop(delegatecall(1, 1, 1, 0, 1, 0))
pop(staticcall(1, 1, 1, 0, 1, 0))
return(1, 0)
}
// ====
// EVMVersion: >=constantinople
// ----
// step: loadResolver
//
// {
// {
// let _1 := 0
// let _2 := 1
// returndatacopy(0, _2, _1)
// calldatacopy(0, _2, _1)
// extcodecopy(_2, 0, _2, _1)
// codecopy(0, _2, _1)
// log0(0, _1)
// log1(0, _1, _2)
// log2(0, _1, _2, _2)
// log3(0, _1, _2, _2, _2)
// log4(0, _1, _2, _2, _2, _2)
// pop(create(_2, 0, _1))
// pop(create2(_2, 0, _1, _2))
// pop(call(_2, _2, _2, 0, _1, _2, _1))
// pop(callcode(_2, _2, _2, 0, _1, _2, _1))
// pop(delegatecall(_2, _2, 0, _1, _2, _1))
// pop(staticcall(_2, _2, 0, _1, _2, _1))
// return(0, _1)
// }
// }

View File

@ -351,9 +351,22 @@ void ProtoConverter::visit(BinaryOp const& _x)
break; break;
} }
m_output << "("; m_output << "(";
visit(_x.left()); if (op == BinaryOp::KECCAK)
m_output << ","; {
visit(_x.right()); m_output << "mod(";
visit(_x.left());
m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")";
m_output << ",";
m_output << "mod(";
visit(_x.right());
m_output << ", " << to_string(s_maxSize) << ")";
}
else
{
visit(_x.left());
m_output << ",";
visit(_x.right());
}
m_output << ")"; m_output << ")";
} }
@ -623,7 +636,14 @@ void ProtoConverter::visit(UnaryOp const& _x)
break; break;
} }
m_output << "("; m_output << "(";
visit(_x.operand()); if (op == UnaryOp::MLOAD)
{
m_output << "mod(";
visit(_x.operand());
m_output << ", " << to_string(s_maxMemory) << ")";
}
else
visit(_x.operand());
m_output << ")"; m_output << ")";
} }
@ -778,11 +798,15 @@ void ProtoConverter::visit(CopyFunc const& _x)
break; break;
} }
m_output << "("; m_output << "(";
m_output << "mod(";
visit(_x.target()); visit(_x.target());
m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")";
m_output << ", "; m_output << ", ";
visit(_x.source()); visit(_x.source());
m_output << ", "; m_output << ", ";
m_output << "mod(";
visit(_x.size()); visit(_x.size());
m_output << ", " << to_string(s_maxSize) << ")";
m_output << ")\n"; m_output << ")\n";
} }
@ -792,32 +816,42 @@ void ProtoConverter::visit(ExtCodeCopy const& _x)
m_output << "("; m_output << "(";
visit(_x.addr()); visit(_x.addr());
m_output << ", "; m_output << ", ";
m_output << "mod(";
visit(_x.target()); visit(_x.target());
m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")";
m_output << ", "; m_output << ", ";
visit(_x.source()); visit(_x.source());
m_output << ", "; m_output << ", ";
m_output << "mod(";
visit(_x.size()); visit(_x.size());
m_output << ", " << to_string(s_maxSize) << ")";
m_output << ")\n"; m_output << ")\n";
} }
void ProtoConverter::visit(LogFunc const& _x) void ProtoConverter::visit(LogFunc const& _x)
{ {
auto visitPosAndSize = [&](LogFunc const& _y) {
m_output << "mod(";
visit(_y.pos());
m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")";
m_output << ", ";
m_output << "mod(";
visit(_y.size());
m_output << ", " << to_string(s_maxSize) << ")";
};
switch (_x.num_topics()) switch (_x.num_topics())
{ {
case LogFunc::ZERO: case LogFunc::ZERO:
m_output << "log0"; m_output << "log0";
m_output << "("; m_output << "(";
visit(_x.pos()); visitPosAndSize(_x);
m_output << ", ";
visit(_x.size());
m_output << ")\n"; m_output << ")\n";
break; break;
case LogFunc::ONE: case LogFunc::ONE:
m_output << "log1"; m_output << "log1";
m_output << "("; m_output << "(";
visit(_x.pos()); visitPosAndSize(_x);
m_output << ", ";
visit(_x.size());
m_output << ", "; m_output << ", ";
visit(_x.t1()); visit(_x.t1());
m_output << ")\n"; m_output << ")\n";
@ -825,9 +859,7 @@ void ProtoConverter::visit(LogFunc const& _x)
case LogFunc::TWO: case LogFunc::TWO:
m_output << "log2"; m_output << "log2";
m_output << "("; m_output << "(";
visit(_x.pos()); visitPosAndSize(_x);
m_output << ", ";
visit(_x.size());
m_output << ", "; m_output << ", ";
visit(_x.t1()); visit(_x.t1());
m_output << ", "; m_output << ", ";
@ -837,9 +869,7 @@ void ProtoConverter::visit(LogFunc const& _x)
case LogFunc::THREE: case LogFunc::THREE:
m_output << "log3"; m_output << "log3";
m_output << "("; m_output << "(";
visit(_x.pos()); visitPosAndSize(_x);
m_output << ", ";
visit(_x.size());
m_output << ", "; m_output << ", ";
visit(_x.t1()); visit(_x.t1());
m_output << ", "; m_output << ", ";
@ -851,9 +881,7 @@ void ProtoConverter::visit(LogFunc const& _x)
case LogFunc::FOUR: case LogFunc::FOUR:
m_output << "log4"; m_output << "log4";
m_output << "("; m_output << "(";
visit(_x.pos()); visitPosAndSize(_x);
m_output << ", ";
visit(_x.size());
m_output << ", "; m_output << ", ";
visit(_x.t1()); visit(_x.t1());
m_output << ", "; m_output << ", ";
@ -1015,13 +1043,21 @@ void ProtoConverter::visit(LowLevelCall const& _x)
visit(_x.wei()); visit(_x.wei());
m_output << ", "; m_output << ", ";
} }
m_output << "mod(";
visit(_x.in()); visit(_x.in());
m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")";
m_output << ", "; m_output << ", ";
m_output << "mod(";
visit(_x.insize()); visit(_x.insize());
m_output << ", " << to_string(s_maxSize) << ")";
m_output << ", "; m_output << ", ";
m_output << "mod(";
visit(_x.out()); visit(_x.out());
m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")";
m_output << ", "; m_output << ", ";
m_output << "mod(";
visit(_x.outsize()); visit(_x.outsize());
m_output << ", " << to_string(s_maxSize) << ")";
m_output << ")"; m_output << ")";
} }
@ -1048,9 +1084,13 @@ void ProtoConverter::visit(Create const& _x)
} }
visit(_x.wei()); visit(_x.wei());
m_output << ", "; m_output << ", ";
m_output << "mod(";
visit(_x.position()); visit(_x.position());
m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")";
m_output << ", "; m_output << ", ";
m_output << "mod(";
visit(_x.size()); visit(_x.size());
m_output << ", " << to_string(s_maxSize) << ")";
if (type == Create::CREATE2) if (type == Create::CREATE2)
{ {
m_output << ", "; m_output << ", ";
@ -1069,7 +1109,8 @@ void ProtoConverter::visit(IfStmt const& _x)
void ProtoConverter::visit(StoreFunc const& _x) void ProtoConverter::visit(StoreFunc const& _x)
{ {
switch (_x.st()) auto storeType = _x.st();
switch (storeType)
{ {
case StoreFunc::MSTORE: case StoreFunc::MSTORE:
m_output << "mstore("; m_output << "mstore(";
@ -1081,7 +1122,15 @@ void ProtoConverter::visit(StoreFunc const& _x)
m_output << "mstore8("; m_output << "mstore8(";
break; break;
} }
visit(_x.loc()); // Write to memory within bounds, storage is unbounded
if (storeType == StoreFunc::SSTORE)
visit(_x.loc());
else
{
m_output << "mod(";
visit(_x.loc());
m_output << ", " << to_string(s_maxMemory) << ")";
}
m_output << ", "; m_output << ", ";
visit(_x.val()); visit(_x.val());
m_output << ")\n"; m_output << ")\n";
@ -1262,9 +1311,13 @@ void ProtoConverter::visit(RetRevStmt const& _x)
break; break;
} }
m_output << "("; m_output << "(";
m_output << "mod(";
visit(_x.pos()); visit(_x.pos());
m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")";
m_output << ", "; m_output << ", ";
m_output << "mod(";
visit(_x.size()); visit(_x.size());
m_output << ", " << to_string(s_maxSize) << ")";
m_output << ")\n"; m_output << ")\n";
} }
@ -1651,8 +1704,12 @@ void ProtoConverter::fillFunctionCallInput(unsigned _numInParams)
m_output << "calldataload(" << slot << ")"; m_output << "calldataload(" << slot << ")";
break; break;
case 1: case 1:
{
// Access memory within stipulated bounds
slot = "mod(" + dictionaryToken() + ", " + to_string(s_maxMemory) + ")";
m_output << "mload(" << slot << ")"; m_output << "mload(" << slot << ")";
break; break;
}
case 2: case 2:
m_output << "sload(" << slot << ")"; m_output << "sload(" << slot << ")";
break; break;

View File

@ -344,6 +344,11 @@ private:
static unsigned constexpr s_modOutputParams = 5; static unsigned constexpr s_modOutputParams = 5;
/// Hard-coded identifier for a Yul object's data block /// Hard-coded identifier for a Yul object's data block
static auto constexpr s_dataIdentifier = "datablock"; static auto constexpr s_dataIdentifier = "datablock";
/// Upper bound on memory writes = 2**32 - 1
/// See: https://eips.ethereum.org/EIPS/eip-1985#memory-size
static unsigned constexpr s_maxMemory = 4294967295;
/// Upper bound on size for range copy functions
static unsigned constexpr s_maxSize = 65536;
/// Predicate to keep track of for body scope. If false, break/continue /// Predicate to keep track of for body scope. If false, break/continue
/// statements can not be created. /// statements can not be created.
bool m_inForBodyScope; bool m_inForBodyScope;

View File

@ -211,22 +211,22 @@ u256 EVMInstructionInterpreter::eval(
case Instruction::CALLDATASIZE: case Instruction::CALLDATASIZE:
return m_state.calldata.size(); return m_state.calldata.size();
case Instruction::CALLDATACOPY: case Instruction::CALLDATACOPY:
logTrace(_instruction, arg);
if (accessMemory(arg[0], arg[2])) if (accessMemory(arg[0], arg[2]))
copyZeroExtended( copyZeroExtended(
m_state.memory, m_state.calldata, m_state.memory, m_state.calldata,
size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) size_t(arg[0]), size_t(arg[1]), size_t(arg[2])
); );
logTrace(_instruction, arg);
return 0; return 0;
case Instruction::CODESIZE: case Instruction::CODESIZE:
return m_state.code.size(); return m_state.code.size();
case Instruction::CODECOPY: case Instruction::CODECOPY:
logTrace(_instruction, arg);
if (accessMemory(arg[0], arg[2])) if (accessMemory(arg[0], arg[2]))
copyZeroExtended( copyZeroExtended(
m_state.memory, m_state.code, m_state.memory, m_state.code,
size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) size_t(arg[0]), size_t(arg[1]), size_t(arg[2])
); );
logTrace(_instruction, arg);
return 0; return 0;
case Instruction::GASPRICE: case Instruction::GASPRICE:
return m_state.gasprice; return m_state.gasprice;
@ -239,23 +239,23 @@ u256 EVMInstructionInterpreter::eval(
case Instruction::EXTCODEHASH: case Instruction::EXTCODEHASH:
return u256(keccak256(h256(arg[0] + 1))); return u256(keccak256(h256(arg[0] + 1)));
case Instruction::EXTCODECOPY: case Instruction::EXTCODECOPY:
logTrace(_instruction, arg);
if (accessMemory(arg[1], arg[3])) if (accessMemory(arg[1], arg[3]))
// TODO this way extcodecopy and codecopy do the same thing. // TODO this way extcodecopy and codecopy do the same thing.
copyZeroExtended( copyZeroExtended(
m_state.memory, m_state.code, m_state.memory, m_state.code,
size_t(arg[1]), size_t(arg[2]), size_t(arg[3]) size_t(arg[1]), size_t(arg[2]), size_t(arg[3])
); );
logTrace(_instruction, arg);
return 0; return 0;
case Instruction::RETURNDATASIZE: case Instruction::RETURNDATASIZE:
return m_state.returndata.size(); return m_state.returndata.size();
case Instruction::RETURNDATACOPY: case Instruction::RETURNDATACOPY:
logTrace(_instruction, arg);
if (accessMemory(arg[0], arg[2])) if (accessMemory(arg[0], arg[2]))
copyZeroExtended( copyZeroExtended(
m_state.memory, m_state.returndata, m_state.memory, m_state.returndata,
size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) size_t(arg[0]), size_t(arg[1]), size_t(arg[2])
); );
logTrace(_instruction, arg);
return 0; return 0;
case Instruction::BLOCKHASH: case Instruction::BLOCKHASH:
if (arg[0] >= m_state.blockNumber || arg[0] + 256 < m_state.blockNumber) if (arg[0] >= m_state.blockNumber || arg[0] + 256 < m_state.blockNumber)
@ -319,11 +319,17 @@ u256 EVMInstructionInterpreter::eval(
case Instruction::CREATE: case Instruction::CREATE:
accessMemory(arg[1], arg[2]); accessMemory(arg[1], arg[2]);
logTrace(_instruction, arg); logTrace(_instruction, arg);
return (0xcccccc + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff"); if (arg[2] != 0)
return (0xcccccc + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff");
else
return 0xcccccc;
case Instruction::CREATE2: case Instruction::CREATE2:
accessMemory(arg[1], arg[2]); accessMemory(arg[1], arg[2]);
logTrace(_instruction, arg); logTrace(_instruction, arg);
return (0xdddddd + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff"); if (arg[2] != 0)
return (0xdddddd + arg[1]) & u256("0xffffffffffffffffffffffffffffffffffffffff");
else
return 0xdddddd;
case Instruction::CALL: case Instruction::CALL:
case Instruction::CALLCODE: case Instruction::CALLCODE:
accessMemory(arg[3], arg[4]); accessMemory(arg[3], arg[4]);
@ -340,7 +346,6 @@ u256 EVMInstructionInterpreter::eval(
accessMemory(arg[2], arg[3]); accessMemory(arg[2], arg[3]);
accessMemory(arg[4], arg[5]); accessMemory(arg[4], arg[5]);
logTrace(_instruction, arg); logTrace(_instruction, arg);
// Randomly fail based on the called address if it isn't a call to self. // Randomly fail based on the called address if it isn't a call to self.
// Used for fuzzing. // Used for fuzzing.
return ( return (
@ -359,7 +364,6 @@ u256 EVMInstructionInterpreter::eval(
accessMemory(arg[0], arg[1]); accessMemory(arg[0], arg[1]);
logTrace(_instruction, arg); logTrace(_instruction, arg);
m_state.storage.clear(); m_state.storage.clear();
m_state.trace.clear();
BOOST_THROW_EXCEPTION(ExplicitlyTerminated()); BOOST_THROW_EXCEPTION(ExplicitlyTerminated());
case Instruction::INVALID: case Instruction::INVALID:
logTrace(_instruction); logTrace(_instruction);
@ -479,7 +483,10 @@ u256 EVMInstructionInterpreter::evalBuiltin(
else if (fun == "datacopy") else if (fun == "datacopy")
{ {
// This is identical to codecopy. // This is identical to codecopy.
if (accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2))) if (
_evaluatedArguments.at(2) != 0 &&
accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2))
)
copyZeroExtended( copyZeroExtended(
m_state.memory, m_state.memory,
m_state.code, m_state.code,
@ -560,8 +567,13 @@ void EVMInstructionInterpreter::logTrace(
if (!(_writesToMemory && memWriteTracingDisabled())) if (!(_writesToMemory && memWriteTracingDisabled()))
{ {
string message = _pseudoInstruction + "("; string message = _pseudoInstruction + "(";
std::pair<bool, size_t> inputMemoryPtrModified = isInputMemoryPtrModified(_pseudoInstruction, _arguments);
for (size_t i = 0; i < _arguments.size(); ++i) for (size_t i = 0; i < _arguments.size(); ++i)
message += (i > 0 ? ", " : "") + formatNumber(_arguments[i]); {
bool printZero = inputMemoryPtrModified.first && inputMemoryPtrModified.second == i;
u256 arg = printZero ? 0 : _arguments[i];
message += (i > 0 ? ", " : "") + formatNumber(arg);
}
message += ")"; message += ")";
if (!_data.empty()) if (!_data.empty())
message += " [" + util::toHex(_data) + "]"; message += " [" + util::toHex(_data) + "]";
@ -573,3 +585,65 @@ void EVMInstructionInterpreter::logTrace(
} }
} }
} }
std::pair<bool, size_t> EVMInstructionInterpreter::isInputMemoryPtrModified(
std::string const& _pseudoInstruction,
std::vector<u256> const& _arguments
)
{
if (_pseudoInstruction == "RETURN" || _pseudoInstruction == "REVERT")
{
if (_arguments[1] == 0)
return {true, 0};
else
return {false, 0};
}
else if (
_pseudoInstruction == "RETURNDATACOPY" || _pseudoInstruction == "CALLDATACOPY"
|| _pseudoInstruction == "CODECOPY")
{
if (_arguments[2] == 0)
return {true, 0};
else
return {false, 0};
}
else if (_pseudoInstruction == "EXTCODECOPY")
{
if (_arguments[3] == 0)
return {true, 1};
else
return {false, 0};
}
else if (
_pseudoInstruction == "LOG0" || _pseudoInstruction == "LOG1" || _pseudoInstruction == "LOG2"
|| _pseudoInstruction == "LOG3" || _pseudoInstruction == "LOG4")
{
if (_arguments[1] == 0)
return {true, 0};
else
return {false, 0};
}
if (_pseudoInstruction == "CREATE" || _pseudoInstruction == "CREATE2")
{
if (_arguments[2] == 0)
return {true, 1};
else
return {false, 0};
}
if (_pseudoInstruction == "CALL" || _pseudoInstruction == "CALLCODE")
{
if (_arguments[4] == 0)
return {true, 3};
else
return {false, 0};
}
else if (_pseudoInstruction == "DELEGATECALL" || _pseudoInstruction == "STATICCALL")
{
if (_arguments[3] == 0)
return {true, 2};
else
return {false, 0};
}
else
return {false, 0};
}

View File

@ -125,6 +125,19 @@ private:
std::vector<u256> const& _arguments = {}, std::vector<u256> const& _arguments = {},
bytes const& _data = {} bytes const& _data = {}
); );
/// @returns a pair of boolean and size_t whose first value is true if @param _pseudoInstruction
/// is a Yul instruction that the Yul optimizer's loadResolver step rewrites the input
/// memory pointer value to zero if that instruction's read length (contained within @param
// _arguments) is zero, and whose second value is the positional index of the input memory
// pointer argument.
/// If the Yul instruction is unaffected or affected but read length is non-zero, the first
/// value is false.
std::pair<bool, size_t> isInputMemoryPtrModified(
std::string const& _pseudoInstruction,
std::vector<u256> const& _arguments
);
/// @returns disable trace flag. /// @returns disable trace flag.
bool memWriteTracingDisabled() bool memWriteTracingDisabled()
{ {