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 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)
|
||||
{
|
||||
_sourceUnit.accept(*this);
|
||||
@ -152,6 +202,18 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
||||
_memberAccess.location(),
|
||||
"\"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")
|
||||
|
@ -38,6 +38,8 @@ namespace dev
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
class ConstructorUsesAssembly;
|
||||
|
||||
|
||||
/**
|
||||
* The module that performs static analysis on the AST.
|
||||
@ -49,7 +51,8 @@ class StaticAnalyzer: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
/// @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.
|
||||
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
|
||||
@ -85,6 +88,10 @@ private:
|
||||
/// when traversing.
|
||||
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;
|
||||
|
||||
/// 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