mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Allow stack limit evasion in system yul routines during old code generation.
This commit is contained in:
@@ -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{};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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() > langutil::EVMVersion::homestead())
|
||||
{
|
||||
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)};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
{}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user