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:
parent
8bd358074e
commit
442666d181
@ -63,6 +63,9 @@ public:
|
|||||||
|
|
||||||
AssemblyItem(u256 _push, langutil::SourceLocation _location = langutil::SourceLocation()):
|
AssemblyItem(u256 _push, langutil::SourceLocation _location = langutil::SourceLocation()):
|
||||||
AssemblyItem(Push, std::move(_push), std::move(_location)) { }
|
AssemblyItem(Push, std::move(_push), std::move(_location)) { }
|
||||||
|
/// Used only for the free memory pointer as "memoryguard". Should probably be replaced by a separate AssemblyItemType.
|
||||||
|
AssemblyItem(std::shared_ptr<u256> _pushData, langutil::SourceLocation _location = langutil::SourceLocation()):
|
||||||
|
m_type(Push), m_data(std::move(_pushData)), m_location(std::move(_location)) { }
|
||||||
AssemblyItem(Instruction _i, langutil::SourceLocation _location = langutil::SourceLocation()):
|
AssemblyItem(Instruction _i, langutil::SourceLocation _location = langutil::SourceLocation()):
|
||||||
m_type(Operation),
|
m_type(Operation),
|
||||||
m_instruction(_i),
|
m_instruction(_i),
|
||||||
|
@ -484,7 +484,7 @@ void CompilerContext::appendInlineAssembly(
|
|||||||
obj.code = parserResult;
|
obj.code = parserResult;
|
||||||
obj.analysisInfo = make_shared<yul::AsmAnalysisInfo>(analysisInfo);
|
obj.analysisInfo = make_shared<yul::AsmAnalysisInfo>(analysisInfo);
|
||||||
|
|
||||||
optimizeYul(obj, dialect, _optimiserSettings, externallyUsedIdentifiers);
|
optimizeYul(obj, dialect, _optimiserSettings, externallyUsedIdentifiers, _system);
|
||||||
|
|
||||||
if (_system)
|
if (_system)
|
||||||
{
|
{
|
||||||
@ -531,7 +531,13 @@ void CompilerContext::appendInlineAssembly(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _dialect, OptimiserSettings const& _optimiserSettings, std::set<yul::YulString> const& _externalIdentifiers)
|
void CompilerContext::optimizeYul(
|
||||||
|
yul::Object& _object,
|
||||||
|
yul::EVMDialect const& _dialect,
|
||||||
|
OptimiserSettings const& _optimiserSettings,
|
||||||
|
std::set<yul::YulString> const& _externalIdentifiers,
|
||||||
|
bool _system
|
||||||
|
)
|
||||||
{
|
{
|
||||||
#ifdef SOL_OUTPUT_ASM
|
#ifdef SOL_OUTPUT_ASM
|
||||||
cout << yul::AsmPrinter(*dialect)(*_object.code) << endl;
|
cout << yul::AsmPrinter(*dialect)(*_object.code) << endl;
|
||||||
@ -539,6 +545,7 @@ void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _
|
|||||||
|
|
||||||
bool const isCreation = runtimeContext() != nullptr;
|
bool const isCreation = runtimeContext() != nullptr;
|
||||||
yul::GasMeter meter(_dialect, isCreation, _optimiserSettings.expectedExecutionsPerDeployment);
|
yul::GasMeter meter(_dialect, isCreation, _optimiserSettings.expectedExecutionsPerDeployment);
|
||||||
|
solAssert(m_freeMemoryInitPush, "");
|
||||||
yul::OptimiserSuite::run(
|
yul::OptimiserSuite::run(
|
||||||
_dialect,
|
_dialect,
|
||||||
&meter,
|
&meter,
|
||||||
@ -546,7 +553,8 @@ void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _
|
|||||||
_optimiserSettings.optimizeStackAllocation,
|
_optimiserSettings.optimizeStackAllocation,
|
||||||
_optimiserSettings.yulOptimiserSteps,
|
_optimiserSettings.yulOptimiserSteps,
|
||||||
isCreation? nullopt : make_optional(_optimiserSettings.expectedExecutionsPerDeployment),
|
isCreation? nullopt : make_optional(_optimiserSettings.expectedExecutionsPerDeployment),
|
||||||
_externalIdentifiers
|
_externalIdentifiers,
|
||||||
|
_system ? m_freeMemoryInitPush : shared_ptr<u256>{}
|
||||||
);
|
);
|
||||||
|
|
||||||
#ifdef SOL_OUTPUT_ASM
|
#ifdef SOL_OUTPUT_ASM
|
||||||
|
@ -276,7 +276,13 @@ public:
|
|||||||
/// Otherwise returns "revert(0, 0)".
|
/// Otherwise returns "revert(0, 0)".
|
||||||
std::string revertReasonIfDebug(std::string const& _message = "");
|
std::string revertReasonIfDebug(std::string const& _message = "");
|
||||||
|
|
||||||
void optimizeYul(yul::Object& _object, yul::EVMDialect const& _dialect, OptimiserSettings const& _optimiserSetting, std::set<yul::YulString> const& _externalIdentifiers = {});
|
void optimizeYul(
|
||||||
|
yul::Object& _object,
|
||||||
|
yul::EVMDialect const& _dialect,
|
||||||
|
OptimiserSettings const& _optimiserSetting,
|
||||||
|
std::set<yul::YulString> const& _externalIdentifiers = {},
|
||||||
|
bool _system = false
|
||||||
|
);
|
||||||
|
|
||||||
/// Appends arbitrary data to the end of the bytecode.
|
/// Appends arbitrary data to the end of the bytecode.
|
||||||
void appendToAuxiliaryData(bytes const& _data) { m_asm->appendToAuxiliaryData(_data); }
|
void appendToAuxiliaryData(bytes const& _data) { m_asm->appendToAuxiliaryData(_data); }
|
||||||
@ -309,6 +315,12 @@ public:
|
|||||||
|
|
||||||
RevertStrings revertStrings() const { return m_revertStrings; }
|
RevertStrings revertStrings() const { return m_revertStrings; }
|
||||||
|
|
||||||
|
evmasm::AssemblyItem makeFreeMemoryInitPush(u256 _value)
|
||||||
|
{
|
||||||
|
m_freeMemoryInitPush = std::make_shared<u256>(_value);
|
||||||
|
return evmasm::AssemblyItem(m_freeMemoryInitPush);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Updates source location set in the assembly.
|
/// Updates source location set in the assembly.
|
||||||
void updateSourceLocation();
|
void updateSourceLocation();
|
||||||
@ -394,6 +406,8 @@ private:
|
|||||||
std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue;
|
std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue;
|
||||||
/// Flag to check that appendYulUtilityFunctions() was called exactly once
|
/// Flag to check that appendYulUtilityFunctions() was called exactly once
|
||||||
bool m_appendYulUtilityFunctionsRan = false;
|
bool m_appendYulUtilityFunctionsRan = false;
|
||||||
|
/// The assembly item that pushes the initial value of the free memory pointer.
|
||||||
|
std::shared_ptr<u256> m_freeMemoryInitPush;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ void CompilerUtils::initialiseFreeMemoryPointer()
|
|||||||
{
|
{
|
||||||
size_t reservedMemory = m_context.reservedMemory();
|
size_t reservedMemory = m_context.reservedMemory();
|
||||||
solAssert(bigint(generalPurposeMemoryStart) + bigint(reservedMemory) < bigint(1) << 63, "");
|
solAssert(bigint(generalPurposeMemoryStart) + bigint(reservedMemory) < bigint(1) << 63, "");
|
||||||
m_context << (u256(generalPurposeMemoryStart) + reservedMemory);
|
m_context << m_context.makeFreeMemoryInitPush((u256(generalPurposeMemoryStart) + reservedMemory));
|
||||||
storeFreeMemoryPointer();
|
storeFreeMemoryPointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include <libyul/Exceptions.h>
|
#include <libyul/Exceptions.h>
|
||||||
|
|
||||||
|
#include <libsolutil/Common.h>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <set>
|
#include <set>
|
||||||
@ -39,6 +41,7 @@ struct OptimiserStepContext
|
|||||||
std::set<YulString> const& reservedIdentifiers;
|
std::set<YulString> const& reservedIdentifiers;
|
||||||
/// The value nullopt represents creation code
|
/// The value nullopt represents creation code
|
||||||
std::optional<size_t> expectedExecutionsPerDeployment;
|
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);
|
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
|
||||||
yulAssert(
|
yulAssert(evmDialect, "StackLimitEvader can only be run on objects using the EVMDialect.");
|
||||||
evmDialect && evmDialect->providesObjectAccess(),
|
|
||||||
"StackLimitEvader can only be run on objects using the EVMDialect with object access."
|
|
||||||
);
|
|
||||||
if (evmDialect && evmDialect->evmVersion() > langutil::EVMVersion::homestead())
|
if (evmDialect && evmDialect->evmVersion() > langutil::EVMVersion::homestead())
|
||||||
{
|
{
|
||||||
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(*evmDialect, _object);
|
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(*evmDialect, _object);
|
||||||
@ -165,11 +162,14 @@ void StackLimitEvader::run(
|
|||||||
{
|
{
|
||||||
yulAssert(_object.code, "");
|
yulAssert(_object.code, "");
|
||||||
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
|
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
|
||||||
yulAssert(
|
yulAssert(evmDialect, "StackLimitEvader can only be run on objects using the EVMDialect.");
|
||||||
evmDialect && evmDialect->providesObjectAccess(),
|
|
||||||
"StackLimitEvader can only be run on objects using the EVMDialect with object access."
|
|
||||||
);
|
|
||||||
|
|
||||||
|
u256 reservedMemory = 0;
|
||||||
|
|
||||||
|
if (_context.externalFreeMemoryPointerInitializer)
|
||||||
|
reservedMemory = *_context.externalFreeMemoryPointerInitializer;
|
||||||
|
else
|
||||||
|
{
|
||||||
vector<FunctionCall*> memoryGuardCalls = FunctionCallFinder::run(
|
vector<FunctionCall*> memoryGuardCalls = FunctionCallFinder::run(
|
||||||
*_object.code,
|
*_object.code,
|
||||||
"memoryguard"_yulstring
|
"memoryguard"_yulstring
|
||||||
@ -179,12 +179,13 @@ void StackLimitEvader::run(
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Make sure all calls to ``memoryguard`` we found have the same value as argument (otherwise, abort).
|
// Make sure all calls to ``memoryguard`` we found have the same value as argument (otherwise, abort).
|
||||||
u256 reservedMemory = literalArgumentValue(*memoryGuardCalls.front());
|
reservedMemory = literalArgumentValue(*memoryGuardCalls.front());
|
||||||
yulAssert(reservedMemory < u256(1) << 32 - 1, "");
|
yulAssert(reservedMemory < u256(1) << 32 - 1, "");
|
||||||
|
|
||||||
for (FunctionCall const* memoryGuardCall: memoryGuardCalls)
|
for (FunctionCall const* memoryGuardCall: memoryGuardCalls)
|
||||||
if (reservedMemory != literalArgumentValue(*memoryGuardCall))
|
if (reservedMemory != literalArgumentValue(*memoryGuardCall))
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CallGraph callGraph = CallGraphGenerator::callGraph(*_object.code);
|
CallGraph callGraph = CallGraphGenerator::callGraph(*_object.code);
|
||||||
|
|
||||||
@ -195,6 +196,11 @@ void StackLimitEvader::run(
|
|||||||
|
|
||||||
map<YulString, FunctionDefinition const*> functionDefinitions = FunctionDefinitionCollector::run(*_object.code);
|
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};
|
MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls, functionDefinitions};
|
||||||
uint64_t requiredSlots = memoryOffsetAllocator.run();
|
uint64_t requiredSlots = memoryOffsetAllocator.run();
|
||||||
yulAssert(requiredSlots < (uint64_t(1) << 32) - 1, "");
|
yulAssert(requiredSlots < (uint64_t(1) << 32) - 1, "");
|
||||||
@ -202,6 +208,9 @@ void StackLimitEvader::run(
|
|||||||
StackToMemoryMover::run(_context, reservedMemory, memoryOffsetAllocator.slotAllocations, requiredSlots, *_object.code);
|
StackToMemoryMover::run(_context, reservedMemory, memoryOffsetAllocator.slotAllocations, requiredSlots, *_object.code);
|
||||||
|
|
||||||
reservedMemory += 32 * requiredSlots;
|
reservedMemory += 32 * requiredSlots;
|
||||||
|
if (_context.externalFreeMemoryPointerInitializer)
|
||||||
|
*_context.externalFreeMemoryPointerInitializer = reservedMemory;
|
||||||
|
else
|
||||||
for (FunctionCall* memoryGuardCall: FunctionCallFinder::run(*_object.code, "memoryguard"_yulstring))
|
for (FunctionCall* memoryGuardCall: FunctionCallFinder::run(*_object.code, "memoryguard"_yulstring))
|
||||||
{
|
{
|
||||||
Literal* literal = std::get_if<Literal>(&memoryGuardCall->arguments.front());
|
Literal* literal = std::get_if<Literal>(&memoryGuardCall->arguments.front());
|
||||||
|
@ -109,10 +109,7 @@ m_nameDispenser(_context.dispenser),
|
|||||||
m_functionReturnVariables(move(_functionReturnVariables))
|
m_functionReturnVariables(move(_functionReturnVariables))
|
||||||
{
|
{
|
||||||
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
|
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
|
||||||
yulAssert(
|
yulAssert(evmDialect, "StackToMemoryMover can only be run on objects using the EVMDialect.");
|
||||||
evmDialect && evmDialect->providesObjectAccess(),
|
|
||||||
"StackToMemoryMover can only be run on objects using the EVMDialect with object access."
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StackToMemoryMover::operator()(FunctionDefinition& _functionDefinition)
|
void StackToMemoryMover::operator()(FunctionDefinition& _functionDefinition)
|
||||||
|
@ -89,7 +89,8 @@ void OptimiserSuite::run(
|
|||||||
bool _optimizeStackAllocation,
|
bool _optimizeStackAllocation,
|
||||||
string const& _optimisationSequence,
|
string const& _optimisationSequence,
|
||||||
optional<size_t> _expectedExecutionsPerDeployment,
|
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);
|
EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&_dialect);
|
||||||
@ -107,7 +108,14 @@ void OptimiserSuite::run(
|
|||||||
)(*_object.code));
|
)(*_object.code));
|
||||||
Block& ast = *_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
|
// Some steps depend on properties ensured by FunctionHoister, BlockFlattener, FunctionGrouper and
|
||||||
// ForLoopInitRewriter. Run them first to be able to run arbitrary sequences safely.
|
// ForLoopInitRewriter. Run them first to be able to run arbitrary sequences safely.
|
||||||
@ -144,10 +152,9 @@ void OptimiserSuite::run(
|
|||||||
_optimizeStackAllocation,
|
_optimizeStackAllocation,
|
||||||
stackCompressorMaxIterations
|
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);
|
StackLimitEvader::run(suite.m_context, _object);
|
||||||
}
|
}
|
||||||
else if (dynamic_cast<WasmDialect const*>(&_dialect))
|
else if (dynamic_cast<WasmDialect const*>(&_dialect))
|
||||||
|
@ -66,7 +66,8 @@ public:
|
|||||||
bool _optimizeStackAllocation,
|
bool _optimizeStackAllocation,
|
||||||
std::string const& _optimisationSequence,
|
std::string const& _optimisationSequence,
|
||||||
std::optional<size_t> _expectedExecutionsPerDeployment,
|
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.
|
/// Ensures that specified sequence of step abbreviations is well-formed and can be executed.
|
||||||
@ -91,10 +92,18 @@ private:
|
|||||||
std::set<YulString> const& _externallyUsedIdentifiers,
|
std::set<YulString> const& _externallyUsedIdentifiers,
|
||||||
Debug _debug,
|
Debug _debug,
|
||||||
Block& _ast,
|
Block& _ast,
|
||||||
std::optional<size_t> expectedExecutionsPerDeployment
|
std::optional<size_t> expectedExecutionsPerDeployment,
|
||||||
|
std::shared_ptr<u256> _externalFreeMemoryPointerInitializer = {}
|
||||||
|
|
||||||
):
|
):
|
||||||
m_dispenser{_dialect, _ast, _externallyUsedIdentifiers},
|
m_dispenser{_dialect, _ast, _externallyUsedIdentifiers},
|
||||||
m_context{_dialect, m_dispenser, _externallyUsedIdentifiers, expectedExecutionsPerDeployment},
|
m_context{
|
||||||
|
_dialect,
|
||||||
|
m_dispenser,
|
||||||
|
_externallyUsedIdentifiers,
|
||||||
|
expectedExecutionsPerDeployment,
|
||||||
|
_externalFreeMemoryPointerInitializer
|
||||||
|
},
|
||||||
m_debug(_debug)
|
m_debug(_debug)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -62,6 +62,6 @@ contract C {
|
|||||||
// via yul disabled because of stack issues.
|
// via yul disabled because of stack issues.
|
||||||
|
|
||||||
// ====
|
// ====
|
||||||
// compileViaYul: false
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// constructor() ->
|
// constructor() ->
|
||||||
|
Loading…
Reference in New Issue
Block a user