Skip to content

Commit 5303a9d

Browse files
authored
Merge pull request #6866 from NlightNFotis/smt_pointer_arithmetic_conversion_final
Add support for conversion of pointer arithmetic expressions to new SMT backend.
2 parents 0de701c + 34b2985 commit 5303a9d

20 files changed

+443
-15
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <stdint.h>
2+
3+
#define NULL (void *)0
4+
5+
int main()
6+
{
7+
int32_t *a;
8+
__CPROVER_assume(a != NULL);
9+
int32_t *z = a + 2 * sizeof(int32_t);
10+
11+
__CPROVER_assert(a != z, "expected successful because of pointer arithmetic");
12+
__CPROVER_assert(a == z, "expected failure because of pointer arithmetic");
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
addition_compound_expr.c
3+
--trace
4+
\[main\.assertion\.1\] line \d+ expected successful because of pointer arithmetic: SUCCESS
5+
\[main\.assertion\.2\] line \d+ expected failure because of pointer arithmetic: FAILURE
6+
^EXIT=10$
7+
^SIGNAL=0$
8+
--
9+
--
10+
This is testing the same thing as the test in addition_simple.desc, with the
11+
difference being that the addition expression here is compound, containing a
12+
more elaborate operand in the form of a multiplication containing a sizeof
13+
operator.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#define NULL (void *)0
2+
3+
int main()
4+
{
5+
int *x;
6+
int *y = x + 1;
7+
__CPROVER_assume(x != NULL);
8+
__CPROVER_assume(y != NULL);
9+
10+
__CPROVER_assert(y == x, "expected false after pointer manipulation");
11+
__CPROVER_assert(y != x, "expected true");
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
addition_simple.c
3+
--trace
4+
\[main\.assertion\.1\] line \d+ expected false after pointer manipulation: FAILURE
5+
\[main\.assertion\.2\] line \d+ expected true: SUCCESS
6+
^EXIT=10$
7+
^SIGNAL=0$
8+
--
9+
--
10+
This is testing basic pointer arithmetic by adding by incrementing a pointer's
11+
address and assigning that value to another pointer, then asserting that they
12+
don't point to the same thing.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <stdlib.h>
2+
int main()
3+
{
4+
int *x = malloc(sizeof(int));
5+
int *y = x + 3;
6+
int z = y - x;
7+
__CPROVER_assert(y == x, "expected failure after pointer manipulation");
8+
__CPROVER_assert(z == 3, "expected successful after pointer manipulation");
9+
__CPROVER_assert(z != 3, "expected failure after pointer manipulation");
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
pointer_subtraction.c
3+
--trace
4+
\[main\.assertion\.1\] line \d+ expected failure after pointer manipulation: FAILURE
5+
\[main\.assertion\.2\] line \d+ expected successful after pointer manipulation: SUCCESS
6+
\[main\.assertion\.3\] line \d+ expected failure after pointer manipulation: FAILURE
7+
z=3
8+
^EXIT=10$
9+
^SIGNAL=0$
10+
--
11+
--
12+
This test is testing that the subtraction between two pointers (giving us the
13+
increment between the two pointers) works as it should.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <stdlib.h>
2+
int main()
3+
{
4+
int *x = malloc(sizeof(int));
5+
float *y = x + 3;
6+
int z = y - x;
7+
__CPROVER_assert(y == x, "expected failure after pointer manipulation");
8+
__CPROVER_assert(z == 3, "expected successful after pointer manipulation");
9+
__CPROVER_assert(z != 3, "expected failure after pointer manipulation");
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
pointer_subtraction_diff_types.c
3+
--trace
4+
^Reason: only pointers of the same object type can be subtracted.
5+
^EXIT=(134|127)$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This test is for making sure that we only subtract pointers with the
10+
same underlying (base) type.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#define NULL (void *)0
2+
3+
int main()
4+
{
5+
int *x;
6+
unsigned int z;
7+
__CPROVER_assume(z < 3);
8+
__CPROVER_assume(z > 1);
9+
int *y = x - z;
10+
__CPROVER_assume(x != NULL);
11+
__CPROVER_assume(y != NULL);
12+
13+
__CPROVER_assert(y == x, "expected failure after pointer manipulation");
14+
__CPROVER_assert(y != x, "expected success after pointer manipulation");
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
pointer_subtraction_unsigned.c
3+
--trace
4+
\[main\.assertion\.1\] line \d+ expected failure after pointer manipulation: FAILURE
5+
\[main\.assertion\.2\] line \d+ expected success after pointer manipulation: SUCCESS
6+
^EXIT=10$
7+
^SIGNAL=0$
8+
--
9+
--
10+
The test is similar to the one in `pointer_subtraction.desc`, but with different
11+
types in the subtraction operands.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#define NULL (void *)0
2+
3+
int main()
4+
{
5+
int *x;
6+
int *y = x - 2;
7+
__CPROVER_assume(x != NULL);
8+
__CPROVER_assume(y != NULL);
9+
10+
__CPROVER_assert(y == x, "expected failure after pointer manipulation");
11+
__CPROVER_assert(y != x, "expected successful after pointer manipulation");
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
subtraction_simple.c
3+
--trace
4+
\[main\.assertion\.1\] line \d+ expected failure after pointer manipulation: FAILURE
5+
\[main\.assertion\.2\] line \d+ expected successful after pointer manipulation: SUCCESS
6+
^EXIT=10$
7+
^SIGNAL=0$
8+
--
9+
--
10+
This test is similar to the one in `addition_simple.desc`, but testing end-to-end
11+
the conversion of a subtraction case of pointer arithmetic.

src/solvers/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ SRC = $(BOOLEFORCE_SRC) \
196196
smt2_incremental/construct_value_expr_from_smt.cpp \
197197
smt2_incremental/convert_expr_to_smt.cpp \
198198
smt2_incremental/object_tracking.cpp \
199+
smt2_incremental/type_size_mapping.cpp \
199200
smt2_incremental/smt_bit_vector_theory.cpp \
200201
smt2_incremental/smt_commands.cpp \
201202
smt2_incremental/smt_core_theory.cpp \

src/solvers/smt2_incremental/convert_expr_to_smt.cpp

+79-5
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,8 @@ static optionalt<smt_termt> try_relational_conversion(
587587

588588
static smt_termt convert_expr_to_smt(
589589
const plus_exprt &plus,
590-
const sub_expression_mapt &converted)
590+
const sub_expression_mapt &converted,
591+
const type_size_mapt &pointer_sizes)
591592
{
592593
if(std::all_of(
593594
plus.operands().cbegin(), plus.operands().cend(), [](exprt operand) {
@@ -597,6 +598,43 @@ static smt_termt convert_expr_to_smt(
597598
return convert_multiary_operator_to_terms(
598599
plus, converted, smt_bit_vector_theoryt::add);
599600
}
601+
else if(can_cast_type<pointer_typet>(plus.type()))
602+
{
603+
INVARIANT(
604+
plus.operands().size() == 2,
605+
"We are only handling a binary version of plus when it has a pointer "
606+
"operand");
607+
608+
exprt pointer;
609+
exprt scalar;
610+
for(auto &operand : plus.operands())
611+
{
612+
if(can_cast_type<pointer_typet>(operand.type()))
613+
{
614+
pointer = operand;
615+
}
616+
else
617+
{
618+
scalar = operand;
619+
}
620+
}
621+
622+
// We need to ensure that we follow this code path only if the expression
623+
// our assumptions about the structure of the addition expression hold.
624+
INVARIANT(
625+
!can_cast_type<pointer_typet>(scalar.type()),
626+
"An addition expression with both operands being pointers when they are "
627+
"not dereferenced is malformed");
628+
629+
const pointer_typet pointer_type =
630+
*type_try_dynamic_cast<pointer_typet>(pointer.type());
631+
const auto base_type = pointer_type.base_type();
632+
const auto pointer_size = pointer_sizes.at(base_type);
633+
634+
return smt_bit_vector_theoryt::add(
635+
converted.at(pointer),
636+
smt_bit_vector_theoryt::multiply(converted.at(scalar), pointer_size));
637+
}
600638
else
601639
{
602640
UNIMPLEMENTED_FEATURE(
@@ -606,17 +644,51 @@ static smt_termt convert_expr_to_smt(
606644

607645
static smt_termt convert_expr_to_smt(
608646
const minus_exprt &minus,
609-
const sub_expression_mapt &converted)
647+
const sub_expression_mapt &converted,
648+
const type_size_mapt &pointer_sizes)
610649
{
611650
const bool both_operands_bitvector =
612651
can_cast_type<integer_bitvector_typet>(minus.lhs().type()) &&
613652
can_cast_type<integer_bitvector_typet>(minus.rhs().type());
614653

654+
const bool lhs_is_pointer = can_cast_type<pointer_typet>(minus.lhs().type());
655+
const bool rhs_is_pointer = can_cast_type<pointer_typet>(minus.rhs().type());
656+
657+
const bool both_operands_pointers = lhs_is_pointer && rhs_is_pointer;
658+
659+
// We don't really handle this - we just compute this to fall
660+
// into an if-else branch that gives proper error handling information.
661+
const bool one_operand_pointer = lhs_is_pointer || rhs_is_pointer;
662+
615663
if(both_operands_bitvector)
616664
{
617665
return smt_bit_vector_theoryt::subtract(
618666
converted.at(minus.lhs()), converted.at(minus.rhs()));
619667
}
668+
else if(both_operands_pointers)
669+
{
670+
const auto lhs_base_type = to_pointer_type(minus.lhs().type()).base_type();
671+
const auto rhs_base_type = to_pointer_type(minus.rhs().type()).base_type();
672+
INVARIANT(
673+
lhs_base_type == rhs_base_type,
674+
"only pointers of the same object type can be subtracted.");
675+
return smt_bit_vector_theoryt::signed_divide(
676+
smt_bit_vector_theoryt::subtract(
677+
converted.at(minus.lhs()), converted.at(minus.rhs())),
678+
pointer_sizes.at(lhs_base_type));
679+
}
680+
else if(one_operand_pointer)
681+
{
682+
UNIMPLEMENTED_FEATURE(
683+
"convert_expr_to_smt::minus_exprt doesn't handle expressions where"
684+
"only one operand is a pointer - this is because these expressions"
685+
"are normally handled by convert_expr_to_smt::plus_exprt due to"
686+
"transformations of the expressions by previous passes bringing"
687+
"them into a form more suitably handled by that version of the function."
688+
"If you are here, this is a mistake or something went wrong before."
689+
"The expression that caused the problem is: " +
690+
minus.pretty());
691+
}
620692
else
621693
{
622694
UNIMPLEMENTED_FEATURE(
@@ -1299,6 +1371,7 @@ static smt_termt dispatch_expr_to_smt_conversion(
12991371
const exprt &expr,
13001372
const sub_expression_mapt &converted,
13011373
const smt_object_mapt &object_map,
1374+
const type_size_mapt &pointer_sizes,
13021375
const smt_object_sizet::make_applicationt &call_object_size)
13031376
{
13041377
if(const auto symbol = expr_try_dynamic_cast<symbol_exprt>(expr))
@@ -1415,11 +1488,11 @@ static smt_termt dispatch_expr_to_smt_conversion(
14151488
}
14161489
if(const auto plus = expr_try_dynamic_cast<plus_exprt>(expr))
14171490
{
1418-
return convert_expr_to_smt(*plus, converted);
1491+
return convert_expr_to_smt(*plus, converted, pointer_sizes);
14191492
}
14201493
if(const auto minus = expr_try_dynamic_cast<minus_exprt>(expr))
14211494
{
1422-
return convert_expr_to_smt(*minus, converted);
1495+
return convert_expr_to_smt(*minus, converted, pointer_sizes);
14231496
}
14241497
if(const auto divide = expr_try_dynamic_cast<div_exprt>(expr))
14251498
{
@@ -1658,6 +1731,7 @@ at_scope_exitt<functiont> at_scope_exit(functiont exit_function)
16581731
smt_termt convert_expr_to_smt(
16591732
const exprt &expr,
16601733
const smt_object_mapt &object_map,
1734+
const type_size_mapt &pointer_sizes,
16611735
const smt_object_sizet::make_applicationt &object_size)
16621736
{
16631737
#ifndef CPROVER_INVARIANT_DO_NOT_CHECK
@@ -1676,7 +1750,7 @@ smt_termt convert_expr_to_smt(
16761750
if(find_result != sub_expression_map.cend())
16771751
return;
16781752
smt_termt term = dispatch_expr_to_smt_conversion(
1679-
expr, sub_expression_map, object_map, object_size);
1753+
expr, sub_expression_map, object_map, pointer_sizes, object_size);
16801754
sub_expression_map.emplace_hint(find_result, expr, std::move(term));
16811755
});
16821756
return std::move(sub_expression_map.at(expr));

src/solvers/smt2_incremental/convert_expr_to_smt.h

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <solvers/smt2_incremental/smt_object_size.h>
88
#include <solvers/smt2_incremental/smt_sorts.h>
99
#include <solvers/smt2_incremental/smt_terms.h>
10+
#include <solvers/smt2_incremental/type_size_mapping.h>
1011

1112
class exprt;
1213
class typet;
@@ -20,6 +21,7 @@ smt_sortt convert_type_to_smt_sort(const typet &type);
2021
smt_termt convert_expr_to_smt(
2122
const exprt &expression,
2223
const smt_object_mapt &object_map,
24+
const type_size_mapt &pointer_sizes,
2325
const smt_object_sizet::make_applicationt &object_size);
2426

2527
#endif // CPROVER_SOLVERS_SMT2_INCREMENTAL_CONVERT_EXPR_TO_SMT_H

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

+20-9
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,6 @@
22

33
#include "smt2_incremental_decision_procedure.h"
44

5-
#include <solvers/smt2_incremental/construct_value_expr_from_smt.h>
6-
#include <solvers/smt2_incremental/convert_expr_to_smt.h>
7-
#include <solvers/smt2_incremental/smt_commands.h>
8-
#include <solvers/smt2_incremental/smt_core_theory.h>
9-
#include <solvers/smt2_incremental/smt_responses.h>
10-
#include <solvers/smt2_incremental/smt_solver_process.h>
11-
#include <solvers/smt2_incremental/smt_terms.h>
125
#include <util/expr.h>
136
#include <util/namespace.h>
147
#include <util/nodiscard.h>
@@ -17,6 +10,15 @@
1710
#include <util/string_utils.h>
1811
#include <util/symbol.h>
1912

13+
#include <solvers/smt2_incremental/construct_value_expr_from_smt.h>
14+
#include <solvers/smt2_incremental/convert_expr_to_smt.h>
15+
#include <solvers/smt2_incremental/smt_commands.h>
16+
#include <solvers/smt2_incremental/smt_core_theory.h>
17+
#include <solvers/smt2_incremental/smt_responses.h>
18+
#include <solvers/smt2_incremental/smt_solver_process.h>
19+
#include <solvers/smt2_incremental/smt_terms.h>
20+
#include <solvers/smt2_incremental/type_size_mapping.h>
21+
2022
#include <stack>
2123

2224
/// Issues a command to the solving process which is expected to optionally
@@ -163,8 +165,14 @@ smt_termt
163165
smt2_incremental_decision_proceduret::convert_expr_to_smt(const exprt &expr)
164166
{
165167
track_expression_objects(expr, ns, object_map);
168+
associate_pointer_sizes(
169+
expr,
170+
ns,
171+
pointer_sizes_map,
172+
object_map,
173+
object_size_function.make_application);
166174
return ::convert_expr_to_smt(
167-
expr, object_map, object_size_function.make_application);
175+
expr, object_map, pointer_sizes_map, object_size_function.make_application);
168176
}
169177

170178
exprt smt2_incremental_decision_proceduret::handle(const exprt &expr)
@@ -208,7 +216,10 @@ exprt smt2_incremental_decision_proceduret::get(const exprt &expr) const
208216
"Objects in expressions being read should already be tracked from "
209217
"point of being set/handled.");
210218
descriptor = ::convert_expr_to_smt(
211-
expr, object_map, object_size_function.make_application);
219+
expr,
220+
object_map,
221+
pointer_sizes_map,
222+
object_size_function.make_application);
212223
}
213224
else
214225
{

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.h

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <solvers/smt2_incremental/object_tracking.h>
1313
#include <solvers/smt2_incremental/smt_object_size.h>
1414
#include <solvers/smt2_incremental/smt_terms.h>
15+
#include <solvers/smt2_incremental/type_size_mapping.h>
1516
#include <solvers/stack_decision_procedure.h>
1617

1718
#include <memory>
@@ -89,6 +90,7 @@ class smt2_incremental_decision_proceduret final
8990
smt_object_mapt object_map;
9091
std::vector<bool> object_size_defined;
9192
smt_object_sizet object_size_function;
93+
type_size_mapt pointer_sizes_map;
9294
};
9395

9496
#endif // CPROVER_SOLVERS_SMT2_INCREMENTAL_SMT2_INCREMENTAL_DECISION_PROCEDURE_H

0 commit comments

Comments
 (0)