Skip to content

Commit 8ab933b

Browse files
authored
Merge pull request #6471 from remi-delmas-3000/pragma-checked
Prevent multiple instrumentation of a same check and instrumentation of instrumentation assertions
2 parents 7b40362 + 0aaf191 commit 8ab933b

File tree

3 files changed

+208
-64
lines changed

3 files changed

+208
-64
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <stdlib.h>
2+
const int N = 100;
3+
int main()
4+
{
5+
int *buf = malloc(N * sizeof(*buf));
6+
char *lb = (((char *)buf) - __CPROVER_POINTER_OFFSET(buf));
7+
char *ub = (((char *)buf) - __CPROVER_POINTER_OFFSET(buf)) +
8+
__CPROVER_OBJECT_SIZE(buf) - 1;
9+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
CORE
2+
main.c
3+
--pointer-overflow-check _ --pointer-overflow-check --unsigned-overflow-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[malloc.assertion.1\].*: SUCCESS
7+
^\[malloc.assertion.2\].*: SUCCESS
8+
^\[main.overflow.1\].*: SUCCESS
9+
^\[main.pointer_arithmetic.1\].*: SUCCESS
10+
^\[main.pointer_arithmetic.2\].*: SUCCESS
11+
^\[main.pointer_arithmetic.3\].*: SUCCESS
12+
^\[main.pointer_arithmetic.4\].*: SUCCESS
13+
^\[main.pointer_arithmetic.5\].*: SUCCESS
14+
^\[main.pointer_arithmetic.6\].*: SUCCESS
15+
^\[main.pointer_arithmetic.7\].*: SUCCESS
16+
^\[main.pointer_arithmetic.8\].*: SUCCESS
17+
^\[main.pointer_arithmetic.9\].*: SUCCESS
18+
^\[main.pointer_arithmetic.10\].*: SUCCESS
19+
^\[main.pointer_arithmetic.11\].*: SUCCESS
20+
^\[main.pointer_arithmetic.12\].*: SUCCESS
21+
^\[main.pointer_arithmetic.13\].*: SUCCESS
22+
^\[main.pointer_arithmetic.14\].*: SUCCESS
23+
^\[main.pointer_arithmetic.15\].*: SUCCESS
24+
^\[main.pointer_arithmetic.16\].*: SUCCESS
25+
^\[main.pointer_arithmetic.17\].*: SUCCESS
26+
^\*\* 0 of 20 failed \(1 iterations\)
27+
^VERIFICATION SUCCESSFUL$
28+
--
29+
^\[main.overflow.\d+\].*: FAILURE
30+
--
31+
Checks that assertions generated by the first --pointer-overflow-check:
32+
- do not get duplicated by the second --unsigned-overflow-check
33+
- do not get instrumented with --unsigned-overflow-check (would fail the proof)
34+
35+
We expect 20 assertions to be generated:
36+
In the goto-instrument run:
37+
- 2 for using malloc
38+
- 17 caused by --pointer-overflow-check
39+
40+
In the final cbmc run:
41+
- 0 caused by --pointer-overflow-check
42+
- 1 caused by the --unsigned-overflow-check

src/analyses/goto_check_c.cpp

Lines changed: 157 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Author: Daniel Kroening, [email protected]
1212
#include "goto_check_c.h"
1313

1414
#include <algorithm>
15+
#include <optional>
1516

1617
#include <util/arith_tools.h>
1718
#include <util/array_name.h>
@@ -278,6 +279,22 @@ class goto_check_ct
278279
bool enable_assumptions;
279280
bool enable_pointer_primitive_check;
280281

282+
/// Maps a named-check name to the corresponding boolean flag.
283+
std::map<irep_idt, bool *> name_to_flag{
284+
{"bounds-check", &enable_bounds_check},
285+
{"pointer-check", &enable_pointer_check},
286+
{"memory-leak-check", &enable_memory_leak_check},
287+
{"div-by-zero-check", &enable_div_by_zero_check},
288+
{"enum-range-check", &enable_enum_range_check},
289+
{"signed-overflow-check", &enable_signed_overflow_check},
290+
{"unsigned-overflow-check", &enable_unsigned_overflow_check},
291+
{"pointer-overflow-check", &enable_pointer_overflow_check},
292+
{"conversion-check", &enable_conversion_check},
293+
{"undefined-shift-check", &enable_undefined_shift_check},
294+
{"float-overflow-check", &enable_float_overflow_check},
295+
{"nan-check", &enable_nan_check},
296+
{"pointer-primitive-check", &enable_pointer_primitive_check}};
297+
281298
typedef optionst::value_listt error_labelst;
282299
error_labelst error_labels;
283300

@@ -288,6 +305,36 @@ class goto_check_ct
288305
allocationst allocations;
289306

290307
irep_idt mode;
308+
309+
/// \brief Adds "checked" pragmas for all currently active named checks
310+
/// on the given source location.
311+
void add_active_named_check_pragmas(source_locationt &source_location) const;
312+
313+
/// \brief Adds "disable" pragmas for all named checks
314+
/// on the given source location.
315+
void
316+
add_all_disable_named_check_pragmas(source_locationt &source_location) const;
317+
318+
/// activation statuses for named checks
319+
typedef enum
320+
{
321+
ENABLE,
322+
DISABLE,
323+
CHECKED
324+
} check_statust;
325+
326+
/// optional (named-check, status) pair
327+
using named_check_statust = optionalt<std::pair<irep_idt, check_statust>>;
328+
329+
/// Matches a named-check string of the form
330+
///
331+
/// ```
332+
/// ("enable"|"disable"|"checked") ":" <named-check>
333+
/// ```
334+
///
335+
/// \returns a pair (name, status) if the match succeeds
336+
/// and the name is known, nothing otherwise.
337+
named_check_statust match_named_check(const irep_idt &named_check) const;
291338
};
292339

293340
void goto_check_ct::collect_allocations(const goto_functionst &goto_functions)
@@ -1566,6 +1613,8 @@ void goto_check_ct::add_guarded_property(
15661613
t->source_location_nonconst().set_comment(
15671614
comment + " in " + source_expr_string);
15681615
t->source_location_nonconst().set_property_class(property_class);
1616+
1617+
add_all_disable_named_check_pragmas(t->source_location_nonconst());
15691618
}
15701619
}
15711620

@@ -1840,11 +1889,13 @@ optionalt<exprt> goto_check_ct::rw_ok_check(exprt expr)
18401889
return {};
18411890
}
18421891

1843-
/// \brief Set a Boolean flag to a new value (via `set_flag`) and restore
1844-
/// the previous value when the entire object goes out of scope.
1892+
/// Allows to:
1893+
/// - override a Boolean flag with a new value via `set_flag`
1894+
/// - set a Boolean flag to false via `disable_flag`, such that
1895+
/// previous `set_flag` are overridden and future `set_flag` are ignored.
18451896
///
1846-
/// \remarks Calls to set_value are tracked to allow detecting doubles sets
1847-
/// with different values and trigger an INVARIANT.
1897+
/// A flag's initial value (before any `set_flag` or `disable_flag`) is restored
1898+
/// when the entire object goes out of scope.
18481899
class flag_resett
18491900
{
18501901
public:
@@ -1856,24 +1907,50 @@ class flag_resett
18561907
/// \brief Store the current value of \p flag and
18571908
/// then set its value to \p new_value.
18581909
///
1859-
/// \remarks an INVARIANT triggers iff the flag is set
1860-
/// more than once with different values.
1910+
/// - calling `set_flag` after `disable_flag` is a no-op
1911+
/// - calling `set_flag` twice triggers an INVARIANT
18611912
void set_flag(bool &flag, bool new_value, const irep_idt &flag_name)
18621913
{
1863-
bool seen = flags_to_reset.find(&flag) != flags_to_reset.end();
1914+
// make this a no-op if the flag is disabled
1915+
if(disabled_flags.find(&flag) != disabled_flags.end())
1916+
return;
1917+
1918+
// detect double sets
18641919
INVARIANT(
1865-
!(seen && flag != new_value),
1866-
"Flag " + id2string(flag_name) +
1867-
" set twice with incompatible values "
1868-
" at \n" +
1920+
flags_to_reset.find(&flag) == flags_to_reset.end(),
1921+
"Flag " + id2string(flag_name) + " set twice at \n" +
18691922
instruction.source_location().pretty());
18701923
if(flag != new_value)
18711924
{
1872-
flags_to_reset.emplace(&flag, flag);
1925+
flags_to_reset[&flag] = flag;
18731926
flag = new_value;
18741927
}
18751928
}
18761929

1930+
/// Sets the given flag to false, overriding any previous value.
1931+
///
1932+
/// - calling `disable_flag` after `set_flag` overrides the set value
1933+
/// - calling `disable_flag` twice triggers an INVARIANT
1934+
void disable_flag(bool &flag, const irep_idt &flag_name)
1935+
{
1936+
INVARIANT(
1937+
disabled_flags.find(&flag) == disabled_flags.end(),
1938+
"Flag " + id2string(flag_name) + " disabled twice at \n" +
1939+
instruction.source_location().pretty());
1940+
1941+
disabled_flags.insert(&flag);
1942+
1943+
// If the flag has not already been set,
1944+
// we store its current value in the reset map.
1945+
// Otherwise, the reset map already holds
1946+
// the initial value we want to reset it to, keep it as is.
1947+
if(flags_to_reset.find(&flag) == flags_to_reset.end())
1948+
flags_to_reset[&flag] = flag;
1949+
1950+
// set the flag to false in all cases.
1951+
flag = false;
1952+
}
1953+
18771954
/// \brief Restore the values of all flags that have been
18781955
/// modified via `set_flag`.
18791956
~flag_resett()
@@ -1885,6 +1962,7 @@ class flag_resett
18851962
private:
18861963
const goto_programt::instructiont &instruction;
18871964
std::map<bool *, bool> flags_to_reset;
1965+
std::set<bool *> disabled_flags;
18881966
};
18891967

18901968
void goto_check_ct::goto_check(
@@ -1915,60 +1993,32 @@ void goto_check_ct::goto_check(
19151993
const auto &pragmas = i.source_location().get_pragmas();
19161994
for(const auto &d : pragmas)
19171995
{
1918-
if(d.first == "disable:bounds-check")
1919-
resetter.set_flag(enable_bounds_check, false, d.first);
1920-
else if(d.first == "disable:pointer-check")
1921-
resetter.set_flag(enable_pointer_check, false, d.first);
1922-
else if(d.first == "disable:memory-leak-check")
1923-
resetter.set_flag(enable_memory_leak_check, false, d.first);
1924-
else if(d.first == "disable:div-by-zero-check")
1925-
resetter.set_flag(enable_div_by_zero_check, false, d.first);
1926-
else if(d.first == "disable:enum-range-check")
1927-
resetter.set_flag(enable_enum_range_check, false, d.first);
1928-
else if(d.first == "disable:signed-overflow-check")
1929-
resetter.set_flag(enable_signed_overflow_check, false, d.first);
1930-
else if(d.first == "disable:unsigned-overflow-check")
1931-
resetter.set_flag(enable_unsigned_overflow_check, false, d.first);
1932-
else if(d.first == "disable:pointer-overflow-check")
1933-
resetter.set_flag(enable_pointer_overflow_check, false, d.first);
1934-
else if(d.first == "disable:float-overflow-check")
1935-
resetter.set_flag(enable_float_overflow_check, false, d.first);
1936-
else if(d.first == "disable:conversion-check")
1937-
resetter.set_flag(enable_conversion_check, false, d.first);
1938-
else if(d.first == "disable:undefined-shift-check")
1939-
resetter.set_flag(enable_undefined_shift_check, false, d.first);
1940-
else if(d.first == "disable:nan-check")
1941-
resetter.set_flag(enable_nan_check, false, d.first);
1942-
else if(d.first == "disable:pointer-primitive-check")
1943-
resetter.set_flag(enable_pointer_primitive_check, false, d.first);
1944-
else if(d.first == "enable:bounds-check")
1945-
resetter.set_flag(enable_bounds_check, true, d.first);
1946-
else if(d.first == "enable:pointer-check")
1947-
resetter.set_flag(enable_pointer_check, true, d.first);
1948-
else if(d.first == "enable:memory_leak-check")
1949-
resetter.set_flag(enable_memory_leak_check, true, d.first);
1950-
else if(d.first == "enable:div-by-zero-check")
1951-
resetter.set_flag(enable_div_by_zero_check, true, d.first);
1952-
else if(d.first == "enable:enum-range-check")
1953-
resetter.set_flag(enable_enum_range_check, true, d.first);
1954-
else if(d.first == "enable:signed-overflow-check")
1955-
resetter.set_flag(enable_signed_overflow_check, true, d.first);
1956-
else if(d.first == "enable:unsigned-overflow-check")
1957-
resetter.set_flag(enable_unsigned_overflow_check, true, d.first);
1958-
else if(d.first == "enable:pointer-overflow-check")
1959-
resetter.set_flag(enable_pointer_overflow_check, true, d.first);
1960-
else if(d.first == "enable:float-overflow-check")
1961-
resetter.set_flag(enable_float_overflow_check, true, d.first);
1962-
else if(d.first == "enable:conversion-check")
1963-
resetter.set_flag(enable_conversion_check, true, d.first);
1964-
else if(d.first == "enable:undefined-shift-check")
1965-
resetter.set_flag(enable_undefined_shift_check, true, d.first);
1966-
else if(d.first == "enable:nan-check")
1967-
resetter.set_flag(enable_nan_check, true, d.first);
1968-
else if(d.first == "enable:pointer-primitive-check")
1969-
resetter.set_flag(enable_pointer_primitive_check, true, d.first);
1996+
// match named-check related pragmas
1997+
auto matched = match_named_check(d.first);
1998+
if(matched.has_value())
1999+
{
2000+
auto named_check = matched.value();
2001+
auto name = named_check.first;
2002+
auto status = named_check.second;
2003+
bool *flag = name_to_flag.find(name)->second;
2004+
switch(status)
2005+
{
2006+
case check_statust::ENABLE:
2007+
resetter.set_flag(*flag, true, name);
2008+
break;
2009+
case check_statust::DISABLE:
2010+
resetter.set_flag(*flag, false, name);
2011+
break;
2012+
case check_statust::CHECKED:
2013+
resetter.disable_flag(*flag, name);
2014+
break;
2015+
}
2016+
}
19702017
}
19712018

2019+
// add checked pragmas for all active checks
2020+
add_active_named_check_pragmas(i.source_location_nonconst());
2021+
19722022
new_code.clear();
19732023

19742024
// we clear all recorded assertions if
@@ -2398,3 +2448,46 @@ void goto_check_c(
23982448
const namespacet ns(goto_model.symbol_table);
23992449
goto_check_c(ns, options, goto_model.goto_functions, message_handler);
24002450
}
2451+
2452+
void goto_check_ct::add_active_named_check_pragmas(
2453+
source_locationt &source_location) const
2454+
{
2455+
for(const auto &entry : name_to_flag)
2456+
if(*(entry.second))
2457+
source_location.add_pragma("checked:" + id2string(entry.first));
2458+
}
2459+
2460+
void goto_check_ct::add_all_disable_named_check_pragmas(
2461+
source_locationt &source_location) const
2462+
{
2463+
for(const auto &entry : name_to_flag)
2464+
source_location.add_pragma("disable:" + id2string(entry.first));
2465+
}
2466+
2467+
goto_check_ct::named_check_statust
2468+
goto_check_ct::match_named_check(const irep_idt &named_check) const
2469+
{
2470+
auto s = id2string(named_check);
2471+
auto col = s.find(":");
2472+
2473+
if(col == std::string::npos)
2474+
return {}; // separator not found
2475+
2476+
auto name = s.substr(col + 1);
2477+
2478+
if(name_to_flag.find(name) == name_to_flag.end())
2479+
return {}; // name unknown
2480+
2481+
check_statust status;
2482+
if(!s.compare(0, 6, "enable"))
2483+
status = check_statust::ENABLE;
2484+
else if(!s.compare(0, 7, "disable"))
2485+
status = check_statust::DISABLE;
2486+
else if(!s.compare(0, 7, "checked"))
2487+
status = check_statust::CHECKED;
2488+
else
2489+
return {}; // prefix unknow
2490+
2491+
// success
2492+
return std::pair<irep_idt, check_statust>{name, status};
2493+
}

0 commit comments

Comments
 (0)