mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #9885 from ethereum/smt_array_slices
[SMTChecker] Support array slices
This commit is contained in:
commit
87e1934bee
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
* SMTChecker: Support ``addmod`` and ``mulmod``.
|
* SMTChecker: Support ``addmod`` and ``mulmod``.
|
||||||
|
* SMTChecker: Support array slices.
|
||||||
* Optimizer: Optimize ``exp`` when base is -1.
|
* Optimizer: Optimize ``exp`` when base is -1.
|
||||||
* Code generator: Implemented events with function type as one of its indexed parameters.
|
* Code generator: Implemented events with function type as one of its indexed parameters.
|
||||||
* General: Option to stop compilation after parsing stage. Can be used with ``solc --stop-after parsing``
|
* General: Option to stop compilation after parsing stage. Can be used with ``solc --stop-after parsing``
|
||||||
|
@ -94,6 +94,8 @@ set(sources
|
|||||||
codegen/ir/IRLValue.h
|
codegen/ir/IRLValue.h
|
||||||
codegen/ir/IRVariable.cpp
|
codegen/ir/IRVariable.cpp
|
||||||
codegen/ir/IRVariable.h
|
codegen/ir/IRVariable.h
|
||||||
|
formal/ArraySlicePredicate.cpp
|
||||||
|
formal/ArraySlicePredicate.h
|
||||||
formal/BMC.cpp
|
formal/BMC.cpp
|
||||||
formal/BMC.h
|
formal/BMC.h
|
||||||
formal/CHC.cpp
|
formal/CHC.cpp
|
||||||
|
91
libsolidity/formal/ArraySlicePredicate.cpp
Normal file
91
libsolidity/formal/ArraySlicePredicate.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
|
||||||
|
#include <libsolidity/formal/ArraySlicePredicate.h>
|
||||||
|
|
||||||
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::smtutil;
|
||||||
|
using namespace solidity::frontend;
|
||||||
|
using namespace solidity::frontend::smt;
|
||||||
|
|
||||||
|
map<string, ArraySlicePredicate::SliceData> ArraySlicePredicate::m_slicePredicates;
|
||||||
|
|
||||||
|
pair<bool, ArraySlicePredicate::SliceData const&> ArraySlicePredicate::create(SortPointer _sort, EncodingContext& _context)
|
||||||
|
{
|
||||||
|
solAssert(_sort->kind == Kind::Tuple, "");
|
||||||
|
auto tupleSort = dynamic_pointer_cast<TupleSort>(_sort);
|
||||||
|
solAssert(tupleSort, "");
|
||||||
|
|
||||||
|
auto tupleName = tupleSort->name;
|
||||||
|
if (m_slicePredicates.count(tupleName))
|
||||||
|
return {true, m_slicePredicates.at(tupleName)};
|
||||||
|
|
||||||
|
auto sort = tupleSort->components.at(0);
|
||||||
|
solAssert(sort->kind == Kind::Array, "");
|
||||||
|
|
||||||
|
smt::SymbolicArrayVariable aVar{sort, "a_" + tupleName, _context };
|
||||||
|
smt::SymbolicArrayVariable bVar{sort, "b_" + tupleName, _context};
|
||||||
|
smt::SymbolicIntVariable startVar{TypeProvider::uint256(), TypeProvider::uint256(), "start_" + tupleName, _context};
|
||||||
|
smt::SymbolicIntVariable endVar{TypeProvider::uint256(), TypeProvider::uint256(), "end_" + tupleName, _context };
|
||||||
|
smt::SymbolicIntVariable iVar{TypeProvider::uint256(), TypeProvider::uint256(), "i_" + tupleName, _context};
|
||||||
|
|
||||||
|
vector<SortPointer> domain{sort, sort, startVar.sort(), endVar.sort()};
|
||||||
|
auto sliceSort = make_shared<FunctionSort>(domain, SortProvider::boolSort);
|
||||||
|
Predicate const& slice = *Predicate::create(sliceSort, "array_slice_" + tupleName, PredicateType::Custom, _context);
|
||||||
|
|
||||||
|
domain.emplace_back(iVar.sort());
|
||||||
|
auto predSort = make_shared<FunctionSort>(domain, SortProvider::boolSort);
|
||||||
|
Predicate const& header = *Predicate::create(predSort, "array_slice_header_" + tupleName, PredicateType::Custom, _context);
|
||||||
|
Predicate const& loop = *Predicate::create(predSort, "array_slice_loop_" + tupleName, PredicateType::Custom, _context);
|
||||||
|
|
||||||
|
auto a = aVar.elements();
|
||||||
|
auto b = bVar.elements();
|
||||||
|
auto start = startVar.currentValue();
|
||||||
|
auto end = endVar.currentValue();
|
||||||
|
auto i = iVar.currentValue();
|
||||||
|
|
||||||
|
auto rule1 = smtutil::Expression::implies(
|
||||||
|
end > start,
|
||||||
|
header({a, b, start, end, 0})
|
||||||
|
);
|
||||||
|
|
||||||
|
auto rule2 = smtutil::Expression::implies(
|
||||||
|
header({a, b, start, end, i}) && i >= (end - start),
|
||||||
|
slice({a, b, start, end})
|
||||||
|
);
|
||||||
|
|
||||||
|
auto rule3 = smtutil::Expression::implies(
|
||||||
|
header({a, b, start, end, i}) && i >= 0 && i < (end - start),
|
||||||
|
loop({a, b, start, end, i})
|
||||||
|
);
|
||||||
|
|
||||||
|
auto b_i = smtutil::Expression::select(b, i);
|
||||||
|
auto a_start_i = smtutil::Expression::select(a, start + i);
|
||||||
|
auto rule4 = smtutil::Expression::implies(
|
||||||
|
loop({a, b, start, end, i}) && b_i == a_start_i,
|
||||||
|
header({a, b, start, end, i + 1})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {false, m_slicePredicates[tupleName] = {
|
||||||
|
{&slice, &header, &loop},
|
||||||
|
{move(rule1), move(rule2), move(rule3), move(rule4)}
|
||||||
|
}};
|
||||||
|
}
|
62
libsolidity/formal/ArraySlicePredicate.h
Normal file
62
libsolidity/formal/ArraySlicePredicate.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
|
||||||
|
#include <libsolidity/formal/EncodingContext.h>
|
||||||
|
#include <libsolidity/formal/Predicate.h>
|
||||||
|
#include <libsolidity/formal/SymbolicVariables.h>
|
||||||
|
|
||||||
|
#include <libsmtutil/Sorts.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace solidity::frontend
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the set of rules to compute an array slice.
|
||||||
|
* Rules:
|
||||||
|
* 1. end > start => ArraySliceHeader(a, b, start, end, 0)
|
||||||
|
* 2. ArraySliceHeader(a, b, start, end, i) && i >= (end - start) => ArraySlice(a, b, start, end)
|
||||||
|
* 3. ArraySliceHeader(a, b, start, end, i) && i >= 0 && i < (end - start) => ArraySliceLoop(a, b, start, end, i)
|
||||||
|
* 4. ArraySliceLoop(a, b, start, end, i) && b[i] = a[start + i] => ArraySliceHeader(a, b, start, end, i + 1)
|
||||||
|
*
|
||||||
|
* The rule to be used by CHC is ArraySlice(a, b, start, end).
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ArraySlicePredicate
|
||||||
|
{
|
||||||
|
/// Contains the predicates and rules created to compute
|
||||||
|
/// array slices for a given sort.
|
||||||
|
struct SliceData
|
||||||
|
{
|
||||||
|
std::vector<Predicate const*> predicates;
|
||||||
|
std::vector<smtutil::Expression> rules;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @returns a flag representing whether the array slice predicates had already been created before for this sort,
|
||||||
|
/// and the corresponding slice data.
|
||||||
|
static std::pair<bool, SliceData const&> create(smtutil::SortPointer _sort, smt::EncodingContext& _context);
|
||||||
|
|
||||||
|
static void reset() { m_slicePredicates.clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Maps a unique sort name to its slice data.
|
||||||
|
static std::map<std::string, SliceData> m_slicePredicates;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -22,6 +22,7 @@
|
|||||||
#include <libsmtutil/Z3CHCInterface.h>
|
#include <libsmtutil/Z3CHCInterface.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <libsolidity/formal/ArraySlicePredicate.h>
|
||||||
#include <libsolidity/formal/PredicateInstance.h>
|
#include <libsolidity/formal/PredicateInstance.h>
|
||||||
#include <libsolidity/formal/PredicateSort.h>
|
#include <libsolidity/formal/PredicateSort.h>
|
||||||
#include <libsolidity/formal/SymbolicTypes.h>
|
#include <libsolidity/formal/SymbolicTypes.h>
|
||||||
@ -470,6 +471,36 @@ void CHC::endVisit(Continue const& _continue)
|
|||||||
m_currentBlock = predicate(*continueGhost);
|
m_currentBlock = predicate(*continueGhost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CHC::endVisit(IndexRangeAccess const& _range)
|
||||||
|
{
|
||||||
|
createExpr(_range);
|
||||||
|
|
||||||
|
auto baseArray = dynamic_pointer_cast<SymbolicArrayVariable>(m_context.expression(_range.baseExpression()));
|
||||||
|
auto sliceArray = dynamic_pointer_cast<SymbolicArrayVariable>(m_context.expression(_range));
|
||||||
|
solAssert(baseArray && sliceArray, "");
|
||||||
|
|
||||||
|
auto const& sliceData = ArraySlicePredicate::create(sliceArray->sort(), m_context);
|
||||||
|
if (!sliceData.first)
|
||||||
|
{
|
||||||
|
for (auto pred: sliceData.second.predicates)
|
||||||
|
m_interface->registerRelation(pred->functor());
|
||||||
|
for (auto const& rule: sliceData.second.rules)
|
||||||
|
addRule(rule, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto start = _range.startExpression() ? expr(*_range.startExpression()) : 0;
|
||||||
|
auto end = _range.endExpression() ? expr(*_range.endExpression()) : baseArray->length();
|
||||||
|
auto slicePred = (*sliceData.second.predicates.at(0))({
|
||||||
|
baseArray->elements(),
|
||||||
|
sliceArray->elements(),
|
||||||
|
start,
|
||||||
|
end
|
||||||
|
});
|
||||||
|
|
||||||
|
m_context.addAssertion(slicePred);
|
||||||
|
m_context.addAssertion(sliceArray->length() == end - start);
|
||||||
|
}
|
||||||
|
|
||||||
void CHC::visitAssert(FunctionCall const& _funCall)
|
void CHC::visitAssert(FunctionCall const& _funCall)
|
||||||
{
|
{
|
||||||
auto const& args = _funCall.arguments();
|
auto const& args = _funCall.arguments();
|
||||||
@ -688,6 +719,7 @@ void CHC::resetSourceAnalysis()
|
|||||||
m_interfaces.clear();
|
m_interfaces.clear();
|
||||||
m_nondetInterfaces.clear();
|
m_nondetInterfaces.clear();
|
||||||
Predicate::reset();
|
Predicate::reset();
|
||||||
|
ArraySlicePredicate::reset();
|
||||||
m_blockCounter = 0;
|
m_blockCounter = 0;
|
||||||
|
|
||||||
bool usesZ3 = false;
|
bool usesZ3 = false;
|
||||||
@ -994,6 +1026,9 @@ smtutil::Expression CHC::predicate(Predicate const& _block)
|
|||||||
case PredicateType::NondetInterface:
|
case PredicateType::NondetInterface:
|
||||||
// Nondeterministic interface predicates are handled differently.
|
// Nondeterministic interface predicates are handled differently.
|
||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
|
case PredicateType::Custom:
|
||||||
|
// Custom rules are handled separately.
|
||||||
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,7 @@ private:
|
|||||||
void endVisit(FunctionCall const& _node) override;
|
void endVisit(FunctionCall const& _node) override;
|
||||||
void endVisit(Break const& _node) override;
|
void endVisit(Break const& _node) override;
|
||||||
void endVisit(Continue const& _node) override;
|
void endVisit(Continue const& _node) override;
|
||||||
|
void endVisit(IndexRangeAccess const& _node) override;
|
||||||
|
|
||||||
void visitAssert(FunctionCall const& _funCall);
|
void visitAssert(FunctionCall const& _funCall);
|
||||||
void visitAddMulMod(FunctionCall const& _funCall) override;
|
void visitAddMulMod(FunctionCall const& _funCall) override;
|
||||||
|
@ -39,7 +39,8 @@ enum class PredicateType
|
|||||||
FunctionEntry,
|
FunctionEntry,
|
||||||
FunctionSummary,
|
FunctionSummary,
|
||||||
FunctionBlock,
|
FunctionBlock,
|
||||||
Error
|
Error,
|
||||||
|
Custom
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1095,11 +1095,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
|
|||||||
void SMTEncoder::endVisit(IndexRangeAccess const& _indexRangeAccess)
|
void SMTEncoder::endVisit(IndexRangeAccess const& _indexRangeAccess)
|
||||||
{
|
{
|
||||||
createExpr(_indexRangeAccess);
|
createExpr(_indexRangeAccess);
|
||||||
m_errorReporter.warning(
|
/// The actual slice is created by CHC which also assigns the length.
|
||||||
2923_error,
|
|
||||||
_indexRangeAccess.location(),
|
|
||||||
"Assertion checker does not yet implement this expression."
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMTEncoder::arrayAssignment()
|
void SMTEncoder::arrayAssignment()
|
||||||
|
12
test/libsolidity/smtCheckerTests/operators/slice.sol
Normal file
12
test/libsolidity/smtCheckerTests/operators/slice.sol
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f(bytes calldata b) external pure {
|
||||||
|
require(b[10] == 0xff);
|
||||||
|
assert(bytes(b[10:20]).length == 10);
|
||||||
|
assert(bytes(b[10:20])[0] == 0xff);
|
||||||
|
assert(bytes(b[10:20])[5] == 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning 6328: (198-232): CHC: Assertion violation happens here.
|
@ -0,0 +1,17 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f(bytes calldata b) external pure {
|
||||||
|
require(b.length == 30);
|
||||||
|
require(b[10] == 0xff);
|
||||||
|
require(b[b.length - 1] == 0xaa);
|
||||||
|
assert(bytes(b[10:]).length == 20);
|
||||||
|
assert(bytes(b[10:])[0] == 0xff);
|
||||||
|
assert(bytes(b[10:])[5] == 0xff);
|
||||||
|
assert(bytes(b[10:])[19] == 0xaa);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning 4661: (221-253): BMC: Assertion violation happens here.
|
||||||
|
// Warning 4661: (257-289): BMC: Assertion violation happens here.
|
||||||
|
// Warning 4661: (293-326): BMC: Assertion violation happens here.
|
@ -0,0 +1,13 @@
|
|||||||
|
pragma experimental SMTChecker;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f(bytes calldata b) external pure {
|
||||||
|
require(b[0] == 0xff);
|
||||||
|
assert(bytes(b[:20]).length == 20);
|
||||||
|
assert(bytes(b[:20])[0] == 0xff);
|
||||||
|
assert(bytes(b[:20])[5] == 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// Warning 6328: (193-225): CHC: Assertion violation happens here.
|
||||||
|
// Warning 4661: (157-189): BMC: Assertion violation happens here.
|
@ -8,6 +8,3 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// Warning 2923: (94-109): Assertion checker does not yet implement this expression.
|
|
||||||
// Warning 2923: (113-128): Assertion checker does not yet implement this expression.
|
|
||||||
// Warning 2923: (132-165): Assertion checker does not yet implement this expression.
|
|
||||||
|
@ -5,5 +5,4 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// Warning 2923: (143-152): Assertion checker does not yet implement this expression.
|
|
||||||
// Warning 4588: (126-154): Assertion checker does not yet implement this type of function call.
|
// Warning 4588: (126-154): Assertion checker does not yet implement this type of function call.
|
||||||
|
@ -8,6 +8,3 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// Warning 2923: (100-115): Assertion checker does not yet implement this expression.
|
|
||||||
// Warning 2923: (126-141): Assertion checker does not yet implement this expression.
|
|
||||||
// Warning 2923: (152-185): Assertion checker does not yet implement this expression.
|
|
||||||
|
Loading…
Reference in New Issue
Block a user