Skip to content

Commit 712829d

Browse files
committed
Do not convert code values in address_of expressions to SMT
Previous to this commit, C examples containing function pointers would cause INVARIANT violations due to an attempt to convert code values inside address_of expressions into SMT terms. This commit avoids performing the conversion. This is a valid approach as the decision procedure only needs to handle code addresses, not the actual code itself.
1 parent 6a7e03a commit 712829d

File tree

2 files changed

+86
-16
lines changed

2 files changed

+86
-16
lines changed

src/solvers/smt2_incremental/convert_expr_to_smt.cpp

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <algorithm>
2525
#include <functional>
2626
#include <numeric>
27+
#include <stack>
2728

2829
/// Post order visitation is used in order to construct the the smt terms bottom
2930
/// upwards without using recursion to traverse the input `exprt`. Therefore
@@ -1845,6 +1846,46 @@ exprt lower_address_of_array_index(exprt expr)
18451846
return expr;
18461847
}
18471848

1849+
/// Post order traversal where the children of a node are only visited if
1850+
/// applying the \p filter function to that node returns true. Note that this
1851+
/// function is based on the `visit_post_template` function.
1852+
void filtered_visit_post(
1853+
const exprt &_expr,
1854+
std::function<bool(const exprt &)> filter,
1855+
std::function<void(const exprt &)> visitor)
1856+
{
1857+
struct stack_entryt
1858+
{
1859+
const exprt *e;
1860+
bool operands_pushed;
1861+
explicit stack_entryt(const exprt *_e) : e(_e), operands_pushed(false)
1862+
{
1863+
}
1864+
};
1865+
1866+
std::stack<stack_entryt> stack;
1867+
1868+
stack.emplace(&_expr);
1869+
1870+
while(!stack.empty())
1871+
{
1872+
auto &top = stack.top();
1873+
if(top.operands_pushed)
1874+
{
1875+
visitor(*top.e);
1876+
stack.pop();
1877+
}
1878+
else
1879+
{
1880+
// do modification of 'top' before pushing in case 'top' isn't stable
1881+
top.operands_pushed = true;
1882+
if(filter(*top.e))
1883+
for(auto &op : top.e->operands())
1884+
stack.emplace(&op);
1885+
}
1886+
}
1887+
}
1888+
18481889
smt_termt convert_expr_to_smt(
18491890
const exprt &expr,
18501891
const smt_object_mapt &object_map,
@@ -1864,18 +1905,30 @@ smt_termt convert_expr_to_smt(
18641905
#endif
18651906
sub_expression_mapt sub_expression_map;
18661907
const auto lowered_expr = lower_address_of_array_index(expr);
1867-
lowered_expr.visit_post([&](const exprt &expr) {
1868-
const auto find_result = sub_expression_map.find(expr);
1869-
if(find_result != sub_expression_map.cend())
1870-
return;
1871-
smt_termt term = dispatch_expr_to_smt_conversion(
1872-
expr,
1873-
sub_expression_map,
1874-
object_map,
1875-
pointer_sizes,
1876-
object_size,
1877-
is_dynamic_object);
1878-
sub_expression_map.emplace_hint(find_result, expr, std::move(term));
1879-
});
1908+
filtered_visit_post(
1909+
lowered_expr,
1910+
[](const exprt &expr) {
1911+
// Code values inside "address of" expressions do not need to be converted
1912+
// as the "address of" conversion only depends on the object identifier.
1913+
// Avoiding the conversion side steps a need to convert arbitrary code to
1914+
// SMT terms.
1915+
const auto address_of = expr_try_dynamic_cast<address_of_exprt>(expr);
1916+
if(!address_of)
1917+
return true;
1918+
return !can_cast_type<code_typet>(address_of->object().type());
1919+
},
1920+
[&](const exprt &expr) {
1921+
const auto find_result = sub_expression_map.find(expr);
1922+
if(find_result != sub_expression_map.cend())
1923+
return;
1924+
smt_termt term = dispatch_expr_to_smt_conversion(
1925+
expr,
1926+
sub_expression_map,
1927+
object_map,
1928+
pointer_sizes,
1929+
object_size,
1930+
is_dynamic_object);
1931+
sub_expression_map.emplace_hint(find_result, expr, std::move(term));
1932+
});
18801933
return std::move(sub_expression_map.at(lowered_expr));
18811934
}

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,17 @@ get_problem_messages(const smt_responset &response)
6969
/// returned by this`gather_dependent_expressions` function.
7070
/// \details `symbol_exprt`, `array_exprt` and `nondet_symbol_exprt` add
7171
/// dependant expressions.
72-
static std::vector<exprt> gather_dependent_expressions(const exprt &expr)
72+
static std::vector<exprt> gather_dependent_expressions(const exprt &root_expr)
7373
{
7474
std::vector<exprt> dependent_expressions;
75-
expr.visit_pre([&](const exprt &expr_node) {
75+
76+
std::stack<const exprt *> stack;
77+
stack.push(&root_expr);
78+
79+
while(!stack.empty())
80+
{
81+
const exprt &expr_node = *stack.top();
82+
stack.pop();
7683
if(
7784
can_cast_expr<symbol_exprt>(expr_node) ||
7885
can_cast_expr<array_exprt>(expr_node) ||
@@ -82,7 +89,17 @@ static std::vector<exprt> gather_dependent_expressions(const exprt &expr)
8289
{
8390
dependent_expressions.push_back(expr_node);
8491
}
85-
});
92+
// The decision procedure does not depend on the values inside address of
93+
// code typed expressions. We can build the address without knowing the
94+
// value at that memory location. In this case the hypothetical compiled
95+
// machine instructions at the address are not relevant to solving, only
96+
// representing *which* function a pointer points to is needed.
97+
const auto address_of = expr_try_dynamic_cast<address_of_exprt>(expr_node);
98+
if(address_of && can_cast_type<code_typet>(address_of->object().type()))
99+
continue;
100+
for(auto &operand : expr_node.operands())
101+
stack.push(&operand);
102+
}
86103
return dependent_expressions;
87104
}
88105

0 commit comments

Comments
 (0)