mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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"}]}
|
||||
@@ -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.
|
||||
@@ -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 {};
|
||||
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user