mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
180 lines
4.5 KiB
C++
180 lines
4.5 KiB
C++
#include <test/tools/ossfuzz/solarithprotoToSol.h>
|
|
#include <test/tools/ossfuzz/protoToYul.h>
|
|
|
|
#include <liblangutil/Exceptions.h>
|
|
|
|
#include <libsolutil/Whiskers.h>
|
|
|
|
#include <libyul/AssemblyStack.h>
|
|
#include <libyul/backends/evm/EVMDialect.h>
|
|
#include <libyul/Exceptions.h>
|
|
|
|
using namespace solidity::test::solarithfuzzer;
|
|
using namespace solidity;
|
|
using namespace solidity::util;
|
|
using namespace std;
|
|
|
|
string ProtoConverter::programToString(Program const& _program)
|
|
{
|
|
m_rand = make_unique<SolRandomNumGenerator>(
|
|
SolRandomNumGenerator(_program.seed())
|
|
);
|
|
return visit(_program);
|
|
}
|
|
|
|
string ProtoConverter::visit(Program const& _program)
|
|
{
|
|
Whiskers p(R"(pragma solidity >= 0.0.0;)");
|
|
Whiskers c(R"(contract C {)");
|
|
Whiskers t(R"(function test() public returns (uint)<body>)");
|
|
t("body", visit(_program.b()));
|
|
return p.render()
|
|
+ '\n'
|
|
+ c.render()
|
|
+ '\n'
|
|
+ '\t'
|
|
+ t.render()
|
|
+ '\n'
|
|
+ '}';
|
|
}
|
|
|
|
string ProtoConverter::visit(Block const& _block)
|
|
{
|
|
ostringstream blockStr;
|
|
blockStr << '\n'
|
|
<< '\t'
|
|
<< '{'
|
|
<< '\n';
|
|
for (auto const& v: _block.v())
|
|
blockStr << visit(v);
|
|
blockStr << visit(_block.a());
|
|
ostringstream trace;
|
|
if (m_yulAssembly.empty())
|
|
blockStr << "\t\treturn 0;";
|
|
else
|
|
blockStr << addChecks(m_yulProgram, langutil::EVMVersion::berlin(), trace);
|
|
blockStr << '\n'
|
|
<< '\t'
|
|
<< '}';
|
|
return blockStr.str();
|
|
}
|
|
|
|
string ProtoConverter::addChecks(
|
|
string const& _yulSource,
|
|
langutil::EVMVersion _version,
|
|
ostringstream& _os
|
|
)
|
|
{
|
|
ostringstream out;
|
|
unsigned error = 1;
|
|
auto memoryDump = interpretYul(_yulSource, _version, _os);
|
|
unsigned index = 0;
|
|
for (auto const& v: m_varTypeMap)
|
|
{
|
|
Whiskers check(R"(<ind>if (<var> != <type>(0x<value>)) return <error>;<endl>)");
|
|
check("ind", "\t\t");
|
|
check("var", v.first);
|
|
check("type", get<1>(v.second));
|
|
u256 memIdx = index * 0x20;
|
|
string val{};
|
|
if (memoryDump.count(memIdx))
|
|
{
|
|
unsigned byteWidth = get<2>(v.second);
|
|
val = extractBytes(memoryDump.at(memIdx), byteWidth);
|
|
// Avoid interpretation of 20 byte literals as address literals
|
|
// by prepending 00.
|
|
if (byteWidth == 20)
|
|
val = "00" + val;
|
|
}
|
|
else
|
|
val = "0";
|
|
check("value", val);
|
|
check("error", to_string(error++));
|
|
check("endl", "\n");
|
|
out << check.render();
|
|
index++;
|
|
}
|
|
out << "\t\treturn 0;\n";
|
|
return out.str();
|
|
}
|
|
|
|
map<u256, string> ProtoConverter::interpretYul(
|
|
string const& _yulSource,
|
|
langutil::EVMVersion _version,
|
|
ostringstream& _os
|
|
)
|
|
{
|
|
using namespace yul;
|
|
using namespace solidity::yul::test::yul_fuzzer;
|
|
YulStringRepository::reset();
|
|
|
|
// AssemblyStack entry point
|
|
AssemblyStack stack(
|
|
_version,
|
|
AssemblyStack::Language::StrictAssembly,
|
|
solidity::frontend::OptimiserSettings::full()
|
|
);
|
|
|
|
// Parse protobuf mutated YUL code
|
|
if (!stack.parseAndAnalyze("source", _yulSource) || !stack.parserResult()->code ||
|
|
!stack.parserResult()->analysisInfo)
|
|
{
|
|
std::cout << _yulSource << std::endl;
|
|
yulAssert(false, "Proto fuzzer generated malformed program");
|
|
}
|
|
stack.optimize();
|
|
yulFuzzerUtil i;
|
|
|
|
auto r = i.interpret(
|
|
_os,
|
|
stack.parserResult()->code,
|
|
EVMDialect::strictAssemblyForEVMObjects(_version)
|
|
);
|
|
if (r != yulFuzzerUtil::TerminationReason::StepLimitReached && r != yulFuzzerUtil::TerminationReason::TraceLimitReached)
|
|
return i.memoryDump();
|
|
else
|
|
throw langutil::FuzzerError();
|
|
}
|
|
|
|
string ProtoConverter::visit(Assembly const& _as)
|
|
{
|
|
using namespace solidity::yul::test;
|
|
ostringstream assemblyStr;
|
|
assemblyStr << "\t\t" << "assembly {\n";
|
|
// Yul converter
|
|
auto c = yul_fuzzer::ProtoConverter{m_varCounter};
|
|
m_yulAssembly = c.programToString(_as.p());
|
|
m_yulProgram = Whiskers("{<endl><init><assembly>}")
|
|
("endl", "\n")
|
|
("init", m_yulInitCode.str())
|
|
("assembly", m_yulAssembly)
|
|
.render();
|
|
assemblyStr << m_yulAssembly;
|
|
assemblyStr << "\t\t}\n";
|
|
return assemblyStr.str();
|
|
}
|
|
|
|
string ProtoConverter::visit(VarDecl const& _vardecl)
|
|
{
|
|
Whiskers v(R"(<type> <varName> = <type>(<value>);)");
|
|
string type = visit(_vardecl.t());
|
|
string varName = newVarName();
|
|
unsigned byteWidth = widthUnsigned(_vardecl.t().bytewidth());
|
|
m_varTypeMap.emplace(varName, tuple(typeSign(_vardecl.t()), type, byteWidth));
|
|
v("type", type);
|
|
v("varName", varName);
|
|
string value = maskUnsignedToHex(64);
|
|
v("value", value);
|
|
Whiskers i(R"(let <varName> := <value><endl>)");
|
|
i("varName", varName);
|
|
i("value", "0x" + extractBytes(value, byteWidth));
|
|
i("endl", "\n");
|
|
m_yulInitCode << i.render();
|
|
incrementVarCounter();
|
|
return "\t\t" + v.render() + '\n';
|
|
}
|
|
|
|
string ProtoConverter::visit(Type const& _type)
|
|
{
|
|
return signString(_type.s()) + widthString(_type.bytewidth());
|
|
} |