Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth
2020-07-22 15:26:44 +02:00
53 changed files with 675 additions and 168 deletions
+7
View File
@@ -38,6 +38,13 @@ TestCaseReader::TestCaseReader(string const& _filename):
m_unreadSettings = m_settings;
}
TestCaseReader::TestCaseReader(istringstream const& _str)
{
tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(
static_cast<istream&>(const_cast<istringstream&>(_str))
);
}
string const& TestCaseReader::source() const
{
if (m_sources.sources.size() != 1)
+1
View File
@@ -42,6 +42,7 @@ class TestCaseReader
public:
TestCaseReader() = default;
explicit TestCaseReader(std::string const& _filename);
explicit TestCaseReader(std::istringstream const& _testCode);
SourceMap const& sources() const { return m_sources; }
std::string const& source() const;
@@ -0,0 +1,17 @@
{
"language": "Yul",
"sources":
{
"A":
{
"content": "object \"NamedObject\" { code { let x := dataoffset(\"NamedObject.\") sstore(add(x, 0), 0) } object \"OtherObject\" { code { revert(0, 0) } } }"
}
},
"settings":
{
"outputSelection":
{
"*": { "*": ["*"], "": [ "*" ] }
}
}
}
@@ -0,0 +1,4 @@
{"errors":[{"component":"general","formattedMessage":"A:1:40: TypeError: Unknown data object \"NamedObject.\".
object \"NamedObject\" { code { let x := dataoffset(\"NamedObject.\") sstore(add(x, 0), 0) } object \"OtherObject\" { code { revert(0, 0) } } }
^--------^
","message":"Unknown data object \"NamedObject.\".","severity":"error","sourceLocation":{"end":49,"file":"A","start":39},"type":"TypeError"}]}
+19
View File
@@ -29,6 +29,7 @@
#include <string>
#include <tuple>
#include <memory>
#include <libyul/Exceptions.h>
using namespace std;
using namespace solidity::langutil;
@@ -254,6 +255,24 @@ BOOST_AUTO_TEST_CASE(immutable)
);
}
BOOST_AUTO_TEST_CASE(subobject_encode_decode)
{
Assembly assembly;
shared_ptr<Assembly> subAsmPtr = make_shared<Assembly>();
shared_ptr<Assembly> subSubAsmPtr = make_shared<Assembly>();
assembly.appendSubroutine(subAsmPtr);
subAsmPtr->appendSubroutine(subSubAsmPtr);
BOOST_CHECK(assembly.encodeSubPath({0}) == 0);
BOOST_REQUIRE_THROW(assembly.encodeSubPath({1}), solidity::evmasm::AssemblyException);
BOOST_REQUIRE_THROW(assembly.decodeSubPath(1), solidity::evmasm::AssemblyException);
vector<size_t> subPath{0, 0};
BOOST_CHECK(assembly.decodeSubPath(assembly.encodeSubPath(subPath)) == subPath);
}
BOOST_AUTO_TEST_SUITE_END()
} // end namespaces
@@ -36,7 +36,8 @@ contract C {
return true;
}
}
// ====
// compileViaYul: also
// ----
// testRuntime() -> true
// testCreation() -> true
@@ -10,18 +10,18 @@ contract D {
}
}
contract C {
function test() public returns (uint256) {
D d = new D();
bytes32 hash;
assembly { hash := extcodehash(d) }
assert(hash == keccak256(type(D).runtimeCode));
return 42;
D d = new D();
bytes32 hash;
assembly { hash := extcodehash(d) }
assert(hash == keccak256(type(D).runtimeCode));
return 42;
}
}
// ====
// EVMVersion: >=constantinople
// compileViaYul: also
// ----
// test() -> 42
@@ -0,0 +1,9 @@
pragma experimental SMTChecker;
contract C {
int[][] array2d;
function l() public {
array2d.push().push();
assert(array2d.length > 0);
assert(array2d[array2d.length - 1].length > 0);
}
}
@@ -0,0 +1,12 @@
pragma experimental SMTChecker;
contract C {
int[][] array2d;
function l() public {
array2d.push().push();
assert(array2d.length > 2);
assert(array2d[array2d.length - 1].length > 3);
}
}
// ----
// Warning 4661: (113-139): Assertion violation happens here
// Warning 4661: (143-189): Assertion violation happens here
@@ -0,0 +1,11 @@
pragma experimental SMTChecker;
contract C {
int[][][] array2d;
function l() public {
array2d.push().push().push();
assert(array2d.length > 0);
uint last = array2d[array2d.length - 1].length;
assert(last > 0);
assert(array2d[array2d.length - 1][last - 1].length > 0);
}
}
@@ -0,0 +1,15 @@
pragma experimental SMTChecker;
contract C {
int[][][] array2d;
function l() public {
array2d.push().push().push();
assert(array2d.length > 2);
uint last = array2d[array2d.length - 1].length;
assert(last > 3);
assert(array2d[array2d.length - 1][last - 1].length > 4);
}
}
// ----
// Warning 4661: (122-148): Assertion violation happens here
// Warning 4661: (202-218): Assertion violation happens here
// Warning 4661: (222-278): Assertion violation happens here
@@ -0,0 +1,18 @@
pragma experimental SMTChecker;
contract C {
int[][] array2d;
function l() public {
s().push();
// False positive.
// Knowledge is erased because `s()` is a storage pointer.
assert(array2d[2].length > 0);
}
function s() internal returns (int[] storage) {
array2d.push();
array2d.push();
array2d.push();
return array2d[2];
}
}
// ----
// Warning 4661: (184-213): Assertion violation happens here
@@ -0,0 +1,15 @@
pragma experimental SMTChecker;
contract C {
int[][] array2d;
function l() public {
s();
array2d[2].push();
assert(array2d[2].length > 0);
}
function s() internal returns (int[] storage) {
array2d.push();
array2d.push();
array2d.push();
return array2d[2];
}
}
@@ -0,0 +1,8 @@
contract C {
receive() external payable { }
receive() external payable { }
receive() external payable { }
}
// ----
// DeclarationError 4046: (52-82): Only one receive function is allowed.
// DeclarationError 4046: (87-117): Only one receive function is allowed.
+1 -1
View File
@@ -90,7 +90,7 @@ pair<shared_ptr<Block>, shared_ptr<yul::AsmAnalysisInfo>> yul::test::parse(
if (!parserResult->code || errorReporter.hasErrors())
return {};
shared_ptr<AsmAnalysisInfo> analysisInfo = make_shared<AsmAnalysisInfo>();
AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect, {}, parserResult->dataNames());
AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect, {}, parserResult->qualifiedDataNames());
// TODO this should be done recursively.
if (!analyzer.analyze(*parserResult->code) || errorReporter.hasErrors())
return {};
+1 -1
View File
@@ -99,7 +99,7 @@ string EwasmTranslationTest::interpret()
{
InterpreterState state;
state.maxTraceSize = 10000;
state.maxSteps = 100000;
state.maxSteps = 1000000;
try
{
Interpreter::run(state, WasmDialect{}, *m_object->code);
@@ -0,0 +1,104 @@
object "A" {
code {
pop(dataoffset("A"))
pop(datasize("A"))
pop(dataoffset("B"))
pop(datasize("B"))
pop(dataoffset("B.C"))
pop(datasize("B.C"))
pop(dataoffset("B.E"))
pop(datasize("B.E"))
pop(dataoffset("B.C.D"))
pop(datasize("B.C.D"))
}
data "data1" "Hello, World!"
object "B" {
code {
pop(dataoffset("C"))
pop(datasize("C"))
pop(dataoffset("E"))
pop(datasize("E"))
pop(dataoffset("C.D"))
pop(datasize("C.D"))
}
object "C" {
code {
pop(dataoffset("D"))
pop(datasize("D"))
}
object "D" {
code {
invalid()
}
}
}
object "E" {
code {
invalid()
}
}
}
}
// ----
// Assembly:
// /* "source":26:46 */
// pop(0x00)
// /* "source":51:69 */
// pop(bytecodeSize)
// /* "source":74:94 */
// pop(dataOffset(sub_0))
// /* "source":99:117 */
// pop(dataSize(sub_0))
// /* "source":122:144 */
// pop(dataOffset(sub_0.sub_0))
// /* "source":149:169 */
// pop(dataSize(sub_0.sub_0))
// /* "source":174:196 */
// pop(dataOffset(sub_0.sub_1))
// /* "source":201:221 */
// pop(dataSize(sub_0.sub_1))
// /* "source":226:250 */
// pop(dataOffset(sub_0.sub_0.sub_0))
// /* "source":255:277 */
// pop(dataSize(sub_0.sub_0.sub_0))
// stop
// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
//
// sub_0: assembly {
// /* "source":347:367 */
// pop(dataOffset(sub_0))
// /* "source":374:392 */
// pop(dataSize(sub_0))
// /* "source":399:419 */
// pop(dataOffset(sub_1))
// /* "source":426:444 */
// pop(dataSize(sub_1))
// /* "source":451:473 */
// pop(dataOffset(sub_0.sub_0))
// /* "source":480:500 */
// pop(dataSize(sub_0.sub_0))
// stop
//
// sub_0: assembly {
// /* "source":545:565 */
// pop(dataOffset(sub_0))
// /* "source":574:592 */
// pop(dataSize(sub_0))
// stop
//
// sub_0: assembly {
// /* "source":645:654 */
// invalid
// }
// }
//
// sub_1: assembly {
// /* "source":717:726 */
// invalid
// }
// }
// Bytecode: 600050604650601f50601d50603e50600850603d50600150603c50600150fe601350600850601b50600150601c50600150fe600750600150fefefefefefe600750600150fefe
// Opcodes: PUSH1 0x0 POP PUSH1 0x46 POP PUSH1 0x1F POP PUSH1 0x1D POP PUSH1 0x3E POP PUSH1 0x8 POP PUSH1 0x3D POP PUSH1 0x1 POP PUSH1 0x3C POP PUSH1 0x1 POP INVALID PUSH1 0x13 POP PUSH1 0x8 POP PUSH1 0x1B POP PUSH1 0x1 POP PUSH1 0x1C POP PUSH1 0x1 POP INVALID PUSH1 0x7 POP PUSH1 0x1 POP INVALID INVALID INVALID INVALID INVALID INVALID PUSH1 0x7 POP PUSH1 0x1 POP INVALID INVALID
// SourceMappings: 26:20:0:-:0;;51:18;;74:20;;99:18;;122:22;;149:20;;174:22;;201:20;;226:24;;255:22;
+3 -3
View File
@@ -72,16 +72,16 @@ void FuzzerUtil::testCompilerJsonInterface(string const& _input, bool _optimize,
runCompiler(jsonCompactPrint(config), _quiet);
}
void FuzzerUtil::testCompiler(string const& _input, bool _optimize)
void FuzzerUtil::testCompiler(StringMap const& _input, bool _optimize, unsigned _rand)
{
frontend::CompilerStack compiler;
EVMVersion evmVersion = s_evmVersions[_input.size() % s_evmVersions.size()];
EVMVersion evmVersion = s_evmVersions[_rand % s_evmVersions.size()];
frontend::OptimiserSettings optimiserSettings;
if (_optimize)
optimiserSettings = frontend::OptimiserSettings::standard();
else
optimiserSettings = frontend::OptimiserSettings::minimal();
compiler.setSources({{"", _input}});
compiler.setSources(_input);
compiler.setEVMVersion(evmVersion);
compiler.setOptimiserSettings(optimiserSettings);
try
+8 -1
View File
@@ -16,6 +16,9 @@
*/
// SPDX-License-Identifier: GPL-3.0
#include <libsolutil/Common.h>
#include <map>
#include <string>
/**
@@ -28,5 +31,9 @@ struct FuzzerUtil
static void testCompilerJsonInterface(std::string const& _input, bool _optimize, bool _quiet);
static void testConstantOptimizer(std::string const& _input, bool _quiet);
static void testStandardCompiler(std::string const& _input, bool _quiet);
static void testCompiler(std::string const& _input, bool _optimize);
/// Compiles @param _input which is a map of input file name to source code
/// string with optimisation turned on if @param _optimize is true
/// (off otherwise) and a pseudo-random @param _rand that selects the EVM
/// version to be compiled for.
static void testCompiler(solidity::StringMap const& _input, bool _optimize, unsigned _rand);
};
+2 -2
View File
@@ -23,11 +23,11 @@ if (OSSFUZZ)
endif()
if (OSSFUZZ)
add_executable(solc_opt_ossfuzz solc_opt_ossfuzz.cpp ../fuzzer_common.cpp)
add_executable(solc_opt_ossfuzz solc_opt_ossfuzz.cpp ../fuzzer_common.cpp ../../TestCaseReader.cpp)
target_link_libraries(solc_opt_ossfuzz PRIVATE libsolc evmasm)
set_target_properties(solc_opt_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
add_executable(solc_noopt_ossfuzz solc_noopt_ossfuzz.cpp ../fuzzer_common.cpp)
add_executable(solc_noopt_ossfuzz solc_noopt_ossfuzz.cpp ../fuzzer_common.cpp ../../TestCaseReader.cpp)
target_link_libraries(solc_noopt_ossfuzz PRIVATE libsolc evmasm)
set_target_properties(solc_noopt_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
+16 -1
View File
@@ -18,6 +18,11 @@
#include <test/tools/fuzzer_common.h>
#include <test/TestCaseReader.h>
#include <sstream>
using namespace solidity::frontend::test;
using namespace std;
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
@@ -25,7 +30,17 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
if (_size <= 600)
{
string input(reinterpret_cast<char const*>(_data), _size);
FuzzerUtil::testCompiler(input, /*optimize=*/false);
map<string, string> sourceCode;
try
{
TestCaseReader t = TestCaseReader(std::istringstream(input));
sourceCode = t.sources().sources;
}
catch (runtime_error const&)
{
return 0;
}
FuzzerUtil::testCompiler(sourceCode, /*optimize=*/false, /*_rand=*/_size);
}
return 0;
}
+17 -2
View File
@@ -18,14 +18,29 @@
#include <test/tools/fuzzer_common.h>
#include <test/TestCaseReader.h>
#include <sstream>
using namespace solidity::frontend::test;
using namespace std;
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
{
if (_size <= 600)
{
string input(reinterpret_cast<char const *>(_data), _size);
FuzzerUtil::testCompiler(input, /*optimize=*/true);
string input(reinterpret_cast<char const*>(_data), _size);
map<string, string> sourceCode;
try
{
TestCaseReader t = TestCaseReader(std::istringstream(input));
sourceCode = t.sources().sources;
}
catch (runtime_error const&)
{
return 0;
}
FuzzerUtil::testCompiler(sourceCode, /*optimize=*/true, /*rand=*/_size);
}
return 0;
}
+6 -4
View File
@@ -97,15 +97,17 @@ DEFINE_PROTO_FUZZER(Program const& _input)
EVMDialect::strictAssemblyForEVMObjects(version)
);
if (termReason == yulFuzzerUtil::TerminationReason::StepLimitReached)
if (
termReason == yulFuzzerUtil::TerminationReason::StepLimitReached ||
termReason == yulFuzzerUtil::TerminationReason::TraceLimitReached
)
return;
stack.optimize();
termReason = yulFuzzerUtil::interpret(
yulFuzzerUtil::interpret(
os2,
stack.parserResult()->code,
EVMDialect::strictAssemblyForEVMObjects(version),
(yul::test::yul_fuzzer::yulFuzzerUtil::maxSteps * 4)
EVMDialect::strictAssemblyForEVMObjects(version)
);
bool isTraceEq = (os1.str() == os2.str());
+16 -6
View File
@@ -145,6 +145,11 @@ void Interpreter::operator()(ForLoop const& _forLoop)
}
while (evaluate(*_forLoop.condition) != 0)
{
// Increment step for each loop iteration for loops with
// an empty body and post blocks to prevent a deadlock.
if (_forLoop.body.statements.size() == 0 && _forLoop.post.statements.size() == 0)
incrementStep();
m_state.controlFlowState = ControlFlowState::Default;
(*this)(_forLoop.body);
if (m_state.controlFlowState == ControlFlowState::Break || m_state.controlFlowState == ControlFlowState::Leave)
@@ -176,12 +181,6 @@ void Interpreter::operator()(Leave const&)
void Interpreter::operator()(Block const& _block)
{
m_state.numSteps++;
if (m_state.maxSteps > 0 && m_state.numSteps >= m_state.maxSteps)
{
m_state.trace.emplace_back("Interpreter execution step limit reached.");
throw StepLimitReached();
}
enterScope(_block);
// Register functions.
for (auto const& statement: _block.statements)
@@ -193,6 +192,7 @@ void Interpreter::operator()(Block const& _block)
for (auto const& statement: _block.statements)
{
incrementStep();
visit(statement);
if (m_state.controlFlowState != ControlFlowState::Default)
break;
@@ -235,6 +235,16 @@ void Interpreter::leaveScope()
yulAssert(m_scope, "");
}
void Interpreter::incrementStep()
{
m_state.numSteps++;
if (m_state.maxSteps > 0 && m_state.numSteps >= m_state.maxSteps)
{
m_state.trace.emplace_back("Interpreter execution step limit reached.");
throw StepLimitReached();
}
}
void ExpressionEvaluator::operator()(Literal const& _literal)
{
static YulString const trueString("true");
+4
View File
@@ -154,6 +154,10 @@ private:
void enterScope(Block const& _block);
void leaveScope();
/// Increment interpreter step count, throwing exception if step limit
/// is reached.
void incrementStep();
Dialect const& m_dialect;
InterpreterState& m_state;
/// Values of variables.