mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8173 from ethereum/fix-7859
yul proto fuzzer: Add EVM version field
This commit is contained in:
commit
a788ba14f3
@ -30,6 +30,7 @@
|
||||
using namespace std;
|
||||
using namespace solidity::yul::test::yul_fuzzer;
|
||||
using namespace solidity::yul::test;
|
||||
using namespace solidity::langutil;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity;
|
||||
|
||||
@ -86,6 +87,29 @@ string ProtoConverter::createAlphaNum(string const& _strBytes)
|
||||
return tmp;
|
||||
}
|
||||
|
||||
EVMVersion ProtoConverter::evmVersionMapping(Program_Version const& _ver)
|
||||
{
|
||||
switch (_ver)
|
||||
{
|
||||
case Program::HOMESTEAD:
|
||||
return EVMVersion::homestead();
|
||||
case Program::TANGERINE:
|
||||
return EVMVersion::tangerineWhistle();
|
||||
case Program::SPURIOUS:
|
||||
return EVMVersion::spuriousDragon();
|
||||
case Program::BYZANTIUM:
|
||||
return EVMVersion::byzantium();
|
||||
case Program::CONSTANTINOPLE:
|
||||
return EVMVersion::constantinople();
|
||||
case Program::PETERSBURG:
|
||||
return EVMVersion::petersburg();
|
||||
case Program::ISTANBUL:
|
||||
return EVMVersion::istanbul();
|
||||
case Program::BERLIN:
|
||||
return EVMVersion::berlin();
|
||||
}
|
||||
}
|
||||
|
||||
string ProtoConverter::visit(Literal const& _x)
|
||||
{
|
||||
switch (_x.literal_oneof_case())
|
||||
@ -230,7 +254,16 @@ void ProtoConverter::visit(Expression const& _x)
|
||||
|
||||
void ProtoConverter::visit(BinaryOp const& _x)
|
||||
{
|
||||
switch (_x.op())
|
||||
BinaryOp_BOp op = _x.op();
|
||||
|
||||
if ((op == BinaryOp::SHL || op == BinaryOp::SHR || op == BinaryOp::SAR) &&
|
||||
!m_evmVersion.hasBitwiseShifting())
|
||||
{
|
||||
m_output << dictionaryToken();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case BinaryOp::ADD:
|
||||
m_output << "add";
|
||||
@ -266,12 +299,15 @@ void ProtoConverter::visit(BinaryOp const& _x)
|
||||
m_output << "gt";
|
||||
break;
|
||||
case BinaryOp::SHR:
|
||||
yulAssert(m_evmVersion.hasBitwiseShifting(), "Proto fuzzer: Invalid evm version");
|
||||
m_output << "shr";
|
||||
break;
|
||||
case BinaryOp::SHL:
|
||||
yulAssert(m_evmVersion.hasBitwiseShifting(), "Proto fuzzer: Invalid evm version");
|
||||
m_output << "shl";
|
||||
break;
|
||||
case BinaryOp::SAR:
|
||||
yulAssert(m_evmVersion.hasBitwiseShifting(), "Proto fuzzer: Invalid evm version");
|
||||
m_output << "sar";
|
||||
break;
|
||||
case BinaryOp::SDIV:
|
||||
@ -475,7 +511,17 @@ void ProtoConverter::visit(TypedVarDecl const& _x)
|
||||
|
||||
void ProtoConverter::visit(UnaryOp const& _x)
|
||||
{
|
||||
switch (_x.op())
|
||||
UnaryOp_UOp op = _x.op();
|
||||
|
||||
// Replace calls to extcodehash on unsupported EVMs with a dictionary
|
||||
// token.
|
||||
if (op == UnaryOp::EXTCODEHASH && !m_evmVersion.hasExtCodeHash())
|
||||
{
|
||||
m_output << dictionaryToken();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case UnaryOp::NOT:
|
||||
m_output << "not";
|
||||
@ -550,7 +596,12 @@ void ProtoConverter::visit(NullaryOp const& _x)
|
||||
m_output << "codesize()";
|
||||
break;
|
||||
case NullaryOp::RETURNDATASIZE:
|
||||
// If evm supports returndatasize, we generate it. Otherwise,
|
||||
// we output a dictionary token.
|
||||
if (m_evmVersion.supportsReturndata())
|
||||
m_output << "returndatasize()";
|
||||
else
|
||||
m_output << dictionaryToken();
|
||||
break;
|
||||
case NullaryOp::ADDRESS:
|
||||
m_output << "address()";
|
||||
@ -583,10 +634,20 @@ void ProtoConverter::visit(NullaryOp const& _x)
|
||||
m_output << "gaslimit()";
|
||||
break;
|
||||
case NullaryOp::SELFBALANCE:
|
||||
// Replace calls to selfbalance() on unsupported EVMs with a dictionary
|
||||
// token.
|
||||
if (m_evmVersion.hasSelfBalance())
|
||||
m_output << "selfbalance()";
|
||||
else
|
||||
m_output << dictionaryToken();
|
||||
break;
|
||||
case NullaryOp::CHAINID:
|
||||
// Replace calls to chainid() on unsupported EVMs with a dictionary
|
||||
// token.
|
||||
if (m_evmVersion.hasChainID())
|
||||
m_output << "chainid()";
|
||||
else
|
||||
m_output << dictionaryToken();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -600,6 +661,11 @@ void ProtoConverter::visit(CopyFunc const& _x)
|
||||
if (type == CopyFunc::DATA && !m_isObject)
|
||||
return;
|
||||
|
||||
// We don't generate code if the copy function is returndatacopy
|
||||
// and the underlying evm does not support it.
|
||||
if (type == CopyFunc::RETURNDATA && !m_evmVersion.supportsReturndata())
|
||||
return;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CopyFunc::CALLDATA:
|
||||
@ -609,6 +675,7 @@ void ProtoConverter::visit(CopyFunc const& _x)
|
||||
m_output << "codecopy";
|
||||
break;
|
||||
case CopyFunc::RETURNDATA:
|
||||
yulAssert(m_evmVersion.supportsReturndata(), "Proto fuzzer: Invalid evm version");
|
||||
m_output << "returndatacopy";
|
||||
break;
|
||||
case CopyFunc::DATA:
|
||||
@ -890,6 +957,16 @@ void ProtoConverter::visit(FunctionCall const& _x)
|
||||
void ProtoConverter::visit(LowLevelCall const& _x)
|
||||
{
|
||||
LowLevelCall_Type type = _x.callty();
|
||||
|
||||
// Generate staticcall if it is supported by the underlying evm
|
||||
if (type == LowLevelCall::STATICCALL && !m_evmVersion.hasStaticCall())
|
||||
{
|
||||
// Since staticcall is supposed to return 0 on success and 1 on
|
||||
// failure, we can use counter value to emulate it
|
||||
m_output << ((counter() % 2) ? "0" : "1");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case LowLevelCall::CALL:
|
||||
@ -902,6 +979,7 @@ void ProtoConverter::visit(LowLevelCall const& _x)
|
||||
m_output << "delegatecall(";
|
||||
break;
|
||||
case LowLevelCall::STATICCALL:
|
||||
yulAssert(m_evmVersion.hasStaticCall(), "Proto fuzzer: Invalid evm version");
|
||||
m_output << "staticcall(";
|
||||
break;
|
||||
}
|
||||
@ -927,6 +1005,15 @@ void ProtoConverter::visit(LowLevelCall const& _x)
|
||||
void ProtoConverter::visit(Create const& _x)
|
||||
{
|
||||
Create_Type type = _x.createty();
|
||||
|
||||
// Replace a call to create2 on unsupported EVMs with a dictionary
|
||||
// token.
|
||||
if (type == Create::CREATE2 && !m_evmVersion.hasCreate2())
|
||||
{
|
||||
m_output << dictionaryToken();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Create::CREATE:
|
||||
@ -1653,6 +1740,7 @@ void ProtoConverter::createFunctionDefAndCall(
|
||||
!m_inForInitScope,
|
||||
"Proto fuzzer: Trying to create function call inside for-init block"
|
||||
);
|
||||
if (_x.force_call())
|
||||
createFunctionCall(funcName, _numInParams, _numOutParams);
|
||||
}
|
||||
|
||||
@ -1737,6 +1825,9 @@ void ProtoConverter::visit(Program const& _x)
|
||||
// Initialize input size
|
||||
m_inputSize = _x.ByteSizeLong();
|
||||
|
||||
// Record EVM Version
|
||||
m_evmVersion = evmVersionMapping(_x.ver());
|
||||
|
||||
// Program is either a yul object or a block of
|
||||
// statements.
|
||||
switch (_x.program_oneof_case())
|
||||
|
@ -31,6 +31,8 @@
|
||||
#include <libsolutil/FixedHash.h>
|
||||
#include <libsolutil/Whiskers.h>
|
||||
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
|
||||
namespace solidity::yul::test::yul_fuzzer
|
||||
{
|
||||
class ProtoConverter
|
||||
@ -56,6 +58,12 @@ public:
|
||||
ProtoConverter(ProtoConverter&&) = delete;
|
||||
std::string programToString(Program const& _input);
|
||||
|
||||
/// Returns evm version
|
||||
solidity::langutil::EVMVersion version()
|
||||
{
|
||||
return m_evmVersion;
|
||||
}
|
||||
|
||||
private:
|
||||
void visit(BinaryOp const&);
|
||||
|
||||
@ -270,6 +278,10 @@ private:
|
||||
/// dictionarySize is the total number of entries in the dictionary.
|
||||
std::string dictionaryToken(util::HexPrefix _p = util::HexPrefix::Add);
|
||||
|
||||
/// Returns an EVMVersion object corresponding to the protobuf
|
||||
/// enum of type Program_Version
|
||||
solidity::langutil::EVMVersion evmVersionMapping(Program_Version const& _x);
|
||||
|
||||
/// Returns a monotonically increasing counter that starts from zero.
|
||||
unsigned counter()
|
||||
{
|
||||
@ -367,5 +379,7 @@ private:
|
||||
/// Flag to track whether scope extension of variables defined in for-init
|
||||
/// block is enabled.
|
||||
bool m_forInitScopeExtEnabled;
|
||||
/// Object that holds the targeted evm version specified by protobuf input
|
||||
solidity::langutil::EVMVersion m_evmVersion;
|
||||
};
|
||||
}
|
||||
|
@ -355,6 +355,7 @@ message FunctionDef {
|
||||
required uint32 num_input_params = 1;
|
||||
required uint32 num_output_params = 2;
|
||||
required Block block = 3;
|
||||
required bool force_call = 4;
|
||||
}
|
||||
|
||||
message PopStmt {
|
||||
@ -405,10 +406,21 @@ message Data {
|
||||
}
|
||||
|
||||
message Program {
|
||||
enum Version {
|
||||
HOMESTEAD = 0;
|
||||
TANGERINE = 1;
|
||||
SPURIOUS = 2;
|
||||
BYZANTIUM = 3;
|
||||
CONSTANTINOPLE = 4;
|
||||
PETERSBURG = 5;
|
||||
ISTANBUL = 6;
|
||||
BERLIN = 7;
|
||||
}
|
||||
oneof program_oneof {
|
||||
Block block = 1;
|
||||
Object obj = 2;
|
||||
}
|
||||
required Version ver = 3;
|
||||
}
|
||||
|
||||
package solidity.yul.test.yul_fuzzer;
|
||||
|
@ -29,12 +29,14 @@
|
||||
using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
using namespace solidity::yul::test::yul_fuzzer;
|
||||
using namespace solidity::langutil;
|
||||
using namespace std;
|
||||
|
||||
DEFINE_PROTO_FUZZER(Program const& _input)
|
||||
{
|
||||
ProtoConverter converter;
|
||||
string yul_source = converter.programToString(_input);
|
||||
EVMVersion version = converter.version();
|
||||
|
||||
if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH"))
|
||||
{
|
||||
@ -51,7 +53,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
||||
|
||||
// AssemblyStack entry point
|
||||
AssemblyStack stack(
|
||||
langutil::EVMVersion(),
|
||||
version,
|
||||
AssemblyStack::Language::StrictAssembly,
|
||||
solidity::frontend::OptimiserSettings::full()
|
||||
);
|
||||
|
@ -55,7 +55,9 @@ void printErrors(ostream& _stream, ErrorList const& _errors)
|
||||
|
||||
DEFINE_PROTO_FUZZER(Program const& _input)
|
||||
{
|
||||
string yul_source = ProtoConverter().programToString(_input);
|
||||
ProtoConverter converter;
|
||||
string yul_source = converter.programToString(_input);
|
||||
EVMVersion version = converter.version();
|
||||
|
||||
if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH"))
|
||||
{
|
||||
@ -69,7 +71,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
||||
|
||||
// AssemblyStack entry point
|
||||
AssemblyStack stack(
|
||||
langutil::EVMVersion::berlin(),
|
||||
version,
|
||||
AssemblyStack::Language::StrictAssembly,
|
||||
solidity::frontend::OptimiserSettings::full()
|
||||
);
|
||||
@ -87,7 +89,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
||||
yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret(
|
||||
os1,
|
||||
stack.parserResult()->code,
|
||||
EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion::berlin())
|
||||
EVMDialect::strictAssemblyForEVMObjects(version)
|
||||
);
|
||||
|
||||
if (termReason == yulFuzzerUtil::TerminationReason::StepLimitReached)
|
||||
@ -97,7 +99,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
||||
termReason = yulFuzzerUtil::interpret(
|
||||
os2,
|
||||
stack.parserResult()->code,
|
||||
EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion::berlin()),
|
||||
EVMDialect::strictAssemblyForEVMObjects(version),
|
||||
(yul::test::yul_fuzzer::yulFuzzerUtil::maxSteps * 4)
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user