Skip to content

Commit 5a2a09e

Browse files
tautschnigDaniel Kroening
authored and
Daniel Kroening
committed
Permit selectively disabling generated assertions
Add support for `#pragma CPROVER check disable "<name_of_check>"` to disable generating assertions for the statement(s) that follow the pragma.
1 parent 09fa70f commit 5a2a09e

File tree

7 files changed

+188
-4
lines changed

7 files changed

+188
-4
lines changed

doc/cprover-manual/properties.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,24 @@ The goto-instrument program supports these checks:
129129
| `--uninitialized-check` | add checks for uninitialized locals (experimental) |
130130
| `--error-label label` | check that given label is unreachable |
131131
132+
As all of these checks apply across the entire input program, we may wish to
133+
disable them for selected statements in the program. For example, unsigned
134+
overflows can be expected and acceptable in certain instructions even when
135+
elsewhere we do not expect them. To selectively disable automatically generated
136+
properties use `#pragma CPROVER check disable "<name_of_check>"`, which remains
137+
in effect until a `#pragma CPROVER check pop` (to re-enable all properties
138+
disabled before or since the last `#pragma CPROVER check push`) is provided.
139+
For example, for unsigned overflow checks, use
140+
```
141+
unsigned foo(unsigned x)
142+
{
143+
#pragma CPROVER check push
144+
#pragma CPROVER check disable "unsigned-overflow"
145+
x = x + 1; // immediately follows the pragma, no unsigned overflow check here
146+
#pragma CPROVER check pop
147+
x = x + 2; // unsigned overflow checks are generated here
148+
```
149+
132150
#### Generating function bodies
133151
134152
Sometimes implementations for called functions are not available in the goto
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
int main()
2+
{
3+
int x;
4+
int y[1];
5+
6+
#pragma CPROVER check push
7+
#pragma CPROVER check disable "bounds"
8+
#pragma CPROVER check disable "signed-overflow"
9+
// do not generate assertions for the following statement
10+
x = x + y[1];
11+
// pop all annotations
12+
#pragma CPROVER check pop
13+
// but do generate assertions for this one
14+
x = y[1];
15+
return x;
16+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--signed-overflow-check --bounds-check
4+
line 14 array `y' upper bound in y\[\(signed long( long)? int\)1\]: FAILURE$
5+
^\*\* 1 of 1 failed
6+
^VERIFICATION FAILED$
7+
^EXIT=10$
8+
^SIGNAL=0$
9+
--
10+
^warning: ignoring

src/analyses/goto_check.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Author: Daniel Kroening, [email protected]
2929
#include <util/simplify_expr.h>
3030
#include <util/std_expr.h>
3131
#include <util/std_types.h>
32+
#include <util/string_utils.h>
3233

3334
#include <langapi/language.h>
3435
#include <langapi/mode.h>
@@ -1690,6 +1691,32 @@ optionalt<exprt> goto_checkt::rw_ok_check(exprt expr)
16901691
return {};
16911692
}
16921693

1694+
/// Set a Boolean flag to a new value (via `set_flag`) and restore the previous
1695+
/// value when the entire object goes out of scope.
1696+
class flag_resett
1697+
{
1698+
public:
1699+
/// Store the current value of \p flag and then set its value to \p new_value.
1700+
void set_flag(bool &flag, bool new_value)
1701+
{
1702+
if(flag != new_value)
1703+
{
1704+
flags_to_reset.emplace_back(&flag, flag);
1705+
flag = new_value;
1706+
}
1707+
}
1708+
1709+
/// Restore the values of all flags that have been modified via `set_flag`.
1710+
~flag_resett()
1711+
{
1712+
for(const auto &flag_pair : flags_to_reset)
1713+
*flag_pair.first = flag_pair.second;
1714+
}
1715+
1716+
private:
1717+
std::list<std::pair<bool *, bool>> flags_to_reset;
1718+
};
1719+
16931720
void goto_checkt::goto_check(
16941721
const irep_idt &function_identifier,
16951722
goto_functiont &goto_function)
@@ -1711,6 +1738,38 @@ void goto_checkt::goto_check(
17111738
current_target = it;
17121739
goto_programt::instructiont &i=*it;
17131740

1741+
flag_resett flag_resetter;
1742+
if(!i.source_location.get_comment().empty())
1743+
{
1744+
auto disabled_checks = split_string(
1745+
id2string(i.source_location.get_comment()), ',', true, true);
1746+
for(const auto &d : disabled_checks)
1747+
{
1748+
if(d == "disable:bounds-check")
1749+
flag_resetter.set_flag(enable_bounds_check, false);
1750+
else if(d == "disable:pointer-check")
1751+
flag_resetter.set_flag(enable_pointer_check, false);
1752+
else if(d == "disable:memory-leak-check")
1753+
flag_resetter.set_flag(enable_memory_leak_check, false);
1754+
else if(d == "disable:div-by-zero-check")
1755+
flag_resetter.set_flag(enable_div_by_zero_check, false);
1756+
else if(d == "disable:signed-overflow-check")
1757+
flag_resetter.set_flag(enable_signed_overflow_check, false);
1758+
else if(d == "disable:unsigned-overflow-check")
1759+
flag_resetter.set_flag(enable_unsigned_overflow_check, false);
1760+
else if(d == "disable:pointer-overflow-check")
1761+
flag_resetter.set_flag(enable_pointer_overflow_check, false);
1762+
else if(d == "disable:float-overflow-check")
1763+
flag_resetter.set_flag(enable_float_overflow_check, false);
1764+
else if(d == "disable:conversion-check")
1765+
flag_resetter.set_flag(enable_conversion_check, false);
1766+
else if(d == "disable:undefined-shift-check")
1767+
flag_resetter.set_flag(enable_undefined_shift_check, false);
1768+
else if(d == "disable:nan-check")
1769+
flag_resetter.set_flag(enable_nan_check, false);
1770+
}
1771+
}
1772+
17141773
new_code.clear();
17151774

17161775
// we clear all recorded assertions if

src/ansi-c/ansi_c_parser.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class ansi_c_parsert:public parsert
5757
parenthesis_counter=0;
5858
string_literal.clear();
5959
pragma_pack.clear();
60+
pragma_cprover.clear();
6061

6162
// set up global scope
6263
scopes.clear();
@@ -69,6 +70,7 @@ class ansi_c_parsert:public parsert
6970
unsigned parenthesis_counter;
7071
std::string string_literal;
7172
std::list<exprt> pragma_pack;
73+
std::list<irep_idt> pragma_cprover;
7274

7375
typedef configt::ansi_ct::flavourt modet;
7476
modet mode;

src/ansi-c/parser.y

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ extern char *yyansi_ctext;
2424

2525
#include "literals/convert_integer_literal.h"
2626

27+
#include <util/string_utils.h>
28+
29+
#include <sstream>
30+
2731
#include "ansi_c_y.tab.h"
2832

2933
#ifdef _MSC_VER
@@ -2306,10 +2310,38 @@ statement_list:
23062310
statement
23072311
{
23082312
init($$);
2313+
if(!PARSER.pragma_cprover.empty())
2314+
{
2315+
std::ostringstream oss;
2316+
join_strings(
2317+
oss,
2318+
PARSER.pragma_cprover.begin(),
2319+
PARSER.pragma_cprover.end(),
2320+
',');
2321+
parser_stack($1).add_source_location().set_comment(oss.str());
2322+
Forall_operands(it, parser_stack($1))
2323+
{
2324+
it->add_source_location().set_comment(oss.str());
2325+
}
2326+
}
23092327
mto($$, $1);
23102328
}
23112329
| statement_list statement
23122330
{
2331+
if(!PARSER.pragma_cprover.empty())
2332+
{
2333+
std::ostringstream oss;
2334+
join_strings(
2335+
oss,
2336+
PARSER.pragma_cprover.begin(),
2337+
PARSER.pragma_cprover.end(),
2338+
',');
2339+
parser_stack($2).add_source_location().set_comment(oss.str());
2340+
Forall_operands(it, parser_stack($2))
2341+
{
2342+
it->add_source_location().set_comment(oss.str());
2343+
}
2344+
}
23132345
mto($$, $2);
23142346
}
23152347
;

src/ansi-c/scanner.l

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
static int isatty(int) { return 0; }
2828
#endif
2929

30+
#include <util/prefix.h>
3031
#include <util/string_constant.h>
32+
#include <util/suffix.h>
3133
#include <util/unicode.h>
3234

3335
#include "preprocessor_line.h"
@@ -221,7 +223,7 @@ float_s {float}{float_suffix}|{integer}[fF]
221223
gcc_ext_float_s {float}{gcc_ext_float_suffix}
222224
cppstart {ws}"#"{ws}
223225
cpplineno {cppstart}"line"*{ws}{integer}{ws}.*{newline}
224-
cppdirective {cppstart}.*
226+
cppdirective {cppstart}({newline}|[^p].*|"p"[^r].*|"pr"[^a].*|"pra"[^g].*|"prag"[^m].*|"pragm"[^a].*)
225227

226228
escape_sequence [\\][^\n]
227229
c_char [^'\\\n]|{escape_sequence}
@@ -232,6 +234,11 @@ string_lit ("L"|"u"|"U"|"u8")?["]{s_char}*["]
232234

233235
CPROVER_PREFIX "__CPROVER_"
234236

237+
arith_check ("conversion"|"undefined-shift"|"nan"|"div-by-zero")
238+
memory_check ("bounds"|"pointer"|"memory_leak")
239+
overflow_check ("signed"|"unsigned"|"pointer"|"float")"-overflow"
240+
named_check ["]({arith_check}|{memory_check}|{overflow_check})["]
241+
235242
%x GRAMMAR
236243
%x COMMENT1
237244
%x COMMENT2
@@ -251,6 +258,8 @@ CPROVER_PREFIX "__CPROVER_"
251258
%x GCC_ASM
252259
%x GCC_ASM_PAREN
253260
%x CPROVER_ID
261+
%x CPROVER_PRAGMA
262+
%x OTHER_PRAGMA
254263

255264
%{
256265
void ansi_c_scanner_init()
@@ -321,7 +330,7 @@ void ansi_c_scanner_init()
321330
preprocessor_line(yytext, PARSER);
322331
PARSER.set_line_no(PARSER.get_line_no()-1);
323332
}
324-
<STRING_LITERAL>{cppdirective} { /* ignore */ }
333+
<STRING_LITERAL>{cppstart}.* { /* ignore */ }
325334
<STRING_LITERAL>"/*" { yy_push_state(STRING_LITERAL_COMMENT); /* C comment, ignore */ }
326335
<STRING_LITERAL>"//".*\n { /* C++ comment, ignore */ }
327336
<STRING_LITERAL>. { // anything else: back to normal
@@ -388,8 +397,46 @@ void ansi_c_scanner_init()
388397
PARSER.pragma_pack.clear();
389398
}
390399

391-
<GRAMMAR>{cppstart}"pragma"{ws}.* {
392-
// silently ignore other pragmas
400+
<GRAMMAR>{cppstart}"pragma"{ws}"CPROVER" { BEGIN(CPROVER_PRAGMA); }
401+
<CPROVER_PRAGMA>{ws}{newline} { BEGIN(GRAMMAR); }
402+
403+
/* CProver specific pragmas: hint to disable named checks */
404+
<CPROVER_PRAGMA>{ws}"check"{ws}"push" {
405+
PARSER.pragma_cprover.push_back(irep_idt{});
406+
}
407+
<CPROVER_PRAGMA>{ws}"check"{ws}"pop" {
408+
if(!PARSER.pragma_cprover.empty())
409+
PARSER.pragma_cprover.pop_back();
410+
}
411+
<CPROVER_PRAGMA>{ws}"check"{ws}"disable"{ws}{named_check} {
412+
std::string tmp(yytext);
413+
std::string::size_type p = tmp.find('"') + 1;
414+
std::string value =
415+
std::string("disable:") +
416+
std::string(tmp, p, tmp.size() - p - 1) +
417+
std::string("-check");
418+
if(PARSER.pragma_cprover.empty())
419+
PARSER.pragma_cprover.push_back(value);
420+
else
421+
{
422+
if(!PARSER.pragma_cprover.back().empty())
423+
{
424+
value =
425+
id2string(PARSER.pragma_cprover.back()) + "," + value;
426+
}
427+
PARSER.pragma_cprover.back() = value;
428+
}
429+
}
430+
431+
<CPROVER_PRAGMA>. {
432+
yyansi_cerror("Unsupported #pragma CPROVER");
433+
return TOK_SCANNER_ERROR;
434+
}
435+
436+
<GRAMMAR>{cppstart}"pragma" { BEGIN(OTHER_PRAGMA); }
437+
<OTHER_PRAGMA>.*{newline} {
438+
/* silently ignore other pragmas */
439+
BEGIN(GRAMMAR);
393440
}
394441

395442
<GRAMMAR>{cppstart}"ident"{ws}.* { /* ignore */ }

0 commit comments

Comments
 (0)