Skip to content

Commit c3c999a

Browse files
committed
introduce pointer_in_range predicate
This adds the predicate __CPROVER_pointer_in_range to the C frontend. The expression __CPROVER_pointer_in_range(a, b, c) evaluates to true iff the following conditions are met: 1) The three pointers a, b, c point to the same object. 2) The object is readable from a to (but not including) c, i.e., c may point just beyond the end of the sequence. 3) a <= b 4) b <= c This predicate is an invariant for the standard loop pattern in which a pointer is used to iterate over an object, stopping when the pointer points one beyond the end of the sequence. The benefit over using a<=b && b<=c is that ANSI-C's <= operator is undefined when the two pointers related by <= do not point into the same object.
1 parent d1c201b commit c3c999a

File tree

8 files changed

+127
-5
lines changed

8 files changed

+127
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
int x;
2+
3+
int main()
4+
{
5+
__CPROVER_assert(__CPROVER_pointer_in_range(&x, &x, &x), "property 1");
6+
__CPROVER_assert(__CPROVER_pointer_in_range(&x, &x, &x + 1), "property 2");
7+
__CPROVER_assert(
8+
__CPROVER_pointer_in_range(&x, &x + 1, &x + 1), "property 3");
9+
__CPROVER_assert(!__CPROVER_pointer_in_range(&x, &x + 1, &x), "property 4");
10+
__CPROVER_assert(!__CPROVER_pointer_in_range(0, &x, &x), "property 5");
11+
__CPROVER_assert(!__CPROVER_pointer_in_range(&x, 0, &x), "property 6");
12+
__CPROVER_assert(!__CPROVER_pointer_in_range(&x, &x, 0), "property 7");
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
in_range1.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
^warning: ignoring
8+
--

src/ansi-c/c_typecheck_expr.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -2522,6 +2522,24 @@ exprt c_typecheck_baset::do_special_functions(
25222522

25232523
return live_object_expr;
25242524
}
2525+
else if(identifier == CPROVER_PREFIX "pointer_in_range")
2526+
{
2527+
// experimental feature for CHC encodings -- do not use
2528+
if(expr.arguments().size() != 3)
2529+
{
2530+
error().source_location = f_op.source_location();
2531+
error() << "pointer_in_range expects three arguments" << eom;
2532+
throw 0;
2533+
}
2534+
2535+
typecheck_function_call_arguments(expr);
2536+
2537+
exprt pointer_in_range_expr = pointer_in_range_exprt(
2538+
expr.arguments()[0], expr.arguments()[1], expr.arguments()[2]);
2539+
pointer_in_range_expr.add_source_location() = source_location;
2540+
2541+
return pointer_in_range_expr;
2542+
}
25252543
else if(identifier == CPROVER_PREFIX "WRITEABLE_OBJECT")
25262544
{
25272545
if(expr.arguments().size() != 1)

src/ansi-c/cprover_builtin_headers.h

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ __CPROVER_size_t __CPROVER_POINTER_OBJECT(const void *);
5757
__CPROVER_ssize_t __CPROVER_POINTER_OFFSET(const void *);
5858
__CPROVER_size_t __CPROVER_OBJECT_SIZE(const void *);
5959
__CPROVER_bool __CPROVER_DYNAMIC_OBJECT(const void *);
60+
__CPROVER_bool __CPROVER_pointer_in_range(const void *, const void *, const void *);
6061
void __CPROVER_allocated_memory(__CPROVER_size_t address, __CPROVER_size_t extent);
6162

6263
// float stuff

src/ansi-c/goto_check_c.cpp

+21-5
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ class goto_check_ct
224224
void conversion_check(const exprt &, const guardt &);
225225
void float_overflow_check(const exprt &, const guardt &);
226226
void nan_check(const exprt &, const guardt &);
227-
optionalt<exprt> rw_ok_check(exprt);
227+
optionalt<exprt> expand_pointer_checks(exprt);
228228

229229
std::string array_name(const exprt &);
230230

@@ -1992,14 +1992,14 @@ void goto_check_ct::check(const exprt &expr)
19921992
check_rec(expr, identity);
19931993
}
19941994

1995-
/// expand the r_ok, w_ok and rw_ok predicates
1996-
optionalt<exprt> goto_check_ct::rw_ok_check(exprt expr)
1995+
/// expand the r_ok, w_ok, rw_ok, pointer_in_range predicates
1996+
optionalt<exprt> goto_check_ct::expand_pointer_checks(exprt expr)
19971997
{
19981998
bool modified = false;
19991999

20002000
for(auto &op : expr.operands())
20012001
{
2002-
auto op_result = rw_ok_check(op);
2002+
auto op_result = expand_pointer_checks(op);
20032003
if(op_result.has_value())
20042004
{
20052005
op = *op_result;
@@ -2026,6 +2026,22 @@ optionalt<exprt> goto_check_ct::rw_ok_check(exprt expr)
20262026
simplify(c, ns);
20272027
return c;
20282028
}
2029+
else if(expr.id() == ID_pointer_in_range)
2030+
{
2031+
const auto &pointer_in_range_expr = to_pointer_in_range_expr(expr);
2032+
2033+
auto expanded = pointer_in_range_expr.lower();
2034+
2035+
// rec. call
2036+
auto expanded_rec_opt = expand_pointer_checks(expanded);
2037+
if(expanded_rec_opt.has_value())
2038+
expanded = *expanded_rec_opt;
2039+
2040+
if(enable_simplify)
2041+
simplify(expanded, ns);
2042+
2043+
return expanded;
2044+
}
20292045
else if(modified)
20302046
return std::move(expr);
20312047
else
@@ -2228,7 +2244,7 @@ void goto_check_ct::goto_check(
22282244
}
22292245
}
22302246

2231-
i.transform([this](exprt expr) { return rw_ok_check(expr); });
2247+
i.transform([this](exprt expr) { return expand_pointer_checks(expr); });
22322248

22332249
for(auto &instruction : new_code.instructions)
22342250
{

src/util/irep_ids.def

+1
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@ IREP_ID_ONE(object_descriptor)
456456
IREP_ID_ONE(is_dynamic_object)
457457
IREP_ID_ONE(dynamic_object)
458458
IREP_ID_TWO(C_dynamic, #dynamic)
459+
IREP_ID_ONE(pointer_in_range)
459460
IREP_ID_ONE(object_size)
460461
IREP_ID_ONE(separate)
461462
IREP_ID_ONE(good_pointer)

src/util/pointer_expr.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Author: Daniel Kroening, [email protected]
1313
#include "c_types.h"
1414
#include "expr_util.h"
1515
#include "pointer_offset_size.h"
16+
#include "pointer_predicates.h"
1617
#include "simplify_expr.h"
1718

1819
void dynamic_object_exprt::set_instance(unsigned int instance)
@@ -214,3 +215,15 @@ void dereference_exprt::validate(
214215
"dereference expression's type must match the base type of the type of its "
215216
"operand");
216217
}
218+
219+
exprt pointer_in_range_exprt::lower() const
220+
{
221+
return and_exprt(
222+
{same_object(op0(), op1()),
223+
same_object(op1(), op2()),
224+
r_ok_exprt(
225+
op0(), minus_exprt(pointer_offset(op2()), pointer_offset(op0()))),
226+
binary_relation_exprt(pointer_offset(op0()), ID_le, pointer_offset(op1())),
227+
binary_relation_exprt(
228+
pointer_offset(op1()), ID_le, pointer_offset(op2()))});
229+
}

src/util/pointer_expr.h

+52
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,58 @@ inline is_dynamic_object_exprt &to_is_dynamic_object_expr(exprt &expr)
366366
return static_cast<is_dynamic_object_exprt &>(expr);
367367
}
368368

369+
/// pointer_in_range(a, b, c) evaluates to true iff
370+
/// same_object(a, b, c) ∧ r_ok(a, offset(c)-offset(a)) ∧ a<=b ∧ b<=c
371+
/// Note that the last inequality is weak, i.e., b may be equal to c.
372+
class pointer_in_range_exprt : public ternary_exprt
373+
{
374+
public:
375+
explicit pointer_in_range_exprt(exprt a, exprt b, exprt c)
376+
: ternary_exprt(
377+
ID_pointer_in_range,
378+
std::move(a),
379+
std::move(b),
380+
std::move(c),
381+
bool_typet())
382+
{
383+
PRECONDITION(op0().type().id() == ID_pointer);
384+
PRECONDITION(op1().type().id() == ID_pointer);
385+
PRECONDITION(op2().type().id() == ID_pointer);
386+
}
387+
388+
// translate into equivalent conjunction
389+
exprt lower() const;
390+
};
391+
392+
template <>
393+
inline bool can_cast_expr<pointer_in_range_exprt>(const exprt &base)
394+
{
395+
return base.id() == ID_pointer_in_range;
396+
}
397+
398+
inline void validate_expr(const pointer_in_range_exprt &value)
399+
{
400+
validate_operands(value, 3, "pointer_in_range must have three operands");
401+
}
402+
403+
inline const pointer_in_range_exprt &to_pointer_in_range_expr(const exprt &expr)
404+
{
405+
PRECONDITION(expr.id() == ID_pointer_in_range);
406+
DATA_INVARIANT(
407+
expr.operands().size() == 3, "pointer_in_range must have three operands");
408+
return static_cast<const pointer_in_range_exprt &>(expr);
409+
}
410+
411+
/// \copydoc to_pointer_in_range_expr(const exprt &)
412+
/// \ingroup gr_std_expr
413+
inline pointer_in_range_exprt &to_pointer_in_range_expr(exprt &expr)
414+
{
415+
PRECONDITION(expr.id() == ID_pointer_in_range);
416+
DATA_INVARIANT(
417+
expr.operands().size() == 3, "pointer_in_range must have one operand");
418+
return static_cast<pointer_in_range_exprt &>(expr);
419+
}
420+
369421
/// \brief Operator to return the address of an object
370422
class address_of_exprt : public unary_exprt
371423
{

0 commit comments

Comments
 (0)