Skip to content

Commit 0008223

Browse files
author
Remi Delmas
committed
Adding the possibility to enable checks per instruction using #pragma CPROVER enable "named-check"
Handling check enable/disable shadowing in the CPROVER pragma stack Detect and error out on simultaneous enable and disable of a check at a same pragma stack level in the ansi-c parser and goto_check.cpp
1 parent e27dba6 commit 0008223

File tree

25 files changed

+678
-78
lines changed

25 files changed

+678
-78
lines changed

doc/cprover-manual/properties.md

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,22 +131,77 @@ The goto-instrument program supports these checks:
131131
| `--error-label label` | check that given label is unreachable |
132132
133133
As all of these checks apply across the entire input program, we may wish to
134-
disable them for selected statements in the program. For example, unsigned
135-
overflows can be expected and acceptable in certain instructions even when
136-
elsewhere we do not expect them. As of version 5.12, CBMC supports selectively
137-
disabling automatically generated properties. To disable property generation,
138-
use `#pragma CPROVER check disable "<name_of_check>"`, which remains in effect
139-
until a `#pragma CPROVER check pop` (to re-enable all properties
140-
disabled before or since the last `#pragma CPROVER check push`) is provided.
134+
disable or enable them for selected statements in the program.
135+
For example, unsigned overflows can be expected and acceptable in certain
136+
instructions even when elsewhere we do not expect them.
137+
As of version 5.12, CBMC supports selectively disabling or enabling
138+
automatically generated properties using pragmas.
139+
140+
141+
CPROVER pragmas are handled using a stack:
142+
- `#pragma CPROVER check push` pushes a new level on the pragma stack
143+
- `#pragma CPROVER check disable "<name_of_check>"` adds a disable pragma
144+
at the top of the stack
145+
- `#pragma CPROVER check enable "<name_of_check>"` adds a enable pragma
146+
at the top of the stack
147+
- an `enable` or `disable` pragma for a given check present at the top level
148+
of the stack shadows other pragmas for the same in lower levels of the stack
149+
- adding both `enable` and `disable` pragmas for a same check in a same level
150+
of the stack creates a PARSING_ERROR.
151+
- `#pragma CPROVER check pop` pops a level in the stack and restores the state
152+
of pragmas at the sub level
153+
141154
For example, for unsigned overflow checks, use
155+
142156
```
143157
unsigned foo(unsigned x)
144158
{
145159
#pragma CPROVER check push
160+
#pragma CPROVER check enable "unsigned-overflow"
161+
// unsigned overflow check apply here
162+
x = x + 1;
163+
#pragma CPROVER check pop
164+
// unsigned overflow checks do not apply here
165+
x = x + 2;
166+
```
167+
168+
```
169+
unsigned foo(unsigned x)
170+
{
171+
#pragma CPROVER check push
172+
#pragma CPROVER check enable "unsigned-overflow"
173+
#pragma CPROVER check enable "signed-overflow"
174+
// unsigned and signed overflow check apply here
175+
x = x + 1;
176+
#pragma CPROVER check push
146177
#pragma CPROVER check disable "unsigned-overflow"
147-
x = x + 1; // immediately follows the pragma, no unsigned overflow check here
178+
// only signed overflow check apply here
179+
x = x + 2;
180+
#pragma CPROVER check pop
181+
// unsigned and signed overflow check apply here
182+
x = x + 3;
183+
#pragma CPROVER check pop
184+
// unsigned overflow checks do not apply here
185+
x = x + 2;
186+
```
187+
188+
```
189+
unsigned foo(unsigned x)
190+
{
191+
#pragma CPROVER check push
192+
#pragma CPROVER check enable "unsigned-overflow"
193+
#pragma CPROVER check enable "signed-overflow"
194+
// unsigned and signed overflow check apply here
195+
x = x + 1;
196+
#pragma CPROVER check push
197+
#pragma CPROVER check disable "unsigned-overflow"
198+
#pragma CPROVER check enable "unsigned-overflow"
199+
// PARSING_ERROR Found enable and disable pragmas for unsigned-overflow-check
200+
x = x + 2;
201+
#pragma CPROVER check pop
202+
x = x + 3;
148203
#pragma CPROVER check pop
149-
x = x + 2; // unsigned overflow checks are generated here
204+
x = x + 2;
150205
```
151206
152207
#### Flag --nan-check limitations

jbmc/src/janalyzer/janalyzer_parse_options.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,11 @@ void janalyzer_parse_optionst::process_goto_function(
705705

706706
// add generic checks
707707
goto_check(
708-
function.get_function_id(), function.get_goto_function(), ns, options);
708+
function.get_function_id(),
709+
function.get_goto_function(),
710+
ns,
711+
options,
712+
ui_message_handler);
709713
}
710714

711715
bool janalyzer_parse_optionst::can_generate_function_body(const irep_idt &name)

jbmc/src/jbmc/jbmc_parse_options.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,11 @@ void jbmc_parse_optionst::process_goto_function(
812812

813813
// add generic checks
814814
goto_check(
815-
function.get_function_id(), function.get_goto_function(), ns, options);
815+
function.get_function_id(),
816+
function.get_goto_function(),
817+
ns,
818+
options,
819+
ui_message_handler);
816820

817821
// Replace Java new side effects
818822
remove_java_new(

jbmc/src/jdiff/jdiff_parse_options.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ bool jdiff_parse_optionst::process_goto_program(
278278

279279
// add generic checks
280280
log.status() << "Generic Property Instrumentation" << messaget::eom;
281-
goto_check(options, goto_model);
281+
goto_check(options, goto_model, ui_message_handler);
282282

283283
// checks don't know about adjusted float expressions
284284
adjust_float_expressions(goto_model);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
int main()
2+
{
3+
int x;
4+
int y[1];
5+
6+
#pragma CPROVER check push
7+
#pragma CPROVER check enable "bounds"
8+
#pragma CPROVER check enable "signed-overflow"
9+
// generate assertions for the following statement
10+
x = x + y[1];
11+
#pragma CPROVER check pop
12+
// but do not generate assertions for this one
13+
x = y[1];
14+
return x;
15+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
4+
^\[main\.array_bounds\.1\] line \d+ array 'y' upper bound.*FAILURE$
5+
^\[main\.overflow\.1\] line \d+ arithmetic overflow on signed.*FAILURE$
6+
^\*\* 2 of 2 failed
7+
^VERIFICATION FAILED$
8+
^EXIT=10$
9+
^SIGNAL=0$
10+
--
11+
--
12+
Checks that we can selectively activate checks using pragmas.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
int foo(int x)
2+
{
3+
return x;
4+
}
5+
6+
int main()
7+
{
8+
int m, n;
9+
10+
#pragma CPROVER check push
11+
#pragma CPROVER check enable "signed-overflow"
12+
// generate assertions for the following statements
13+
int x = m = n + n;
14+
++n;
15+
n++;
16+
n += 1;
17+
foo(x + n);
18+
#pragma CPROVER check pop
19+
// but do not generate assertions for these
20+
x = n + n;
21+
foo(x + n);
22+
return x;
23+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
4+
^\[main\.overflow\.1\] line 13 arithmetic overflow on signed \+ in n \+ n: FAILURE$
5+
^\[main\.overflow\.2\] line 14 arithmetic overflow on signed \+ in n \+ 1: FAILURE$
6+
^\[main\.overflow\.3\] line 15 arithmetic overflow on signed \+ in n \+ 1: FAILURE$
7+
^\[main\.overflow\.4\] line 16 arithmetic overflow on signed \+ in n \+ 1: FAILURE$
8+
^\[main\.overflow\.5\] line 17 arithmetic overflow on signed \+ in x \+ n: FAILURE$
9+
^\*\* 5 of 5 failed
10+
^VERIFICATION FAILED$
11+
^EXIT=10$
12+
^SIGNAL=0$
13+
--
14+
^warning: ignoring
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <stdlib.h>
2+
3+
int main()
4+
{
5+
char *p = malloc(sizeof(*p));
6+
char *q;
7+
8+
#pragma CPROVER check push
9+
#pragma CPROVER check enable "pointer-primitive"
10+
// generate checks for the following statements and fail
11+
if(__CPROVER_same_object(p, q))
12+
{
13+
}
14+
#pragma CPROVER check pop
15+
16+
// but do not generate checks on the following statements
17+
if(__CPROVER_same_object(p, q))
18+
{
19+
}
20+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
CORE
2+
main.c
3+
4+
^main.c function main$
5+
^\[main.pointer_primitives.\d+\] line 11 pointer invalid in POINTER_OBJECT\(\(const void \*\)p\): SUCCESS$
6+
^\[main.pointer_primitives.\d+\] line 11 deallocated dynamic object in POINTER_OBJECT\(\(const void \*\)p\): SUCCESS$
7+
^\[main.pointer_primitives.\d+\] line 11 dead object in POINTER_OBJECT\(\(const void \*\)p\): SUCCESS$
8+
^\[main.pointer_primitives.\d+\] line 11 pointer outside object bounds in POINTER_OBJECT\(\(const void \*\)p\): SUCCESS$
9+
^\[main.pointer_primitives.\d+\] line 11 pointer invalid in POINTER_OBJECT\(\(const void \*\)q\): FAILURE$
10+
^\[main.pointer_primitives.\d+\] line 11 deallocated dynamic object in POINTER_OBJECT\(\(const void \*\)q\): SUCCESS$
11+
^\[main.pointer_primitives.\d+\] line 11 dead object in POINTER_OBJECT\(\(const void \*\)q\): SUCCESS$
12+
^\[main.pointer_primitives.\d+\] line 11 pointer outside object bounds in POINTER_OBJECT\(\(const void \*\)q\): FAILURE$
13+
^VERIFICATION FAILED$
14+
^EXIT=10$
15+
^SIGNAL=0$
16+
--
17+
^\[main.pointer_primitives.\d+\] line 17
18+
--
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#include <stdbool.h>
2+
#include <stdlib.h>
3+
4+
bool nondet_bool();
5+
6+
typedef enum ABC
7+
{
8+
A = 0,
9+
B = 1,
10+
C = 2
11+
} ABC;
12+
13+
int main()
14+
{
15+
#pragma CPROVER check push
16+
#pragma CPROVER check disable "bounds"
17+
#pragma CPROVER check disable "pointer"
18+
#pragma CPROVER check disable "div-by-zero"
19+
#pragma CPROVER check disable "enum-range"
20+
#pragma CPROVER check disable "signed-overflow"
21+
#pragma CPROVER check disable "unsigned-overflow"
22+
#pragma CPROVER check disable "pointer-overflow"
23+
#pragma CPROVER check disable "float-overflow"
24+
#pragma CPROVER check disable "conversion"
25+
#pragma CPROVER check disable "undefined-shift"
26+
#pragma CPROVER check disable "nan"
27+
#pragma CPROVER check disable "pointer-primitive"
28+
{
29+
int N = 10;
30+
char *p = malloc(N * sizeof(*p));
31+
char *q;
32+
char *r;
33+
float den;
34+
float x;
35+
float y;
36+
ABC e;
37+
bool same;
38+
char i;
39+
signed int j;
40+
same = __CPROVER_same_object(p, q);
41+
q = p + 2000000000000;
42+
q = r;
43+
if(nondet_bool())
44+
den = 0.0;
45+
else
46+
den = 1.0;
47+
y = x / den;
48+
e = 10;
49+
i += 1;
50+
j += 1;
51+
}
52+
#pragma CPROVER check push
53+
#pragma CPROVER check enable "bounds"
54+
#pragma CPROVER check enable "pointer"
55+
#pragma CPROVER check enable "div-by-zero"
56+
#pragma CPROVER check enable "enum-range"
57+
#pragma CPROVER check enable "signed-overflow"
58+
#pragma CPROVER check enable "unsigned-overflow"
59+
#pragma CPROVER check enable "pointer-overflow"
60+
#pragma CPROVER check enable "float-overflow"
61+
#pragma CPROVER check enable "conversion"
62+
#pragma CPROVER check enable "undefined-shift"
63+
#pragma CPROVER check enable "nan"
64+
#pragma CPROVER check enable "pointer-primitive"
65+
{
66+
int N = 10;
67+
char *p = malloc(N * sizeof(*p));
68+
char *q;
69+
char *r;
70+
float den;
71+
float x;
72+
float y;
73+
ABC e;
74+
bool same;
75+
char i;
76+
signed int j;
77+
same = __CPROVER_same_object(p, q);
78+
q = p + 2000000000000;
79+
q = r;
80+
if(nondet_bool())
81+
den = 0.0;
82+
else
83+
den = 1.0;
84+
y = x / den;
85+
e = 10;
86+
i += 1;
87+
j += 1;
88+
}
89+
#pragma CPROVER check pop
90+
#pragma CPROVER check pop
91+
return 0;
92+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
CORE broken-smt-backend
2+
main.c
3+
--object-bits 8 --bounds-check --pointer-check --pointer-primitive-check --div-by-zero-check --enum-range-check --unsigned-overflow-check --signed-overflow-check --pointer-overflow-check --float-overflow-check --conversion-check --undefined-shift-check --nan-check --pointer-primitive-check
4+
^\[main\.pointer_primitives\.\d+\] line 77 pointer invalid in POINTER_OBJECT\(\(const void \*\)q\): FAILURE
5+
^\[main\.pointer_primitives\.\d+\] line 77 pointer outside object bounds in POINTER_OBJECT\(\(const void \*\)q\): FAILURE
6+
^\[main\.pointer_arithmetic\.\d+\] line 78 pointer arithmetic: pointer outside object bounds in p \+ 2000000000000l: FAILURE
7+
^\[main\.NaN\.\d+\] line 84 NaN on / in x / den: FAILURE
8+
^\[main\.division-by-zero\.\d+\] line 84 division by zero in x / den: FAILURE
9+
^\[main\.overflow\.\d+\] line 84 arithmetic overflow on floating-point division in x / den: FAILURE
10+
^\[main\.enum-range-check\.\d+\] line 85 enum range check in \(ABC\)10: FAILURE
11+
^\[main\.overflow\.\d+\] line 86 arithmetic overflow on signed type conversion in \(char\)\(\(signed int\)i \+ 1\): FAILURE
12+
^\[main\.overflow\.\d+\] line 87 arithmetic overflow on signed \+ in j \+ 1: FAILURE
13+
^VERIFICATION FAILED$
14+
^EXIT=10$
15+
^SIGNAL=0$
16+
--
17+
^\[main\.pointer_primitives\.\d+\] line 40 pointer invalid in POINTER_OBJECT\(const void \*\)q\): FAILURE
18+
^\[main\.pointer_primitives\.\d+\] line 40 pointer outside object bounds in POINTER_OBJECT\(const void \*\)q\): FAILURE
19+
^\[main\.pointer_arithmetic\.\d+\] line 41 pointer arithmetic: pointer outside object bounds in p \+ 2000000000000l: FAILURE
20+
^\[main\.NaN\.\d+\] line 47 NaN on / in x / den: FAILURE
21+
^\[main\.division-by-zero\.\d+\] line 47 division by zero in x / den: FAILURE
22+
^\[main\.overflow\.\d+\] line 47 arithmetic overflow on floating-point division in x / den: FAILURE
23+
^\[main\.enum-range-check\.\d+\] line 48 enum range check in \(ABC\)10: FAILURE
24+
^\[main\.overflow\.\d+\] line 49 arithmetic overflow on signed type conversion in \(char\)\(signed int\)i \+ 1\): FAILURE
25+
^\[main\.overflow\.\d+\] line 50 arithmetic overflow on signed \+ in j \+ 1: FAILURE
26+
--
27+
This test uses all possible named-checks to maximize coverage.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include <stdlib.h>
2+
3+
int main()
4+
{
5+
char *p = malloc(sizeof(*p));
6+
char *q;
7+
8+
#pragma CPROVER check push
9+
#pragma CPROVER check enable "pointer-primitive"
10+
// must generate checks
11+
if(__CPROVER_same_object(p, q))
12+
{
13+
}
14+
#pragma CPROVER check push
15+
#pragma CPROVER check disable "pointer-primitive"
16+
// must not generate checks
17+
if(__CPROVER_same_object(p, q))
18+
{
19+
}
20+
#pragma CPROVER check push
21+
#pragma CPROVER check enable "pointer-primitive"
22+
// must generate checks
23+
if(__CPROVER_same_object(p, q))
24+
{
25+
}
26+
#pragma CPROVER check pop
27+
// must not generate generate checks
28+
if(__CPROVER_same_object(p, q))
29+
{
30+
}
31+
#pragma CPROVER check pop
32+
// must generate generate checks
33+
if(__CPROVER_same_object(p, q))
34+
{
35+
}
36+
#pragma CPROVER check pop
37+
// must not generate generate checks
38+
if(__CPROVER_same_object(p, q))
39+
{
40+
}
41+
return 0;
42+
}

0 commit comments

Comments
 (0)