Skip to content

Commit f060b9c

Browse files
committed
__CPROVER_r/w_ok does not require null or a valid pointer
The definition of __CPROVER_r/w_ok (in goto_check.cpp) does include the case of an invalid pointer, and thus, there is no need to check that the pointer given to these predicates is valid.
1 parent 3e8b6df commit f060b9c

File tree

8 files changed

+35
-66
lines changed

8 files changed

+35
-66
lines changed

doc/architectural/memory-primitives.md

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -177,19 +177,25 @@ given number of bytes:
177177
- `_Bool __CPROVER_w_ok(const void *p, size_t size)`
178178
- `_Bool __CPROVER_rw_ok(const void *p, size_t size)`
179179
180-
At present, both primitives are equivalent as all memory in CBMC is considered
181-
both readable and writeable. If `p` is the null pointer, the primitives return
182-
false. If `p` is valid, the primitives return true if the memory segment
183-
starting at the pointer has at least the given size, and false otherwise. If `p`
184-
is neither null nor valid, the semantics is unspecified. It is valid to apply
185-
the primitive to pointers that point to within a memory object. For example:
180+
At present, all three primitives are equivalent as all memory in CBMC is
181+
considered both readable and writeable. If `p` points to a valid object the
182+
primitives return true if the memory segment starting at the pointer has at
183+
least the given size, and false otherwise. Specificially, when `size` is
184+
zero, and `p` points to the byte one beyond the end of the object, the
185+
predicate returns true.
186+
187+
If `p` is either null or does not point to a valid object, the predicate
188+
returns false.
189+
190+
It is valid to apply the primitive to pointers that have a nonzero offset.
191+
For example:
186192
187193
```C
188194
char *p = malloc(10);
189-
assert(__CPROVER_r_ok(p, 10)); // valid
190-
p += 5;
191-
assert(__CPROVER_r_ok(p, 3)); // valid
192-
assert(__CPROVER_r_ok(p, 10)); // fails
195+
assert(__CPROVER_r_ok(p, 10)); // passes
196+
assert(__CPROVER_r_ok(p + 5, 3)); // passes
197+
assert(__CPROVER_r_ok(p + 5, 10)); // fails
198+
assert(__CPROVER_r_ok(p + 10, 0)); // passes
193199
```
194200

195201
# Detecting potential misuses of memory primitives
@@ -205,8 +211,6 @@ primitives are either null or valid:
205211
- `__CPROVER_same_object`
206212
- `__CPROVER_OBJECT_SIZE`
207213
- `__CPROVER_DYNAMIC_OBJECT`
208-
- `__CPROVER_r_ok`
209-
- `__CPROVER_w_ok`
210214

211215
While the first three primitives have well-defined semantics even on invalid
212216
pointers, using them on invalid pointers is usually unintended in user programs.

regression/cbmc/pointer-primitive-check-01/main.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,5 @@ void main()
1818
__CPROVER_OBJECT_SIZE(p4);
1919

2020
char *p5;
21-
__CPROVER_r_ok(p5, 1);
22-
23-
char *p6;
24-
__CPROVER_w_ok(p6, 1);
25-
26-
char *p7;
27-
__CPROVER_DYNAMIC_OBJECT(p7);
21+
__CPROVER_DYNAMIC_OBJECT(p5);
2822
}

regression/cbmc/pointer-primitive-check-01/test.desc

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,10 @@ main.c
1919
\[main.pointer_primitives.14\] line \d+ deallocated dynamic object in OBJECT_SIZE\(\(const void \*\)p4\): SUCCESS
2020
\[main.pointer_primitives.15\] line \d+ dead object in OBJECT_SIZE\(\(const void \*\)p4\): SUCCESS
2121
\[main.pointer_primitives.16\] line \d+ pointer outside object bounds in OBJECT_SIZE\(\(const void \*\)p4\): FAILURE
22-
\[main.pointer_primitives.17\] line \d+ pointer invalid in R_OK\(p5, .*1\): FAILURE
23-
\[main.pointer_primitives.18\] line \d+ deallocated dynamic object in R_OK\(p5, .*1\): SUCCESS
24-
\[main.pointer_primitives.19\] line \d+ dead object in R_OK\(p5, .*1\): SUCCESS
25-
\[main.pointer_primitives.20\] line \d+ pointer outside object bounds in R_OK\(p5, .*1\): FAILURE
26-
\[main.pointer_primitives.21\] line \d+ pointer invalid in W_OK\(p6, \(.*\)1\): FAILURE
27-
\[main.pointer_primitives.22\] line \d+ deallocated dynamic object in W_OK\(p6, .*1\): SUCCESS
28-
\[main.pointer_primitives.23\] line \d+ dead object in W_OK\(p6, .*1\): SUCCESS
29-
\[main.pointer_primitives.24\] line \d+ pointer outside object bounds in W_OK\(p6, .*1\): FAILURE
30-
\[main.pointer_primitives.25\] line \d+ pointer invalid in IS_DYNAMIC_OBJECT\(\(const void \*\)p7\): FAILURE
31-
\[main.pointer_primitives.26\] line \d+ deallocated dynamic object in IS_DYNAMIC_OBJECT\(\(const void \*\)p7\): SUCCESS
32-
\[main.pointer_primitives.27\] line \d+ dead object in IS_DYNAMIC_OBJECT\(\(const void \*\)p7\): SUCCESS
33-
\[main.pointer_primitives.28\] line \d+ pointer outside object bounds in IS_DYNAMIC_OBJECT\(\(const void \*\)p7\): FAILURE
22+
\[main.pointer_primitives.17\] line \d+ pointer invalid in IS_DYNAMIC_OBJECT\(\(const void \*\)p5\): FAILURE
23+
\[main.pointer_primitives.18\] line \d+ deallocated dynamic object in IS_DYNAMIC_OBJECT\(\(const void \*\)p5\): SUCCESS
24+
\[main.pointer_primitives.19\] line \d+ dead object in IS_DYNAMIC_OBJECT\(\(const void \*\)p5\): SUCCESS
25+
\[main.pointer_primitives.20\] line \d+ pointer outside object bounds in IS_DYNAMIC_OBJECT\(\(const void \*\)p5\): FAILURE
3426
--
3527
^warning: ignoring
3628
--

regression/cbmc/pointer-primitive-check-04/main.c

Lines changed: 0 additions & 6 deletions
This file was deleted.

regression/cbmc/pointer-primitive-check-04/test.desc

Lines changed: 0 additions & 14 deletions
This file was deleted.

regression/cbmc/r_w_ok1/main.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,24 @@ int main()
77

88
assert(!__CPROVER_r_ok(p, sizeof(int)));
99
assert(!__CPROVER_w_ok(p, sizeof(int)));
10+
assert(!__CPROVER_r_ok(p, 0));
11+
assert(!__CPROVER_w_ok(p, 0));
1012

1113
p = malloc(sizeof(int));
1214

1315
assert(__CPROVER_r_ok(p, 1));
1416
assert(__CPROVER_w_ok(p, 1));
1517
assert(__CPROVER_r_ok(p, sizeof(int)));
1618
assert(__CPROVER_w_ok(p, sizeof(int)));
19+
assert(__CPROVER_r_ok(p + 1, 0));
20+
assert(__CPROVER_w_ok(p + 1, 0));
1721

1822
size_t n;
1923
char *arbitrary_size = malloc(n);
2024

2125
assert(__CPROVER_r_ok(arbitrary_size, n));
2226
assert(__CPROVER_w_ok(arbitrary_size, n));
2327

24-
assert(__CPROVER_r_ok(arbitrary_size, n + 1));
25-
assert(__CPROVER_w_ok(arbitrary_size, n + 1));
28+
assert(__CPROVER_r_ok(arbitrary_size, n + 1)); // expected to fail
29+
assert(__CPROVER_w_ok(arbitrary_size, n + 1)); // expected to fail
2630
}

regression/cbmc/r_w_ok1/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ CORE
22
main.c
33

44
__CPROVER_[rw]_ok\(arbitrary_size, n \+ 1\): FAILURE$
5-
^\*\* 2 of 12 failed
5+
^\*\* 2 of 16 failed
66
^VERIFICATION FAILED$
77
^EXIT=10$
88
^SIGNAL=0$

src/analyses/goto_check.cpp

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,12 @@ class goto_checkt
201201
/// \param guard: the condition under which the operation happens
202202
void pointer_primitive_check(const exprt &expr, const guardt &guard);
203203

204-
/// Returns true if the given expression is a pointer primitive such as
205-
/// __CPROVER_r_ok()
204+
/// Returns true if the given expression is a pointer primitive
205+
/// that requires a pointer primitive check
206206
///
207207
/// \param expr expression
208208
/// \return true if the given expression is a pointer primitive
209-
bool is_pointer_primitive(const exprt &expr);
209+
bool requires_pointer_primitive_check(const exprt &);
210210

211211
optionalt<goto_checkt::conditiont>
212212
get_pointer_is_null_condition(const exprt &address, const exprt &size);
@@ -1282,10 +1282,7 @@ void goto_checkt::pointer_primitive_check(
12821282
if(expr.source_location().is_built_in())
12831283
return;
12841284

1285-
const exprt pointer =
1286-
(expr.id() == ID_r_ok || expr.id() == ID_w_ok || expr.id() == ID_rw_ok)
1287-
? to_r_or_w_ok_expr(expr).pointer()
1288-
: to_unary_expr(expr).op();
1285+
const exprt pointer = to_unary_expr(expr).op();
12891286

12901287
CHECK_RETURN(pointer.type().id() == ID_pointer);
12911288

@@ -1317,15 +1314,13 @@ void goto_checkt::pointer_primitive_check(
13171314
}
13181315
}
13191316

1320-
bool goto_checkt::is_pointer_primitive(const exprt &expr)
1317+
bool goto_checkt::requires_pointer_primitive_check(const exprt &expr)
13211318
{
13221319
// we don't need to include the __CPROVER_same_object primitive here as it
13231320
// is replaced by lower level primitives in the special function handling
13241321
// during typechecking (see c_typecheck_expr.cpp)
13251322
return expr.id() == ID_pointer_object || expr.id() == ID_pointer_offset ||
1326-
expr.id() == ID_object_size || expr.id() == ID_r_ok ||
1327-
expr.id() == ID_w_ok || expr.id() == ID_rw_ok ||
1328-
expr.id() == ID_is_dynamic_object;
1323+
expr.id() == ID_object_size || expr.id() == ID_is_dynamic_object;
13291324
}
13301325

13311326
goto_checkt::conditionst goto_checkt::get_pointer_dereferenceable_conditions(
@@ -1795,7 +1790,7 @@ void goto_checkt::check_rec(const exprt &expr, guardt &guard)
17951790
{
17961791
pointer_validity_check(to_dereference_expr(expr), expr, guard);
17971792
}
1798-
else if(is_pointer_primitive(expr))
1793+
else if(requires_pointer_primitive_check(expr))
17991794
{
18001795
pointer_primitive_check(expr, guard);
18011796
}

0 commit comments

Comments
 (0)