mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Warn if type(..).runtimeCode is used with assembly in the constructor.
This commit is contained in:
parent
0bfdaa500a
commit
4669b06ab4
@ -32,6 +32,56 @@ using namespace dev;
|
|||||||
using namespace langutil;
|
using namespace langutil;
|
||||||
using namespace dev::solidity;
|
using namespace dev::solidity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class that determines whether a contract's constructor uses inline assembly.
|
||||||
|
*/
|
||||||
|
class dev::solidity::ConstructorUsesAssembly
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @returns true if and only if the contract's or any of its bases' constructors
|
||||||
|
/// use inline assembly.
|
||||||
|
bool check(ContractDefinition const& _contract)
|
||||||
|
{
|
||||||
|
for (auto const* base: _contract.annotation().linearizedBaseContracts)
|
||||||
|
if (checkInternal(*base))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Checker: public ASTConstVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Checker(FunctionDefinition const& _f) { _f.accept(*this); }
|
||||||
|
bool visit(InlineAssembly const&) override { assemblySeen = true; return false; }
|
||||||
|
bool assemblySeen = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool checkInternal(ContractDefinition const& _contract)
|
||||||
|
{
|
||||||
|
if (!m_usesAssembly.count(&_contract))
|
||||||
|
{
|
||||||
|
bool usesAssembly = false;
|
||||||
|
if (_contract.constructor())
|
||||||
|
usesAssembly = Checker{*_contract.constructor()}.assemblySeen;
|
||||||
|
m_usesAssembly[&_contract] = usesAssembly;
|
||||||
|
}
|
||||||
|
return m_usesAssembly[&_contract];
|
||||||
|
}
|
||||||
|
|
||||||
|
map<ContractDefinition const*, bool> m_usesAssembly;
|
||||||
|
};
|
||||||
|
|
||||||
|
StaticAnalyzer::StaticAnalyzer(ErrorReporter& _errorReporter):
|
||||||
|
m_errorReporter(_errorReporter)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticAnalyzer::~StaticAnalyzer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit)
|
bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit)
|
||||||
{
|
{
|
||||||
_sourceUnit.accept(*this);
|
_sourceUnit.accept(*this);
|
||||||
@ -152,6 +202,18 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
|||||||
_memberAccess.location(),
|
_memberAccess.location(),
|
||||||
"\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""
|
"\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""
|
||||||
);
|
);
|
||||||
|
else if (type->kind() == MagicType::Kind::MetaType && _memberAccess.memberName() == "runtimeCode")
|
||||||
|
{
|
||||||
|
if (!m_constructorUsesAssembly)
|
||||||
|
m_constructorUsesAssembly = make_unique<ConstructorUsesAssembly>();
|
||||||
|
ContractType const& contract = dynamic_cast<ContractType const&>(*type->typeArgument());
|
||||||
|
if (m_constructorUsesAssembly->check(contract.contractDefinition()))
|
||||||
|
m_errorReporter.warning(
|
||||||
|
_memberAccess.location(),
|
||||||
|
"The constructor of the contract (or its base) uses inline assembly. "
|
||||||
|
"Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode."
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_memberAccess.memberName() == "callcode")
|
if (_memberAccess.memberName() == "callcode")
|
||||||
|
@ -38,6 +38,8 @@ namespace dev
|
|||||||
namespace solidity
|
namespace solidity
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class ConstructorUsesAssembly;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The module that performs static analysis on the AST.
|
* The module that performs static analysis on the AST.
|
||||||
@ -49,7 +51,8 @@ class StaticAnalyzer: private ASTConstVisitor
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// @param _errorReporter provides the error logging functionality.
|
/// @param _errorReporter provides the error logging functionality.
|
||||||
explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
|
explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter);
|
||||||
|
~StaticAnalyzer();
|
||||||
|
|
||||||
/// Performs static analysis on the given source unit and all of its sub-nodes.
|
/// Performs static analysis on the given source unit and all of its sub-nodes.
|
||||||
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
|
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
|
||||||
@ -85,6 +88,10 @@ private:
|
|||||||
/// when traversing.
|
/// when traversing.
|
||||||
std::map<std::pair<size_t, VariableDeclaration const*>, int> m_localVarUseCount;
|
std::map<std::pair<size_t, VariableDeclaration const*>, int> m_localVarUseCount;
|
||||||
|
|
||||||
|
/// Cache that holds information about whether a contract's constructor
|
||||||
|
/// uses inline assembly.
|
||||||
|
std::unique_ptr<ConstructorUsesAssembly> m_constructorUsesAssembly;
|
||||||
|
|
||||||
FunctionDefinition const* m_currentFunction = nullptr;
|
FunctionDefinition const* m_currentFunction = nullptr;
|
||||||
|
|
||||||
/// Flag that indicates a constructor.
|
/// Flag that indicates a constructor.
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
contract Test {
|
||||||
|
function f() public pure returns (uint) {
|
||||||
|
return type(C).runtimeCode.length +
|
||||||
|
type(D).runtimeCode.length +
|
||||||
|
type(C).creationCode.length +
|
||||||
|
type(D).creationCode.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract C {
|
||||||
|
constructor() public { assembly {} }
|
||||||
|
}
|
||||||
|
contract D is C {
|
||||||
|
constructor() public {}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning: (77-96): The constructor of the contract (or its base) uses inline assembly. Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode.
|
||||||
|
// Warning: (118-137): The constructor of the contract (or its base) uses inline assembly. Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode.
|
Loading…
Reference in New Issue
Block a user