Merge pull request #9373 from ethereum/develop

Merge develop into breaking.
This commit is contained in:
chriseth 2020-07-09 16:15:43 +02:00 committed by GitHub
commit 9ef050af9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 674 additions and 100 deletions

View File

@ -97,6 +97,7 @@ defaults:
- test/tools/ossfuzz/strictasm_diff_ossfuzz
- test/tools/ossfuzz/strictasm_opt_ossfuzz
- test/tools/ossfuzz/yul_proto_diff_ossfuzz
- test/tools/ossfuzz/yul_proto_diff_custom_mutate_ossfuzz
- test/tools/ossfuzz/yul_proto_ossfuzz
- test/tools/ossfuzz/sol_proto_ossfuzz

View File

@ -54,7 +54,7 @@ get_logfile_basename() {
echo -ne "${filename}"
}
BOOST_TEST_ARGS="--color_output=no --show_progress=yes --logger=JUNIT,error,test_results/`get_logfile_basename`.xml"
BOOST_TEST_ARGS="--color_output=no --show_progress=yes --logger=JUNIT,error,test_results/`get_logfile_basename`.xml ${BOOST_TEST_ARGS}"
SOLTEST_ARGS="--evm-version=$EVM $SOLTEST_FLAGS"
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --optimize"
test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2"

View File

@ -30,7 +30,7 @@ REPODIR="$(realpath $(dirname $0)/..)"
for OPTIMIZE in 0 1; do
for EVM in homestead byzantium constantinople petersburg istanbul; do
EVM=$EVM OPTIMIZE=$OPTIMIZE ${REPODIR}/.circleci/soltest.sh
EVM=$EVM OPTIMIZE=$OPTIMIZE BOOST_TEST_ARGS="-t !@nooptions" ${REPODIR}/.circleci/soltest.sh
done
done

2
.gitignore vendored
View File

@ -35,7 +35,7 @@ build/
build*/
emscripten_build/
docs/_build
docs/utils/__pycache__
__pycache__
docs/utils/*.pyc
/deps/downloads/
deps/install

View File

@ -26,7 +26,14 @@ Bugfixes:
### 0.6.12 (unreleased)
Compiler Features:
* Code Generator: Evaluate ``keccak256`` of string literals at compile-time.
Bugfixes:
* Type Checker: Fix overload resolution in combination with ``{value: ...}``.
Compiler Features:
* Optimizer: Add rule to remove shifts inside the byte opcode.
### 0.6.11 (2020-07-07)

View File

@ -571,6 +571,20 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
feasibilityFunction
});
rules.push_back({
Builtins::BYTE(A, Builtins::SHL(B, X)),
[=]() -> Pattern { return Builtins::BYTE(A.d() + B.d() / 8, X); },
false,
[=] { return B.d() % 8 == 0 && A.d() <= 32 && B.d() <= 256; }
});
rules.push_back({
Builtins::BYTE(A, Builtins::SHR(B, X)),
[=]() -> Pattern { return A.d() < B.d() / 8 ? Word(0) : Builtins::BYTE(A.d() - B.d() / 8, X); },
false,
[=] { return B.d() % 8 == 0 && A.d() < Pattern::WordSize / 8 && B.d() <= Pattern::WordSize; }
});
return rules;
}

View File

@ -2404,6 +2404,8 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions)
{
solAssert(_functionCallOptions.options().size() == _functionCallOptions.names().size(), "Lengths of name & value arrays differ!");
_functionCallOptions.expression().annotation().arguments = _functionCallOptions.annotation().arguments;
_functionCallOptions.expression().accept(*this);
auto expressionFunctionType = dynamic_cast<FunctionType const*>(type(_functionCallOptions.expression()));

View File

@ -935,11 +935,6 @@ void ArrayUtils::clearStorageLoop(TypePointer _type) const
}
// stack: end_pos pos
// jump to and return from the loop to allow for duplicate code removal
evmasm::AssemblyItem returnTag = _context.pushNewTag();
_context << Instruction::SWAP2 << Instruction::SWAP1;
// stack: <return tag> end_pos pos
evmasm::AssemblyItem loopStart = _context.appendJumpToNew();
_context << loopStart;
// check for loop condition
@ -959,11 +954,8 @@ void ArrayUtils::clearStorageLoop(TypePointer _type) const
_context.appendJumpTo(loopStart);
// cleanup
_context << zeroLoopEnd;
_context << Instruction::POP << Instruction::SWAP1;
// "return"
_context << Instruction::JUMP;
_context << Instruction::POP;
_context << returnTag;
solAssert(_context.stackHeight() == stackHeightStart - 1, "");
}
);

View File

@ -792,20 +792,24 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
TypePointer const& argType = arguments.front()->annotation().type;
solAssert(argType, "");
arguments.front()->accept(*this);
// Optimization: If type is bytes or string, then do not encode,
// but directly compute keccak256 on memory.
if (*argType == *TypeProvider::bytesMemory() || *argType == *TypeProvider::stringMemory())
if (auto const* stringLiteral = dynamic_cast<StringLiteralType const*>(argType))
// Optimization: Compute keccak256 on string literals at compile-time.
m_context << u256(keccak256(stringLiteral->value()));
else if (*argType == *TypeProvider::bytesMemory() || *argType == *TypeProvider::stringMemory())
{
// Optimization: If type is bytes or string, then do not encode,
// but directly compute keccak256 on memory.
ArrayUtils(m_context).retrieveLength(*TypeProvider::bytesMemory());
m_context << Instruction::SWAP1 << u256(0x20) << Instruction::ADD;
m_context << Instruction::KECCAK256;
}
else
{
utils().fetchFreeMemoryPointer();
utils().packedEncode({argType}, TypePointers());
utils().toSizeAfterFreeMemoryPointer();
m_context << Instruction::KECCAK256;
}
m_context << Instruction::KECCAK256;
break;
}
case FunctionType::Kind::Log0:

View File

@ -1020,18 +1020,28 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
ArrayType const* arrayType = TypeProvider::bytesMemory();
auto array = convert(*arguments[0], *arrayType);
if (auto const* stringLiteral = dynamic_cast<StringLiteralType const*>(arguments.front()->annotation().type))
{
// Optimization: Compute keccak256 on string literals at compile-time.
define(_functionCall) <<
("0x" + keccak256(stringLiteral->value()).hex()) <<
"\n";
}
else
{
auto array = convert(*arguments[0], *arrayType);
define(_functionCall) <<
"keccak256(" <<
m_utils.arrayDataAreaFunction(*arrayType) <<
"(" <<
array.commaSeparatedList() <<
"), " <<
m_utils.arrayLengthFunction(*arrayType) <<
"(" <<
array.commaSeparatedList() <<
"))\n";
define(_functionCall) <<
"keccak256(" <<
m_utils.arrayDataAreaFunction(*arrayType) <<
"(" <<
array.commaSeparatedList() <<
"), " <<
m_utils.arrayLengthFunction(*arrayType) <<
"(" <<
array.commaSeparatedList() <<
"))\n";
}
break;
}
case FunctionType::Kind::ArrayPop:

View File

@ -10,16 +10,18 @@ git fetch origin
error=0
for new_proof in $(git diff origin/develop --name-only test/formal/)
do
set +e
echo "Proving $new_proof..."
output=$(python3 "$new_proof")
result=$?
set -e
if [ -e "$new_proof" ]; then
set +e
echo "Proving $new_proof..."
output=$(python3 "$new_proof")
result=$?
set -e
if [[ "$result" != 0 ]]
then
echo "Proof $(basename "$new_proof" ".py") failed: $output."
error=1
if [[ "$result" != 0 ]]
then
echo "Proof $(basename "$new_proof" ".py") failed: $output."
error=1
fi
fi
done

View File

@ -0,0 +1,29 @@
from rule import Rule
from opcodes import *
"""
byte(A, shl(B, X))
given B % 8 == 0 && A <= 32 && B <= 256
->
byte(A + B / 8, X)
"""
rule = Rule()
n_bits = 256
# Input vars
X = BitVec('X', n_bits)
A = BitVec('A', n_bits)
B = BitVec('B', n_bits)
# Non optimized result
nonopt = BYTE(A, SHL(B, X))
# Optimized result
opt = BYTE(A + B / 8, X)
rule.require(B % 8 == 0)
rule.require(ULE(A, 32))
rule.require(ULE(B, 256))
rule.check(nonopt, opt)

View File

@ -0,0 +1,30 @@
from rule import Rule
from opcodes import *
"""
byte(A, shr(B, X))
given B % 8 == 0 && A < n_bits/8 && B <= n_bits && A >= B / 8
->
byte(A - B / 8, X)
"""
rule = Rule()
n_bits = 256
# Input vars
X = BitVec('X', n_bits)
A = BitVec('A', n_bits)
B = BitVec('B', n_bits)
# Non optimized result
nonopt = BYTE(A, SHR(B, X))
# Optimized result
opt = BYTE(A - B / 8, X)
rule.require(B % 8 == 0)
rule.require(ULT(A, n_bits/8))
rule.require(ULE(B, n_bits))
rule.require(UGE(A, DIV(B,8)))
rule.check(nonopt, opt)

View File

@ -0,0 +1,30 @@
from rule import Rule
from opcodes import *
"""
byte(A, shr(B, X))
given B % 8 == 0 && A < n_bits/8 && B <= n_bits && A < B / 8
->
0
"""
rule = Rule()
n_bits = 256
# Input vars
X = BitVec('X', n_bits)
A = BitVec('A', n_bits)
B = BitVec('B', n_bits)
# Non optimized result
nonopt = BYTE(A, SHR(B, X))
# Optimized result
opt = 0
rule.require(B % 8 == 0)
rule.require(ULT(A, n_bits/8))
rule.require(ULE(B, n_bits))
rule.require(ULT(A, DIV(B,8)))
rule.check(nonopt, opt)

View File

@ -56,3 +56,7 @@ def SHR(x, y):
def SAR(x, y):
return y >> x
def BYTE(i, x):
bit = (i + 1) * 8
return If(UGT(bit, x.size()), BitVecVal(0, x.size()), (LShR(x, (x.size() - bit))) & 0xff)

View File

@ -61,18 +61,40 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
evmasm::LinkerObject obj;
if (m_compileViaYul)
{
yul::AssemblyStack asmStack(
m_evmVersion,
yul::AssemblyStack::Language::StrictAssembly,
// Ignore optimiser settings here because we need Yul optimisation to
// get code that does not exhaust the stack.
OptimiserSettings::full()
);
bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName));
solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors");
// Try compiling twice: If the first run fails due to stack errors, forcefully enable
// the optimizer.
for (bool forceEnableOptimizer: {false, true})
{
OptimiserSettings optimiserSettings = m_optimiserSettings;
if (!forceEnableOptimizer && !optimiserSettings.runYulOptimiser)
{
// Enable some optimizations on the first run
optimiserSettings.runYulOptimiser = true;
optimiserSettings.yulOptimiserSteps = "uljmul jmul";
}
else if (forceEnableOptimizer)
optimiserSettings = OptimiserSettings::full();
asmStack.optimize();
obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode);
yul::AssemblyStack asmStack(
m_evmVersion,
yul::AssemblyStack::Language::StrictAssembly,
optimiserSettings
);
bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName));
solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors");
try
{
asmStack.optimize();
obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode);
break;
}
catch (...)
{
if (forceEnableOptimizer || optimiserSettings == OptimiserSettings::full())
throw;
}
}
}
else
obj = m_compiler.object(contractName);

View File

@ -16,6 +16,7 @@ contract C {
return (x[199], y[203][1], z[170].a[1], z[170].b[99]);
}
}
// ====
// compileViaYul: also
// ----
// f() -> "A", 8, 4, "B"

View File

@ -0,0 +1,17 @@
contract C {
function len() public returns (uint ret) {
uint[] memory data = new uint[](2);
data[0] = 234;
data[1] = 123;
delete data;
assembly {
ret := mload(data)
}
}
}
// ====
// compileViaYul: also
// ----
// len() -> 0

View File

@ -0,0 +1,40 @@
contract C {
uint[] data;
function len() public returns (uint ret) {
data.push(234);
data.push(123);
delete data;
assembly {
ret := sload(data_slot)
}
}
function val() public returns (uint ret) {
assembly {
sstore(0, 2)
mstore(0, 0)
sstore(keccak256(0, 32), 234)
sstore(add(keccak256(0, 32), 1), 123)
}
assert(data[0] == 234);
assert(data[1] == 123);
delete data;
uint size = 999;
assembly {
size := sload(0)
mstore(0, 0)
ret := sload(keccak256(0, 32))
}
}
}
// ====
// compileViaYul: also
// ----
// len() -> 0
// val() -> 0

View File

@ -0,0 +1,14 @@
contract C {
function f(uint x) external payable returns (uint) { return 1; }
function f(uint x, uint y) external payable returns (uint) { return 2; }
function call() public payable returns (uint v, uint x, uint y, uint z) {
v = this.f{value: 10}(2);
x = this.f{gas: 1000}(2, 3);
y = this.f{gas: 1000, value: 10}(2, 3);
z = this.f{gas: 1000}{value: 10}(2, 3);
}
receive() external payable {}
}
// ----
// (), 1 ether
// call() -> 1, 2, 2, 2

View File

@ -0,0 +1,44 @@
// This tests the optimizer rule
// byte(A, shl(B, X))
// ->
// byte(A + B / 8, X)
// given A <= 32 && B % 8 == 0 && B <= 256
//
// and the respective rule about shr
contract C {
function f(uint a) public returns (uint, uint, uint) {
uint x = a << (256 - 8);
assembly {
x := byte(0, x)
}
uint y = a << 8;
assembly {
y := byte(30, y)
}
uint z = a << 16;
assembly {
z := byte(1, z)
}
return (x, y, z);
}
function g(uint a) public returns (uint, uint, uint) {
uint x = a >> (256 - 16);
assembly {
x := byte(31, x)
}
uint y = a >> 8;
assembly {
y := byte(4, y)
}
uint z = a >> 16;
assembly {
z := byte(7, z)
}
return (x, y, z);
}
}
// ====
// compileViaYul: also
// ----
// f(uint256): 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f -> 0x1f, 0x1f, 3
// g(uint256): 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f -> 1, 3, 5

View File

@ -0,0 +1,19 @@
// tests compile-time evaluation of keccak256 on literal strings
contract C {
function short() public pure returns (bool) {
bytes32 a = keccak256("abcdefghijklmn");
bytes memory s = "abcdefghijklmn";
return a == keccak256(s);
}
bytes32 constant sc = keccak256("abcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmn");
function long() public pure returns (bool, bool) {
bytes32 a = keccak256("abcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmn");
bytes memory s = "abcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmnabcdefghijklmn";
return (a == keccak256(s), sc == keccak256(s));
}
}
// ====
// compileViaYul: also
// ----
// short() -> true
// long() -> true, true

View File

@ -1,9 +0,0 @@
contract C {
function f(uint x) external payable { }
function f(uint x, uint y) external payable { }
function call() internal {
this.f{value: 10}(2);
}
}
// ----
// TypeError 6675: (148-154): Member "f" not unique after argument-dependent lookup in contract C.

View File

@ -29,7 +29,7 @@ using namespace std;
namespace solidity::util::test
{
BOOST_AUTO_TEST_SUITE(IpfsHash)
BOOST_AUTO_TEST_SUITE(IpfsHash, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_CASE(test_small)
{

View File

@ -29,7 +29,7 @@ using namespace std;
namespace solidity::util::test
{
BOOST_AUTO_TEST_SUITE(IterateReplacing)
BOOST_AUTO_TEST_SUITE(IterateReplacing, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_CASE(no_replacement)
{

View File

@ -30,7 +30,7 @@ using namespace std;
namespace solidity::util::test
{
BOOST_AUTO_TEST_SUITE(JsonTest)
BOOST_AUTO_TEST_SUITE(JsonTest, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_CASE(json_pretty_print)
{

View File

@ -26,7 +26,7 @@ using namespace std;
namespace solidity::util::test
{
BOOST_AUTO_TEST_SUITE(Keccak256)
BOOST_AUTO_TEST_SUITE(Keccak256, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_CASE(empty)
{

View File

@ -59,7 +59,7 @@ T valueOf(LazyInit<T> _lazyInit)
}
BOOST_AUTO_TEST_SUITE(LazyInitTests)
BOOST_AUTO_TEST_SUITE(LazyInitTests, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_CASE(default_constructed_is_empty)
{

View File

@ -33,7 +33,7 @@ using namespace std;
namespace solidity::util::test
{
BOOST_AUTO_TEST_SUITE(StringUtils)
BOOST_AUTO_TEST_SUITE(StringUtils, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_CASE(test_similarity)
{

View File

@ -31,7 +31,7 @@ using namespace std;
namespace solidity::util::test
{
BOOST_AUTO_TEST_SUITE(SwarmHash)
BOOST_AUTO_TEST_SUITE(SwarmHash, *boost::unit_test::label("nooptions"))
string bzzr0HashHex(string const& _input)
{

View File

@ -30,7 +30,7 @@ using namespace std;
namespace solidity::util::test
{
BOOST_AUTO_TEST_SUITE(UTF8)
BOOST_AUTO_TEST_SUITE(UTF8, *boost::unit_test::label("nooptions"))
namespace {

View File

@ -29,7 +29,7 @@ using namespace std;
namespace solidity::util::test
{
BOOST_AUTO_TEST_SUITE(WhiskersTest)
BOOST_AUTO_TEST_SUITE(WhiskersTest, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_CASE(no_templates)
{

View File

@ -43,7 +43,7 @@ string assemble(string const& _input)
}
}
BOOST_AUTO_TEST_SUITE(StackReuseCodegen)
BOOST_AUTO_TEST_SUITE(StackReuseCodegen, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_CASE(smoke_test)
{

View File

@ -10,7 +10,12 @@ add_dependencies(ossfuzz
if (OSSFUZZ)
add_custom_target(ossfuzz_proto)
add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz sol_proto_ossfuzz)
add_dependencies(ossfuzz_proto
sol_proto_ossfuzz
yul_proto_ossfuzz
yul_proto_diff_ossfuzz
yul_proto_diff_custom_mutate_ossfuzz
)
add_custom_target(ossfuzz_abiv2)
add_dependencies(ossfuzz_abiv2 abiv2_proto_ossfuzz)
@ -60,6 +65,22 @@ if (OSSFUZZ)
)
set_target_properties(yul_proto_diff_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
add_executable(yul_proto_diff_custom_mutate_ossfuzz
yulProto_diff_ossfuzz.cpp
yulFuzzerCommon.cpp
protoToYul.cpp
yulProto.pb.cc
protomutators/YulProtoMutator.cpp
)
target_include_directories(yul_proto_diff_custom_mutate_ossfuzz PRIVATE /usr/include/libprotobuf-mutator)
target_link_libraries(yul_proto_diff_custom_mutate_ossfuzz PRIVATE yul
yulInterpreter
protobuf-mutator-libfuzzer.a
protobuf-mutator.a
protobuf.a
)
set_target_properties(yul_proto_diff_custom_mutate_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
add_executable(abiv2_proto_ossfuzz
../../EVMHost.cpp
abiV2ProtoFuzzer.cpp

View File

@ -178,6 +178,14 @@ bool ProtoConverter::functionCallNotPossible(FunctionCall_Returns _type)
(_type == FunctionCall::MULTIASSIGN && !varDeclAvailable());
}
unsigned ProtoConverter::numVarsInScope()
{
if (m_inFunctionDef)
return m_currentFuncVars.size();
else
return m_currentGlobalVars.size();
}
void ProtoConverter::visit(VarRef const& _x)
{
if (m_inFunctionDef)
@ -827,18 +835,18 @@ void ProtoConverter::visitFunctionInputParams(FunctionCall const& _x, unsigned _
case 4:
visit(_x.in_param4());
m_output << ", ";
BOOST_FALLTHROUGH;
[[fallthrough]];
case 3:
visit(_x.in_param3());
m_output << ", ";
BOOST_FALLTHROUGH;
[[fallthrough]];
case 2:
visit(_x.in_param2());
m_output << ", ";
BOOST_FALLTHROUGH;
[[fallthrough]];
case 1:
visit(_x.in_param1());
BOOST_FALLTHROUGH;
[[fallthrough]];
case 0:
break;
default:
@ -963,23 +971,43 @@ void ProtoConverter::visit(FunctionCall const& _x)
"Proto fuzzer: Function call with too many output params encountered."
);
// Return early if numOutParams > number of available variables
if (numOutParams > numVarsInScope())
return;
// Copy variables in scope in order to prevent repeated references
vector<string> variables;
if (m_inFunctionDef)
for (auto var: m_currentFuncVars)
variables.push_back(*var);
else
for (auto var: m_currentGlobalVars)
variables.push_back(*var);
auto refVar = [](vector<string>& _var, unsigned _rand, bool _comma = true) -> string
{
auto index = _rand % _var.size();
string ref = _var[index];
_var.erase(_var.begin() + index);
if (_comma)
ref += ", ";
return ref;
};
// Convert LHS of multi assignment
// We reverse the order of out param visits since the order does not matter.
// This helps reduce the size of this switch statement.
switch (numOutParams)
{
case 4:
visit(_x.out_param4());
m_output << ", ";
BOOST_FALLTHROUGH;
m_output << refVar(variables, _x.out_param4().varnum());
[[fallthrough]];
case 3:
visit(_x.out_param3());
m_output << ", ";
BOOST_FALLTHROUGH;
m_output << refVar(variables, _x.out_param3().varnum());
[[fallthrough]];
case 2:
visit(_x.out_param2());
m_output << ", ";
visit(_x.out_param1());
m_output << refVar(variables, _x.out_param2().varnum());
m_output << refVar(variables, _x.out_param1().varnum(), false);
break;
default:
yulAssert(false, "Proto fuzzer: Function call with too many or too few input parameters.");

View File

@ -120,6 +120,8 @@ private:
void closeFunctionScope();
/// Adds @a _vars to current scope
void addVarsToScope(std::vector<std::string> const& _vars);
/// @returns number of variables that are in scope
unsigned numVarsInScope();
std::string createHex(std::string const& _hexBytes);

View File

@ -0,0 +1,147 @@
#include <test/tools/ossfuzz/protomutators/YulProtoMutator.h>
#include <libyul/Exceptions.h>
#include <src/text_format.h>
using namespace solidity::yul::test::yul_fuzzer;
using namespace protobuf_mutator;
using namespace std;
using YPM = YulProtoMutator;
MutationInfo::MutationInfo(ProtobufMessage const* _message, string const& _info):
ScopeGuard([&]{ exitInfo(); }),
m_protobufMsg(_message)
{
writeLine("----------------------------------");
writeLine("YULMUTATOR: " + _info);
writeLine("Before");
writeLine(SaveMessageAsText(*m_protobufMsg));
}
void MutationInfo::exitInfo()
{
writeLine("After");
writeLine(SaveMessageAsText(*m_protobufMsg));
}
/// Initialize deterministic PRNG.
static YulRandomNumGenerator s_rand(1337);
/// Add m/sstore(0, variable)
static LPMPostProcessor<Block> addStoreToZero(
[](Block* _message, unsigned _seed)
{
if (_seed % YPM::s_highIP == 0)
{
MutationInfo m{_message, "Added store to zero"};
auto storeStmt = new StoreFunc();
storeStmt->set_st(YPM::EnumTypeConverter<StoreFunc_Storage>{}.enumFromSeed(s_rand()));
storeStmt->set_allocated_loc(YPM::litExpression(0));
storeStmt->set_allocated_val(YPM::refExpression(s_rand));
auto stmt = _message->add_statements();
stmt->set_allocated_storage_func(storeStmt);
}
}
);
Literal* YPM::intLiteral(unsigned _value)
{
auto lit = new Literal();
lit->set_intval(_value);
return lit;
}
VarRef* YPM::varRef(unsigned _seed)
{
auto varref = new VarRef();
varref->set_varnum(_seed);
return varref;
}
Expression* YPM::refExpression(YulRandomNumGenerator& _rand)
{
auto refExpr = new Expression();
refExpr->set_allocated_varref(varRef(_rand()));
return refExpr;
}
Expression* YPM::litExpression(unsigned _value)
{
auto lit = intLiteral(_value);
auto expr = new Expression();
expr->set_allocated_cons(lit);
return expr;
}
template <typename T>
T YPM::EnumTypeConverter<T>::validEnum(unsigned _seed)
{
auto ret = static_cast<T>(_seed % (enumMax() - enumMin() + 1) + enumMin());
if constexpr (std::is_same_v<std::decay_t<T>, FunctionCall_Returns>)
yulAssert(FunctionCall_Returns_IsValid(ret), "Yul proto mutator: Invalid enum");
else if constexpr (std::is_same_v<std::decay_t<T>, StoreFunc_Storage>)
yulAssert(StoreFunc_Storage_IsValid(ret), "Yul proto mutator: Invalid enum");
else if constexpr (std::is_same_v<std::decay_t<T>, NullaryOp_NOp>)
yulAssert(NullaryOp_NOp_IsValid(ret), "Yul proto mutator: Invalid enum");
else if constexpr (std::is_same_v<std::decay_t<T>, BinaryOp_BOp>)
yulAssert(BinaryOp_BOp_IsValid(ret), "Yul proto mutator: Invalid enum");
else if constexpr (std::is_same_v<std::decay_t<T>, UnaryOp_UOp>)
yulAssert(UnaryOp_UOp_IsValid(ret), "Yul proto mutator: Invalid enum");
else if constexpr (std::is_same_v<std::decay_t<T>, LowLevelCall_Type>)
yulAssert(LowLevelCall_Type_IsValid(ret), "Yul proto mutator: Invalid enum");
else if constexpr (std::is_same_v<std::decay_t<T>, Create_Type>)
yulAssert(Create_Type_IsValid(ret), "Yul proto mutator: Invalid enum");
else if constexpr (std::is_same_v<std::decay_t<T>, UnaryOpData_UOpData>)
yulAssert(UnaryOpData_UOpData_IsValid(ret), "Yul proto mutator: Invalid enum");
else
static_assert(AlwaysFalse<T>::value, "Yul proto mutator: non-exhaustive visitor.");
return ret;
}
template <typename T>
unsigned YPM::EnumTypeConverter<T>::enumMax()
{
if constexpr (std::is_same_v<std::decay_t<T>, FunctionCall_Returns>)
return FunctionCall_Returns_Returns_MAX;
else if constexpr (std::is_same_v<std::decay_t<T>, StoreFunc_Storage>)
return StoreFunc_Storage_Storage_MAX;
else if constexpr (std::is_same_v<std::decay_t<T>, NullaryOp_NOp>)
return NullaryOp_NOp_NOp_MAX;
else if constexpr (std::is_same_v<std::decay_t<T>, BinaryOp_BOp>)
return BinaryOp_BOp_BOp_MAX;
else if constexpr (std::is_same_v<std::decay_t<T>, UnaryOp_UOp>)
return UnaryOp_UOp_UOp_MAX;
else if constexpr (std::is_same_v<std::decay_t<T>, LowLevelCall_Type>)
return LowLevelCall_Type_Type_MAX;
else if constexpr (std::is_same_v<std::decay_t<T>, Create_Type>)
return Create_Type_Type_MAX;
else if constexpr (std::is_same_v<std::decay_t<T>, UnaryOpData_UOpData>)
return UnaryOpData_UOpData_UOpData_MAX;
else
static_assert(AlwaysFalse<T>::value, "Yul proto mutator: non-exhaustive visitor.");
}
template <typename T>
unsigned YPM::EnumTypeConverter<T>::enumMin()
{
if constexpr (std::is_same_v<std::decay_t<T>, FunctionCall_Returns>)
return FunctionCall_Returns_Returns_MIN;
else if constexpr (std::is_same_v<std::decay_t<T>, StoreFunc_Storage>)
return StoreFunc_Storage_Storage_MIN;
else if constexpr (std::is_same_v<std::decay_t<T>, NullaryOp_NOp>)
return NullaryOp_NOp_NOp_MIN;
else if constexpr (std::is_same_v<std::decay_t<T>, BinaryOp_BOp>)
return BinaryOp_BOp_BOp_MIN;
else if constexpr (std::is_same_v<std::decay_t<T>, UnaryOp_UOp>)
return UnaryOp_UOp_UOp_MIN;
else if constexpr (std::is_same_v<std::decay_t<T>, LowLevelCall_Type>)
return LowLevelCall_Type_Type_MIN;
else if constexpr (std::is_same_v<std::decay_t<T>, Create_Type>)
return Create_Type_Type_MIN;
else if constexpr (std::is_same_v<std::decay_t<T>, UnaryOpData_UOpData>)
return UnaryOpData_UOpData_UOpData_MIN;
else
static_assert(AlwaysFalse<T>::value, "Yul proto mutator: non-exhaustive visitor.");
}

View File

@ -0,0 +1,98 @@
#pragma once
#include <test/tools/ossfuzz/yulProto.pb.h>
#include <libsolutil/Common.h>
#include <src/libfuzzer/libfuzzer_macro.h>
#include <random>
namespace solidity::yul::test::yul_fuzzer
{
using ProtobufMessage = google::protobuf::Message;
template <typename Proto>
using LPMPostProcessor = protobuf_mutator::libfuzzer::PostProcessorRegistration<Proto>;
class MutationInfo: public ScopeGuard
{
public:
MutationInfo(ProtobufMessage const* _message, std::string const& _info);
static void writeLine(std::string const& _str)
{
std::cout << _str << std::endl;
}
void exitInfo();
ProtobufMessage const* m_protobufMsg;
};
struct YulRandomNumGenerator
{
using RandomEngine = std::minstd_rand;
explicit YulRandomNumGenerator(unsigned _seed): m_random(RandomEngine(_seed)) {}
unsigned operator()()
{
return m_random();
}
RandomEngine m_random;
};
struct YulProtoMutator
{
/// @param _value: Value of the integer literal
/// @returns an integer literal protobuf message initialized with
/// the given value.
static Literal* intLiteral(unsigned _value);
/// @param _seed: Pseudo-random unsigned integer used as index
/// of variable to be referenced
/// @returns a variable reference protobuf message.
static VarRef* varRef(unsigned _seed);
/// @param _value: value of literal expression
/// @returns an expression protobuf message
static Expression* litExpression(unsigned _value);
/// @param _rand: Pseudo-random number generator
/// of variable to be referenced
/// @returns a variable reference protobuf message
static Expression* refExpression(YulRandomNumGenerator& _rand);
/// Helper type for type matching visitor.
template<class T> struct AlwaysFalse: std::false_type {};
/// Template struct for obtaining a valid enum value of
/// template type from a pseudo-random unsigned integer.
/// @param _seed: Pseudo-random integer
/// @returns Valid enum of enum type T
template <typename T>
struct EnumTypeConverter
{
T enumFromSeed(unsigned _seed)
{
return validEnum(_seed);
}
/// @returns a valid enum of type T from _seed
T validEnum(unsigned _seed);
/// @returns maximum enum value for enum of type T
static unsigned enumMax();
/// @returns minimum enum value for enum of type T
static unsigned enumMin();
};
/// Modulo for mutations that should occur rarely
static constexpr unsigned s_lowIP = 31;
/// Modulo for mutations that should occur not too often
static constexpr unsigned s_mediumIP = 29;
/// Modulo for mutations that should occur often
static constexpr unsigned s_highIP = 23;
};
}

View File

@ -121,7 +121,7 @@ protected:
RandomisingAlgorithm m_algorithm;
};
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(AlgorithmRunnerTest)
BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRunnerFixture)

View File

@ -46,7 +46,7 @@ using namespace solidity::util;
namespace solidity::phaser::test
{
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(ChromosomeTest)
BOOST_AUTO_TEST_CASE(constructor_should_convert_from_string_to_optimisation_steps)

View File

@ -66,7 +66,7 @@ map<string, TestEnum> const StringToTestEnumMap = invertMap(TestEnumToStringMap)
}
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(CommonTest)
BOOST_FIXTURE_TEST_CASE(readLinesFromFile_should_return_all_lines_from_a_text_file_as_strings_without_newlines, ReadLinesFromFileFixture)

View File

@ -95,7 +95,7 @@ protected:
};
};
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(FitnessMetricsTest)
BOOST_AUTO_TEST_SUITE(ProgramBasedMetricTest)

View File

@ -56,7 +56,7 @@ protected:
};
};
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(GeneticAlgorithmsTest)
BOOST_AUTO_TEST_SUITE(RandomAlgorithmTest)
@ -212,7 +212,8 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossove
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(ClassicGeneticAlgorithmTest)
BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_individuals_with_probability_proportional_to_fitness, ClassicGeneticAlgorithmFixture)
// FIXME: This test runs *very* slowly (tens of seconds). Investigate, fix and re-enable.
BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_individuals_with_probability_proportional_to_fitness, ClassicGeneticAlgorithmFixture, *boost::unit_test::disabled())
{
constexpr double relativeTolerance = 0.1;
constexpr size_t populationSize = 1000;
@ -253,7 +254,8 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_individuals_with_probability_
BOOST_TEST(abs(meanSquaredError(newFitness, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_only_individuals_existing_in_the_original_population, ClassicGeneticAlgorithmFixture)
// FIXME: This test runs *very* slowly (tens of seconds). Investigate, fix and re-enable.
BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_only_individuals_existing_in_the_original_population, ClassicGeneticAlgorithmFixture, *boost::unit_test::disabled())
{
constexpr size_t populationSize = 1000;
auto population = Population::makeRandom(m_fitnessMetric, populationSize, 1, 10);
@ -297,7 +299,8 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_crossover, ClassicGeneticAlgorith
BOOST_TEST(totalCrossed >= 2);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_mutation, ClassicGeneticAlgorithmFixture)
// FIXME: This test runs *very* slowly (tens of seconds). Investigate, fix and re-enable.
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_mutation, ClassicGeneticAlgorithmFixture, *boost::unit_test::disabled())
{
m_options.mutationChance = 0.6;
ClassicGeneticAlgorithm algorithm(m_options);
@ -326,7 +329,8 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_mutation, ClassicGeneticAlgorithm
BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_deletion, ClassicGeneticAlgorithmFixture)
// FIXME: This test runs *very* slowly (tens of seconds). Investigate, fix and re-enable.
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_deletion, ClassicGeneticAlgorithmFixture, *boost::unit_test::disabled())
{
m_options.deletionChance = 0.6;
ClassicGeneticAlgorithm algorithm(m_options);
@ -355,7 +359,8 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_deletion, ClassicGeneticAlgorithm
BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance);
}
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_addition, ClassicGeneticAlgorithmFixture)
// FIXME: This test runs *very* slowly (tens of seconds). Investigate, fix and re-enable.
BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_addition, ClassicGeneticAlgorithmFixture, *boost::unit_test::disabled())
{
m_options.additionChance = 0.6;
ClassicGeneticAlgorithm algorithm(m_options);

View File

@ -33,7 +33,7 @@ using namespace solidity::util;
namespace solidity::phaser::test
{
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(MutationsTest)
BOOST_AUTO_TEST_SUITE(GeneRandomisationTest)

View File

@ -31,7 +31,7 @@ using namespace std;
namespace solidity::phaser::test
{
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(PairSelectionsTest)
BOOST_AUTO_TEST_SUITE(RandomPairSelectionTest)

View File

@ -103,7 +103,7 @@ protected:
};
};
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(PhaserTest)
BOOST_AUTO_TEST_SUITE(GeneticAlgorithmFactoryTest)

View File

@ -59,7 +59,7 @@ protected:
shared_ptr<FitnessMetric> m_fitnessMetric = make_shared<ChromosomeLengthMetric>();
};
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(PopulationTest)
BOOST_AUTO_TEST_CASE(isFitter_should_use_fitness_as_the_main_criterion)

View File

@ -57,7 +57,7 @@ namespace
namespace solidity::phaser::test
{
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(ProgramTest)
BOOST_AUTO_TEST_CASE(copy_constructor_should_make_deep_copy_of_ast)

View File

@ -70,7 +70,7 @@ protected:
ProgramCache m_programCache{m_program};
};
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(ProgramCacheTest)
BOOST_AUTO_TEST_CASE(CacheStats_operator_plus_should_add_stats_together)

View File

@ -34,7 +34,7 @@ using namespace solidity::util;
namespace solidity::phaser::test
{
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(SelectionsTest)
BOOST_AUTO_TEST_SUITE(RangeSelectionTest)

View File

@ -28,7 +28,7 @@ using namespace std;
namespace solidity::phaser::test
{
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(RandomTest)
BOOST_AUTO_TEST_CASE(bernoulliTrial_should_produce_samples_with_right_expected_value_and_variance)

View File

@ -34,7 +34,7 @@ namespace fs = boost::filesystem;
namespace solidity::phaser::test
{
BOOST_AUTO_TEST_SUITE(Phaser)
BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions"))
BOOST_AUTO_TEST_SUITE(TestHelpersTest)
BOOST_AUTO_TEST_CASE(ChromosomeLengthMetric_evaluate_should_return_chromosome_length)