Merge pull request #5942 from bshastry/sol-protofuzzer

LibProtobuf Mutator (LPM) based fuzzer for yul subset
This commit is contained in:
chriseth 2019-03-05 13:01:03 +01:00 committed by GitHub
commit 5ddf9e269f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 605 additions and 1 deletions

View File

@ -1,3 +1,4 @@
link_directories(/src/LPM/src /src/LPM/src/libfuzzer /src/LPM/external.protobuf/lib)
add_custom_target(ossfuzz) add_custom_target(ossfuzz)
add_dependencies(ossfuzz add_dependencies(ossfuzz
solc_opt_ossfuzz solc_opt_ossfuzz
@ -6,6 +7,9 @@ add_dependencies(ossfuzz
strictasm_diff_ossfuzz strictasm_diff_ossfuzz
) )
add_custom_target(ossfuzz_proto)
add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz)
#[[FuzzingEngine.a is provided by oss-fuzz's Dockerized build environment]] #[[FuzzingEngine.a is provided by oss-fuzz's Dockerized build environment]]
add_executable(solc_opt_ossfuzz solc_opt_ossfuzz.cpp ../fuzzer_common.cpp) add_executable(solc_opt_ossfuzz solc_opt_ossfuzz.cpp ../fuzzer_common.cpp)
target_link_libraries(solc_opt_ossfuzz PRIVATE libsolc evmasm FuzzingEngine.a) target_link_libraries(solc_opt_ossfuzz PRIVATE libsolc evmasm FuzzingEngine.a)
@ -17,4 +21,21 @@ add_executable(const_opt_ossfuzz const_opt_ossfuzz.cpp ../fuzzer_common.cpp)
target_link_libraries(const_opt_ossfuzz PRIVATE libsolc evmasm FuzzingEngine.a) target_link_libraries(const_opt_ossfuzz PRIVATE libsolc evmasm FuzzingEngine.a)
add_executable(strictasm_diff_ossfuzz strictasm_diff_ossfuzz.cpp yulFuzzerCommon.cpp) add_executable(strictasm_diff_ossfuzz strictasm_diff_ossfuzz.cpp yulFuzzerCommon.cpp)
target_link_libraries(strictasm_diff_ossfuzz PRIVATE libsolc evmasm yulInterpreter FuzzingEngine.a) target_link_libraries(strictasm_diff_ossfuzz PRIVATE libsolc evmasm yulInterpreter FuzzingEngine.a)
add_executable(yul_proto_ossfuzz yulProtoFuzzer.cpp protoToYul.cpp yulProto.pb.cc)
target_include_directories(yul_proto_ossfuzz PRIVATE /src/libprotobuf-mutator /src/LPM/external.protobuf/include)
target_link_libraries(yul_proto_ossfuzz PRIVATE yul evmasm
protobuf-mutator-libfuzzer.a
protobuf-mutator.a
protobuf.a
FuzzingEngine.a)
add_executable(yul_proto_diff_ossfuzz yulProto_diff_ossfuzz.cpp yulFuzzerCommon.cpp protoToYul.cpp yulProto.pb.cc)
target_include_directories(yul_proto_diff_ossfuzz PRIVATE /src/libprotobuf-mutator /src/LPM/external.protobuf/include)
target_link_libraries(yul_proto_diff_ossfuzz PRIVATE yul evmasm
yulInterpreter
protobuf-mutator-libfuzzer.a
protobuf-mutator.a
protobuf.a
FuzzingEngine.a)

View File

@ -0,0 +1,242 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <test/tools/ossfuzz/protoToYul.h>
#include <ostream>
#include <sstream>
using namespace std;
using namespace yul::test::yul_fuzzer;
ostream& yul::test::yul_fuzzer::operator<<(ostream& _os, Literal const& _x)
{
return _os << "(" << _x.val() << ")";
}
ostream& yul::test::yul_fuzzer::operator<<(ostream& _os, VarRef const& _x)
{
return _os << "x_" << (static_cast<uint32_t>(_x.varnum()) % 10);
}
ostream& yul::test::yul_fuzzer::operator<<(ostream& _os, Expression const& _x)
{
if (_x.has_varref())
return _os << _x.varref();
else if (_x.has_cons())
return _os << _x.cons();
else if (_x.has_binop())
return _os << _x.binop();
else if (_x.has_unop())
return _os << _x.unop();
return _os << "";
}
ostream& yul::test::yul_fuzzer::operator<<(ostream& _os, BinaryOp const& _x)
{
switch (_x.op())
{
case BinaryOp::ADD:
_os << "add(";
break;
case BinaryOp::SUB:
_os << "sub(";
break;
case BinaryOp::MUL:
_os << "mul(";
break;
case BinaryOp::DIV:
_os << "div(";
break;
case BinaryOp::MOD:
_os << "mod(";
break;
case BinaryOp::XOR:
_os << "xor(";
break;
case BinaryOp::AND:
_os << "and(";
break;
case BinaryOp::OR:
_os << "or(";
break;
case BinaryOp::EQ:
_os << "eq(";
break;
case BinaryOp::LT:
_os << "lt(";
break;
case BinaryOp::GT:
_os << "gt(";
break;
}
return _os << _x.left() << "," << _x.right() << ")";
}
// New var numbering starts from x_10 until x_16
ostream& yul::test::yul_fuzzer::operator<<(ostream& _os, VarDecl const& _x)
{
_os << "let x_" << ((_x.id() % 7) + 10) << " := " << _x.expr() << "\n";
return _os;
}
ostream& yul::test::yul_fuzzer::operator<<(ostream& _os, TypedVarDecl const& _x)
{
_os << "let x_" << ((_x.id() % 7) + 10);
switch (_x.type())
{
case TypedVarDecl::BOOL:
_os << ": bool := " << _x.expr() << " : bool\n";
break;
case TypedVarDecl::S8:
_os << ": s8 := " << _x.expr() << " : s8\n";
break;
case TypedVarDecl::S32:
_os << ": s32 := " << _x.expr() << " : s32\n";
break;
case TypedVarDecl::S64:
_os << ": s64 := " << _x.expr() << " : s64\n";
break;
case TypedVarDecl::S128:
_os << ": s128 := " << _x.expr() << " : s128\n";
break;
case TypedVarDecl::S256:
_os << ": s256 := " << _x.expr() << " : s256\n";
break;
case TypedVarDecl::U8:
_os << ": u8 := " << _x.expr() << " : u8\n";
break;
case TypedVarDecl::U32:
_os << ": u32 := " << _x.expr() << " : u32\n";
break;
case TypedVarDecl::U64:
_os << ": u64 := " << _x.expr() << " : u64\n";
break;
case TypedVarDecl::U128:
_os << ": u128 := " << _x.expr() << " : u128\n";
break;
case TypedVarDecl::U256:
_os << ": u256 := " << _x.expr() << " : u256\n";
break;
}
return _os;
}
ostream& yul::test::yul_fuzzer::operator<<(ostream& _os, UnaryOp const& _x)
{
switch (_x.op())
{
case UnaryOp::NOT:
_os << "not(";
break;
case UnaryOp::MLOAD:
_os << "mload(";
break;
case UnaryOp::SLOAD:
_os << "sload(";
break;
case UnaryOp::ISZERO:
_os << "iszero(";
break;
}
_os << _x.operand() << ")";
return _os;
}
ostream& yul::test::yul_fuzzer::operator<<(ostream& _os, AssignmentStatement const& _x)
{
return _os << _x.ref_id() << " := " << _x.expr() << "\n";
}
ostream& yul::test::yul_fuzzer::operator<<(ostream& _os, IfStmt const& _x)
{
return _os <<
"if " <<
_x.cond() <<
" " <<
_x.if_body();
}
ostream& yul::test::yul_fuzzer::operator<<(ostream& _os, StoreFunc const& _x)
{
switch (_x.st())
{
case StoreFunc::MSTORE:
_os << "mstore(" << _x.loc() << ", " << _x.val() << ")\n";
break;
case StoreFunc::SSTORE:
_os << "sstore(" << _x.loc() << ", " << _x.val() << ")\n";
break;
}
return _os;
}
ostream& yul::test::yul_fuzzer::operator<<(ostream& _os, Statement const& _x)
{
if (_x.has_decl())
return _os << _x.decl();
else if (_x.has_assignment())
return _os << _x.assignment();
else if (_x.has_ifstmt())
return _os << _x.ifstmt();
else if (_x.has_storage_func())
return _os << _x.storage_func();
else if (_x.has_blockstmt())
return _os << _x.blockstmt();
return _os << "";
}
ostream& yul::test::yul_fuzzer::operator<<(ostream& _os, Block const& _x)
{
if (_x.statements_size() > 0)
{
_os << "{\n";
for (auto const& st: _x.statements())
_os << st;
_os << "}\n";
}
return _os;
}
ostream& yul::test::yul_fuzzer::operator<<(ostream& _os, Function const& _x)
{
_os << "{\n"
<< "let a,b := foo(calldataload(0),calldataload(32),calldataload(64),calldataload(96),calldataload(128),"
<< "calldataload(160),calldataload(192),calldataload(224))\n"
<< "sstore(0, a)\n"
<< "sstore(32, b)\n"
<< "function foo(x_0, x_1, x_2, x_3, x_4, x_5, x_6, x_7) -> x_8, x_9\n"
<< _x.statements()
<< "}\n";
return _os;
}
string yul::test::yul_fuzzer::functionToString(Function const& _input)
{
ostringstream os;
os << _input;
return os.str();
}
string yul::test::yul_fuzzer::protoToYul(const uint8_t* _data, size_t _size)
{
Function message;
if (!message.ParsePartialFromArray(_data, _size))
return "#error invalid proto\n";
return functionToString(message);
}

View File

@ -0,0 +1,51 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdint>
#include <cstddef>
#include <string>
#include <test/tools/ossfuzz/yulProto.pb.h>
namespace yul
{
namespace test
{
namespace yul_fuzzer
{
class Function;
std::string functionToString(Function const& input);
std::string protoToYul(uint8_t const* data, size_t size);
std::ostream& operator<<(std::ostream& _os, BinaryOp const& _x);
std::ostream& operator<<(std::ostream& _os, Block const& _x);
std::ostream& operator<<(std::ostream& _os, Literal const& _x);
std::ostream& operator<<(std::ostream& _os, VarRef const& _x);
std::ostream& operator<<(std::ostream& _os, Expression const& _x);
std::ostream& operator<<(std::ostream& _os, BinaryOp const& _x);
std::ostream& operator<<(std::ostream& _os, VarDecl const& _x);
std::ostream& operator<<(std::ostream& _os, TypedVarDecl const& _x);
std::ostream& operator<<(std::ostream& _os, UnaryOp const& _x);
std::ostream& operator<<(std::ostream& _os, AssignmentStatement const& _x);
std::ostream& operator<<(std::ostream& _os, IfStmt const& _x);
std::ostream& operator<<(std::ostream& _os, StoreFunc const& _x);
std::ostream& operator<<(std::ostream& _os, Statement const& _x);
std::ostream& operator<<(std::ostream& _os, Block const& _x);
std::ostream& operator<<(std::ostream& _os, Function const& _x);
}
}
}

View File

@ -0,0 +1,156 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto2";
// VariableDeclaration =
// 'let' TypedIdentifierList ( ':=' Expression )?
// TypedIdentifierList = Identifier ':' TypeName ( ',' Identifier ':' TypeName )*
// Identifier = [a-zA-Z_$] [a-zA-Z_$0-9]*
// IdentifierList = Identifier ( ',' Identifier)*
// TypeName = Identifier | BuiltinTypeName
// BuiltinTypeName = 'bool' | [us] ( '8' | '32' | '64' | '128' | '256' )
message VarDecl {
required int32 id = 1;
required Expression expr = 3;
}
message TypedVarDecl {
enum TypeName {
BOOL = 1;
U8 = 2;
U32 = 3;
U64 = 4;
U128 = 5;
U256 = 6;
S8 = 7;
S32 = 8;
S64 = 9;
S128 = 10;
S256 = 11;
};
required int32 id = 1;
required TypeName type = 2;
required Expression expr = 3;
}
message VarRef {
required int32 varnum = 1;
}
message Literal {
required int32 val = 1;
}
message TypedLiteral {
enum TypeName {
BOOL = 1;
U8 = 2;
U32 = 3;
U64 = 4;
U128 = 5;
U256 = 6;
S8 = 7;
S32 = 8;
S64 = 9;
S128 = 10;
S256 = 11;
};
required int32 val = 1;
required TypeName type = 2;
}
message BinaryOp {
enum BOp {
ADD = 0;
SUB = 1;
MUL = 2;
DIV = 3;
MOD = 4;
XOR = 5;
AND = 6;
OR = 7;
EQ = 8;
LT = 9;
GT = 10;
};
required BOp op = 1;
required Expression left = 2;
required Expression right = 3;
}
message UnaryOp {
enum UOp {
NOT = 0;
MLOAD = 1;
SLOAD = 2;
ISZERO = 3;
}
required UOp op = 1;
required Expression operand = 2;
}
message StoreFunc {
enum Storage {
MSTORE = 0;
SSTORE = 1;
}
required Expression loc = 1;
required Expression val = 2;
required Storage st = 3;
}
message Expression {
oneof expr_oneof {
VarRef varref = 1;
Literal cons = 2;
BinaryOp binop = 3;
UnaryOp unop = 4;
}
}
message AssignmentStatement {
required VarRef ref_id = 1;
required Expression expr = 2;
}
message IfStmt {
required Expression cond = 1;
required Block if_body = 2;
}
// add for loop
// TODO: add block and scope for if
message Statement {
oneof stmt_oneof {
VarDecl decl = 1;
AssignmentStatement assignment = 2;
IfStmt ifstmt = 3;
StoreFunc storage_func = 4;
Block blockstmt = 5;
}
}
message Block {
repeated Statement statements = 1;
}
message Function {
required Block statements = 1;
}
package yul.test.yul_fuzzer;

View File

@ -0,0 +1,59 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fstream>
#include <test/tools/ossfuzz/yulProto.pb.h>
#include <test/tools/fuzzer_common.h>
#include <test/tools/ossfuzz/protoToYul.h>
#include <src/libfuzzer/libfuzzer_macro.h>
#include <libyul/AssemblyStack.h>
#include <liblangutil/EVMVersion.h>
#include <libyul/Exceptions.h>
using namespace yul;
using namespace yul::test::yul_fuzzer;
using namespace std;
DEFINE_BINARY_PROTO_FUZZER(Function const& _input)
{
string yul_source = functionToString(_input);
if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH"))
{
// With libFuzzer binary run this to generate a YUL source file x.yul:
// PROTO_FUZZER_DUMP_PATH=x.yul ./a.out proto-input
ofstream of(dump_path);
of.write(yul_source.data(), yul_source.size());
}
// AssemblyStack entry point
AssemblyStack stack(langutil::EVMVersion(), AssemblyStack::Language::StrictAssembly);
// Parse protobuf mutated YUL code
if (!stack.parseAndAnalyze("source", yul_source))
return;
yulAssert(stack.errors().empty(), "Parsed successfully but had errors.");
if (!stack.parserResult()->code || !stack.parserResult()->analysisInfo)
return;
// Optimize
stack.optimize();
}

View File

@ -0,0 +1,75 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fstream>
#include <test/tools/ossfuzz/yulProto.pb.h>
#include <test/tools/fuzzer_common.h>
#include <test/tools/ossfuzz/protoToYul.h>
#include <src/libfuzzer/libfuzzer_macro.h>
#include <libyul/AssemblyStack.h>
#include <liblangutil/EVMVersion.h>
#include <libyul/Exceptions.h>
#include <test/tools/ossfuzz/yulFuzzerCommon.h>
using namespace yul;
using namespace yul::test::yul_fuzzer;
using namespace std;
using namespace langutil;
using namespace dev;
using namespace yul::test;
DEFINE_BINARY_PROTO_FUZZER(Function const& _input)
{
string yul_source = functionToString(_input);
if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH"))
{
// With libFuzzer binary run this to generate a YUL source file x.yul:
// PROTO_FUZZER_DUMP_PATH=x.yul ./a.out proto-input
ofstream of(dump_path);
of.write(yul_source.data(), yul_source.size());
}
// AssemblyStack entry point
AssemblyStack stack(langutil::EVMVersion(), AssemblyStack::Language::StrictAssembly);
try
{
// Parse protobuf mutated YUL code
if (!stack.parseAndAnalyze("source", yul_source) || !stack.parserResult()->code ||
!stack.parserResult()->analysisInfo)
return;
}
catch (Exception const&)
{
return;
}
ostringstream os1;
ostringstream os2;
yulFuzzerUtil::interpret(os1, stack.parserResult()->code);
stack.optimize();
yulFuzzerUtil::interpret(os2, stack.parserResult()->code);
bool isTraceEq = (os1.str() == os2.str());
yulAssert(isTraceEq, "Interpreted traces for optimized and unoptimized code differ.");
return;
}