Merge pull request #8173 from ethereum/fix-7859

yul proto fuzzer: Add EVM version field
This commit is contained in:
chriseth 2020-01-28 22:14:25 +01:00 committed by GitHub
commit a788ba14f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 132 additions and 11 deletions

View File

@ -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())

View File

@ -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;
};
}

View File

@ -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;

View File

@ -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()
);

View File

@ -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)
);