Allow stack limit evasion in system yul routines during old code generation.

This commit is contained in:
Daniel Kirchner
2021-09-15 17:34:40 +02:00
parent 2e6d3e0a1b
commit abf0e47e0a
10 changed files with 95 additions and 45 deletions
+3
View File
@@ -20,6 +20,8 @@
#include <libyul/Exceptions.h>
#include <libsolutil/Common.h>
#include <optional>
#include <string>
#include <set>
@@ -39,6 +41,7 @@ struct OptimiserStepContext
std::set<YulString> const& reservedIdentifiers;
/// The value nullopt represents creation code
std::optional<size_t> expectedExecutionsPerDeployment;
std::shared_ptr<u256> externalFreeMemoryPointerInitializer{};
};
+36 -27
View File
@@ -124,10 +124,7 @@ void StackLimitEvader::run(
)
{
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
yulAssert(
evmDialect && evmDialect->providesObjectAccess(),
"StackLimitEvader can only be run on objects using the EVMDialect with object access."
);
yulAssert(evmDialect, "StackLimitEvader can only be run on objects using the EVMDialect.");
if (evmDialect && evmDialect->evmVersion().canOverchargeGasForCall())
{
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(*evmDialect, _object);
@@ -165,27 +162,31 @@ void StackLimitEvader::run(
{
yulAssert(_object.code, "");
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
yulAssert(
evmDialect && evmDialect->providesObjectAccess(),
"StackLimitEvader can only be run on objects using the EVMDialect with object access."
);
yulAssert(evmDialect, "StackLimitEvader can only be run on objects using the EVMDialect.");
vector<FunctionCall*> memoryGuardCalls = FunctionCallFinder::run(
*_object.code,
"memoryguard"_yulstring
);
// Do not optimise, if no ``memoryguard`` call is found.
if (memoryGuardCalls.empty())
return;
u256 reservedMemory = 0;
// Make sure all calls to ``memoryguard`` we found have the same value as argument (otherwise, abort).
u256 reservedMemory = literalArgumentValue(*memoryGuardCalls.front());
yulAssert(reservedMemory < u256(1) << 32 - 1, "");
for (FunctionCall const* memoryGuardCall: memoryGuardCalls)
if (reservedMemory != literalArgumentValue(*memoryGuardCall))
if (_context.externalFreeMemoryPointerInitializer)
reservedMemory = *_context.externalFreeMemoryPointerInitializer;
else
{
vector<FunctionCall*> memoryGuardCalls = FunctionCallFinder::run(
*_object.code,
"memoryguard"_yulstring
);
// Do not optimise, if no ``memoryguard`` call is found.
if (memoryGuardCalls.empty())
return;
// Make sure all calls to ``memoryguard`` we found have the same value as argument (otherwise, abort).
reservedMemory = literalArgumentValue(*memoryGuardCalls.front());
yulAssert(reservedMemory < u256(1) << 32 - 1, "");
for (FunctionCall const* memoryGuardCall: memoryGuardCalls)
if (reservedMemory != literalArgumentValue(*memoryGuardCall))
return;
}
CallGraph callGraph = CallGraphGenerator::callGraph(*_object.code);
// We cannot move variables in recursive functions to fixed memory offsets.
@@ -195,6 +196,11 @@ void StackLimitEvader::run(
map<YulString, FunctionDefinition const*> functionDefinitions = FunctionDefinitionCollector::run(*_object.code);
if (_context.externalFreeMemoryPointerInitializer)
// Simulate calls from outer block to all defined functions.
for (auto functionDef: functionDefinitions)
callGraph.functionCalls[YulString{}].insert(functionDef.first);
MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls, functionDefinitions};
uint64_t requiredSlots = memoryOffsetAllocator.run();
yulAssert(requiredSlots < (uint64_t(1) << 32) - 1, "");
@@ -202,10 +208,13 @@ void StackLimitEvader::run(
StackToMemoryMover::run(_context, reservedMemory, memoryOffsetAllocator.slotAllocations, requiredSlots, *_object.code);
reservedMemory += 32 * requiredSlots;
for (FunctionCall* memoryGuardCall: FunctionCallFinder::run(*_object.code, "memoryguard"_yulstring))
{
Literal* literal = std::get_if<Literal>(&memoryGuardCall->arguments.front());
yulAssert(literal && literal->kind == LiteralKind::Number, "");
literal->value = YulString{util::toCompactHexWithPrefix(reservedMemory)};
}
if (_context.externalFreeMemoryPointerInitializer)
*_context.externalFreeMemoryPointerInitializer = reservedMemory;
else
for (FunctionCall* memoryGuardCall: FunctionCallFinder::run(*_object.code, "memoryguard"_yulstring))
{
Literal* literal = std::get_if<Literal>(&memoryGuardCall->arguments.front());
yulAssert(literal && literal->kind == LiteralKind::Number, "");
literal->value = YulString{util::toCompactHexWithPrefix(reservedMemory)};
}
}
+1 -4
View File
@@ -109,10 +109,7 @@ m_nameDispenser(_context.dispenser),
m_functionReturnVariables(move(_functionReturnVariables))
{
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
yulAssert(
evmDialect && evmDialect->providesObjectAccess(),
"StackToMemoryMover can only be run on objects using the EVMDialect with object access."
);
yulAssert(evmDialect, "StackToMemoryMover can only be run on objects using the EVMDialect.");
}
void StackToMemoryMover::operator()(FunctionDefinition& _functionDefinition)
+12 -5
View File
@@ -89,7 +89,8 @@ void OptimiserSuite::run(
bool _optimizeStackAllocation,
string const& _optimisationSequence,
optional<size_t> _expectedExecutionsPerDeployment,
set<YulString> const& _externallyUsedIdentifiers
set<YulString> const& _externallyUsedIdentifiers,
std::shared_ptr<u256> _externalFreeMemoryPointerInitializer
)
{
EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&_dialect);
@@ -107,7 +108,14 @@ void OptimiserSuite::run(
)(*_object.code));
Block& ast = *_object.code;
OptimiserSuite suite(_dialect, reservedIdentifiers, Debug::None, ast, _expectedExecutionsPerDeployment);
OptimiserSuite suite(
_dialect,
reservedIdentifiers,
Debug::None,
ast,
_expectedExecutionsPerDeployment,
_externalFreeMemoryPointerInitializer
);
// Some steps depend on properties ensured by FunctionHoister, BlockFlattener, FunctionGrouper and
// ForLoopInitRewriter. Run them first to be able to run arbitrary sequences safely.
@@ -144,10 +152,9 @@ void OptimiserSuite::run(
_optimizeStackAllocation,
stackCompressorMaxIterations
);
if (evmDialect->providesObjectAccess())
StackLimitEvader::run(suite.m_context, _object);
StackLimitEvader::run(suite.m_context, _object);
}
else if (evmDialect->providesObjectAccess() && _optimizeStackAllocation)
else if (_optimizeStackAllocation)
StackLimitEvader::run(suite.m_context, _object);
}
else if (dynamic_cast<WasmDialect const*>(&_dialect))
+12 -3
View File
@@ -66,7 +66,8 @@ public:
bool _optimizeStackAllocation,
std::string const& _optimisationSequence,
std::optional<size_t> _expectedExecutionsPerDeployment,
std::set<YulString> const& _externallyUsedIdentifiers = {}
std::set<YulString> const& _externallyUsedIdentifiers = {},
std::shared_ptr<u256> _externalFreeMemoryPointerInitializer = {}
);
/// Ensures that specified sequence of step abbreviations is well-formed and can be executed.
@@ -91,10 +92,18 @@ private:
std::set<YulString> const& _externallyUsedIdentifiers,
Debug _debug,
Block& _ast,
std::optional<size_t> expectedExecutionsPerDeployment
std::optional<size_t> expectedExecutionsPerDeployment,
std::shared_ptr<u256> _externalFreeMemoryPointerInitializer = {}
):
m_dispenser{_dialect, _ast, _externallyUsedIdentifiers},
m_context{_dialect, m_dispenser, _externallyUsedIdentifiers, expectedExecutionsPerDeployment},
m_context{
_dialect,
m_dispenser,
_externallyUsedIdentifiers,
expectedExecutionsPerDeployment,
_externalFreeMemoryPointerInitializer
},
m_debug(_debug)
{}