Skip to content

Commit 61160aa

Browse files
author
Leo Alt
committed
Add constraints correlating address(this).balance and msg.value
1 parent c69f919 commit 61160aa

File tree

7 files changed

+123
-23
lines changed

7 files changed

+123
-23
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Language Features:
66
Compiler Features:
77
* Immutable variables can be read at construction time once they are initialized.
88
* SMTChecker: Support low level ``call`` as external calls to unknown code.
9+
* SMTChecker: Add constraints to better correlate ``address(this).balance`` and ``msg.value``.
910

1011

1112
Bugfixes:

libsmtutil/Sorts.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,15 @@ struct TupleSort: public Sort
159159
name(std::move(_name)),
160160
members(std::move(_members)),
161161
components(std::move(_components))
162-
{}
162+
{
163+
for (size_t i = 0; i < members.size(); ++i)
164+
memberToIndex[members.at(i)] = i;
165+
}
166+
167+
SortPointer memberSort(std::string const& _member)
168+
{
169+
return components.at(memberToIndex.at(_member));
170+
}
163171

164172
bool operator==(Sort const& _other) const override
165173
{
@@ -186,8 +194,10 @@ struct TupleSort: public Sort
186194
std::string const name;
187195
std::vector<std::string> const members;
188196
std::vector<SortPointer> const components;
197+
std::map<std::string, size_t> memberToIndex;
189198
};
190199

200+
191201
/** Frequently used sorts.*/
192202
struct SortProvider
193203
{

libsolidity/formal/CHC.cpp

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,13 @@ void CHC::endVisit(ContractDefinition const& _contract)
169169
smtutil::Expression zeroes(true);
170170
for (auto var: stateVariablesIncludingInheritedAndPrivate(_contract))
171171
zeroes = zeroes && currentValue(*var) == smt::zeroValue(var->type());
172-
addRule(smtutil::Expression::implies(initialConstraints(_contract) && zeroes, predicate(entry)), entry.functor().name);
172+
// The contract's address might already have funds before deployment,
173+
// so the balance must be at least `msg.value`, but not equals.
174+
auto initialBalanceConstraint = state().balance(state().thisAddress()) >= state().txMember("msg.value");
175+
addRule(smtutil::Expression::implies(
176+
initialConstraints(_contract) && zeroes && initialBalanceConstraint,
177+
predicate(entry)
178+
), entry.functor().name);
173179
setCurrentBlock(entry);
174180

175181
solAssert(!m_errorDest, "");
@@ -315,11 +321,16 @@ void CHC::endVisit(FunctionDefinition const& _function)
315321
shouldAnalyze(*m_currentContract)
316322
)
317323
{
318-
auto sum = summary(_function);
324+
defineExternalFunctionInterface(_function, *m_currentContract);
325+
setCurrentBlock(*m_interfaces.at(m_currentContract));
326+
327+
// Create the rule
328+
// interface \land externalFunctionEntry => interface'
319329
auto ifacePre = smt::interfacePre(*m_interfaces.at(m_currentContract), *m_currentContract, m_context);
320-
auto txConstraints = state().txTypeConstraints() && state().txFunctionConstraints(_function);
321-
m_queryPlaceholders[&_function].push_back({txConstraints && sum, errorFlag().currentValue(), ifacePre});
322-
connectBlocks(ifacePre, interface(), txConstraints && sum && errorFlag().currentValue() == 0);
330+
auto sum = externalSummary(_function);
331+
332+
m_queryPlaceholders[&_function].push_back({sum, errorFlag().currentValue(), ifacePre});
333+
connectBlocks(ifacePre, interface(), sum && errorFlag().currentValue() == 0);
323334
}
324335

325336
m_currentFunction = nullptr;
@@ -949,6 +960,7 @@ void CHC::resetSourceAnalysis()
949960
m_queryPlaceholders.clear();
950961
m_callGraph.clear();
951962
m_summaries.clear();
963+
m_externalSummaries.clear();
952964
m_interfaces.clear();
953965
m_nondetInterfaces.clear();
954966
m_constructorSummaries.clear();
@@ -1128,6 +1140,8 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
11281140

11291141
if (!function->isConstructor() && function->isPublic() && resolved.count(function))
11301142
{
1143+
m_externalSummaries[contract].emplace(function, createSummaryBlock(*function, *contract));
1144+
11311145
auto state1 = stateVariablesAtIndex(1, *contract);
11321146
auto state2 = stateVariablesAtIndex(2, *contract);
11331147

@@ -1144,12 +1158,41 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
11441158
applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 1); }) +
11451159
applyMap(function->returnParameters(), [this](auto _var) { return valueAtIndex(*_var, 1); });
11461160

1147-
connectBlocks(nondetPre, nondetPost, errorPre == 0 && (*m_summaries.at(contract).at(function))(args));
1161+
connectBlocks(nondetPre, nondetPost, errorPre == 0 && (*m_externalSummaries.at(contract).at(function))(args));
11481162
}
11491163
}
11501164
}
11511165
}
11521166

1167+
void CHC::defineExternalFunctionInterface(FunctionDefinition const& _function, ContractDefinition const& _contract)
1168+
{
1169+
// Create a rule that represents an external call to this function.
1170+
// This contains more things than the function body itself,
1171+
// such as balance updates because of ``msg.value``.
1172+
auto functionEntryBlock = createBlock(&_function, PredicateType::FunctionBlock);
1173+
auto functionPred = predicate(*functionEntryBlock);
1174+
addRule(functionPred, functionPred.name);
1175+
setCurrentBlock(*functionEntryBlock);
1176+
1177+
m_context.addAssertion(initialConstraints(_contract, &_function));
1178+
m_context.addAssertion(state().txTypeConstraints() && state().txFunctionConstraints(_function));
1179+
1180+
// The contract may have received funds through a selfdestruct or
1181+
// block.coinbase, which do not trigger calls into the contract.
1182+
// So the only constraint we can add here is that the balance of
1183+
// the contract grows by at least `msg.value`.
1184+
SymbolicIntVariable k{TypeProvider::uint256(), TypeProvider::uint256(), "funds_" + to_string(m_context.newUniqueId()), m_context};
1185+
m_context.addAssertion(k.currentValue() >= state().txMember("msg.value"));
1186+
// Assume that address(this).balance cannot overflow.
1187+
m_context.addAssertion(smt::symbolicUnknownConstraints(state().balance(state().thisAddress()) + k.currentValue(), TypeProvider::uint256()));
1188+
state().addBalance(state().thisAddress(), k.currentValue());
1189+
1190+
errorFlag().increaseIndex();
1191+
m_context.addAssertion(summaryCall(_function));
1192+
1193+
connectBlocks(functionPred, externalSummary(_function));
1194+
}
1195+
11531196
void CHC::defineContractInitializer(ContractDefinition const& _contract, ContractDefinition const& _contextContract)
11541197
{
11551198
m_contractInitializers[&_contextContract][&_contract] = createConstructorBlock(_contract, "contract_initializer");
@@ -1220,12 +1263,34 @@ smtutil::Expression CHC::summary(FunctionDefinition const& _function, ContractDe
12201263
return smt::function(*m_summaries.at(&_contract).at(&_function), &_contract, m_context);
12211264
}
12221265

1266+
smtutil::Expression CHC::summaryCall(FunctionDefinition const& _function, ContractDefinition const& _contract)
1267+
{
1268+
return smt::functionCall(*m_summaries.at(&_contract).at(&_function), &_contract, m_context);
1269+
}
1270+
1271+
smtutil::Expression CHC::externalSummary(FunctionDefinition const& _function, ContractDefinition const& _contract)
1272+
{
1273+
return smt::function(*m_externalSummaries.at(&_contract).at(&_function), &_contract, m_context);
1274+
}
1275+
12231276
smtutil::Expression CHC::summary(FunctionDefinition const& _function)
12241277
{
12251278
solAssert(m_currentContract, "");
12261279
return summary(_function, *m_currentContract);
12271280
}
12281281

1282+
smtutil::Expression CHC::summaryCall(FunctionDefinition const& _function)
1283+
{
1284+
solAssert(m_currentContract, "");
1285+
return summaryCall(_function, *m_currentContract);
1286+
}
1287+
1288+
smtutil::Expression CHC::externalSummary(FunctionDefinition const& _function)
1289+
{
1290+
solAssert(m_currentContract, "");
1291+
return externalSummary(_function, *m_currentContract);
1292+
}
1293+
12291294
Predicate const* CHC::createBlock(ASTNode const* _node, PredicateType _predType, string const& _prefix)
12301295
{
12311296
auto block = createSymbolicBlock(

libsolidity/formal/CHC.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ class CHC: public SMTEncoder
164164
/// in a given _source.
165165
void defineInterfacesAndSummaries(SourceUnit const& _source);
166166

167+
/// Creates the rule
168+
/// summary_function \land transaction_entry_constraints => external_summary_function
169+
/// This is needed to add these transaction entry constraints which include
170+
/// potential balance increase by external means, for example.
171+
void defineExternalFunctionInterface(FunctionDefinition const& _function, ContractDefinition const& _contract);
172+
167173
/// Creates a CHC system that, for a given contract,
168174
/// - initializes its state variables (as 0 or given value, if any).
169175
/// - "calls" the explicit constructor function of the contract, if any.
@@ -225,6 +231,13 @@ class CHC: public SMTEncoder
225231
/// @returns a predicate that defines a function summary.
226232
smtutil::Expression summary(FunctionDefinition const& _function);
227233
smtutil::Expression summary(FunctionDefinition const& _function, ContractDefinition const& _contract);
234+
/// @returns a predicate that applies a function summary
235+
/// over the constrained variables.
236+
smtutil::Expression summaryCall(FunctionDefinition const& _function);
237+
smtutil::Expression summaryCall(FunctionDefinition const& _function, ContractDefinition const& _contract);
238+
/// @returns a predicate that defines an external function summary.
239+
smtutil::Expression externalSummary(FunctionDefinition const& _function);
240+
smtutil::Expression externalSummary(FunctionDefinition const& _function, ContractDefinition const& _contract);
228241
//@}
229242

230243
/// Solver related.
@@ -317,6 +330,9 @@ class CHC: public SMTEncoder
317330

318331
/// Function predicates.
319332
std::map<ContractDefinition const*, std::map<FunctionDefinition const*, Predicate const*>> m_summaries;
333+
334+
/// External function predicates.
335+
std::map<ContractDefinition const*, std::map<FunctionDefinition const*, Predicate const*>> m_externalSummaries;
320336
//@}
321337

322338
/// Variables.

libsolidity/formal/SMTEncoder.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2283,9 +2283,7 @@ void SMTEncoder::resetStorageVariables()
22832283

22842284
void SMTEncoder::resetBalances()
22852285
{
2286-
// TODO this should be changed to `balances` only
2287-
// when `state` gets more members.
2288-
state().newState();
2286+
state().newBalances();
22892287
}
22902288

22912289
void SMTEncoder::resetReferences(VariableDeclaration const& _varDecl)

libsolidity/formal/SymbolicState.cpp

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ smtutil::Expression SymbolicState::blockhash(smtutil::Expression _blockNumber) c
104104
return smtutil::Expression::select(m_tx.member("blockhash"), move(_blockNumber));
105105
}
106106

107+
void SymbolicState::newBalances()
108+
{
109+
auto tupleSort = dynamic_pointer_cast<TupleSort>(stateSort());
110+
auto balanceSort = tupleSort->components.at(tupleSort->memberToIndex.at("balances"));
111+
SymbolicVariable newBalances(balanceSort, "fresh_balances_" + to_string(m_context.newUniqueId()), m_context);
112+
m_state.assignMember("balances", newBalances.currentValue());
113+
}
114+
107115
void SymbolicState::transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value)
108116
{
109117
unsigned indexBefore = m_state.index();
@@ -121,6 +129,16 @@ void SymbolicState::transfer(smtutil::Expression _from, smtutil::Expression _to,
121129
m_context.addAssertion(m_state.value() == newState);
122130
}
123131

132+
void SymbolicState::addBalance(smtutil::Expression _address, smtutil::Expression _value)
133+
{
134+
auto newBalances = smtutil::Expression::store(
135+
balances(),
136+
_address,
137+
balance(_address) + move(_value)
138+
);
139+
m_state.assignMember("balances", newBalances);
140+
}
141+
124142
smtutil::Expression SymbolicState::txMember(string const& _member) const
125143
{
126144
return m_tx.member(_member);
@@ -181,16 +199,6 @@ void SymbolicState::prepareForSourceUnit(SourceUnit const& _source)
181199

182200
/// Private helpers.
183201

184-
void SymbolicState::addBalance(smtutil::Expression _address, smtutil::Expression _value)
185-
{
186-
auto newBalances = smtutil::Expression::store(
187-
balances(),
188-
_address,
189-
balance(_address) + move(_value)
190-
);
191-
m_state.assignMember("balances", newBalances);
192-
}
193-
194202
void SymbolicState::buildABIFunctions(set<FunctionCall const*> const& _abiFunctions)
195203
{
196204
map<string, SortPointer> functions;

libsolidity/formal/SymbolicState.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ class SymbolicState
108108
smtutil::Expression state(unsigned _idx) const { return m_state.value(_idx); }
109109
smtutil::SortPointer const& stateSort() const { return m_state.sort(); }
110110
void newState() { m_state.newVar(); }
111+
112+
void newBalances();
111113
/// @returns the symbolic balances.
112114
smtutil::Expression balances() const;
113115
/// @returns the symbolic balance of address `this`.
@@ -117,6 +119,9 @@ class SymbolicState
117119

118120
/// Transfer _value from _from to _to.
119121
void transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value);
122+
123+
/// Adds _value to _account's balance.
124+
void addBalance(smtutil::Expression _account, smtutil::Expression _value);
120125
//@}
121126

122127
/// Transaction data.
@@ -163,9 +168,6 @@ class SymbolicState
163168
//@}
164169

165170
private:
166-
/// Adds _value to _account's balance.
167-
void addBalance(smtutil::Expression _account, smtutil::Expression _value);
168-
169171
/// Builds m_abi based on the abi.* calls _abiFunctions.
170172
void buildABIFunctions(std::set<FunctionCall const*> const& _abiFunctions);
171173

0 commit comments

Comments
 (0)