Skip to content

Commit fa9256f

Browse files
committed
introduce 'fatal assertions'
This introduces a variant of ASSERT instructions that are fatal when they are refuted. Execution paths through fatal assertions that are refuted are undefined. Assertions that (otherwise) pass and that are reachable from a refuted fatal assertion are now reported as UNKNOWN. The motivating use-case for fatal assertions is undefined behavior in languages such as C/C++ or Rust.
1 parent 6cad8ca commit fa9256f

File tree

5 files changed

+173
-1
lines changed

5 files changed

+173
-1
lines changed

src/goto-checker/Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
SRC = bmc_util.cpp \
22
counterexample_beautification.cpp \
33
cover_goals_report_util.cpp \
4-
incremental_goto_checker.cpp \
4+
fatal_assertions.cpp \
55
goto_symex_fault_localizer.cpp \
66
goto_symex_property_decider.cpp \
77
goto_trace_storage.cpp \
88
goto_verifier.cpp \
9+
incremental_goto_checker.cpp \
910
multi_path_symex_checker.cpp \
1011
multi_path_symex_only_checker.cpp \
1112
properties.cpp \

src/goto-checker/fatal_assertions.cpp

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*******************************************************************\
2+
3+
Module: Fatal Assertions
4+
5+
Author: Daniel Kroening, [email protected]
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Fatal Assertions
11+
12+
#include "fatal_assertions.h"
13+
14+
#include <util/irep_hash.h>
15+
16+
#include <goto-programs/goto_functions.h>
17+
18+
#include <stack>
19+
#include <unordered_set>
20+
21+
struct function_loc_pairt
22+
{
23+
using function_itt = goto_functionst::function_mapt::const_iterator;
24+
function_loc_pairt(
25+
function_itt __function_it,
26+
goto_programt::const_targett __target)
27+
: function_it(__function_it), target(__target)
28+
{
29+
}
30+
function_itt function_it;
31+
goto_programt::const_targett target;
32+
bool operator==(const function_loc_pairt &other) const
33+
{
34+
return function_it->first == other.function_it->first &&
35+
target == other.target;
36+
}
37+
};
38+
39+
struct function_loc_pair_hasht
40+
{
41+
std::size_t operator()(const function_loc_pairt &p) const
42+
{
43+
auto h1 = p.function_it->first.hash();
44+
auto h2 = const_target_hash{}(p.target);
45+
return hash_combine(h1, h2);
46+
}
47+
};
48+
49+
using loc_sett =
50+
std::unordered_set<function_loc_pairt, function_loc_pair_hasht>;
51+
52+
static void
53+
reachable_fixpoint(loc_sett &locs, const goto_functionst &goto_functions)
54+
{
55+
std::stack<function_loc_pairt> working;
56+
57+
for(auto loc : locs)
58+
working.push(loc);
59+
60+
while(!working.empty())
61+
{
62+
auto loc = working.top();
63+
working.pop();
64+
locs.insert(loc);
65+
66+
if(loc.target->is_function_call())
67+
{
68+
}
69+
else
70+
{
71+
auto &body = loc.function_it->second.body;
72+
73+
for(auto successor : body.get_successors(loc.target))
74+
working.emplace(loc.function_it, successor);
75+
}
76+
}
77+
}
78+
79+
void propagate_fatal_assertions(
80+
propertiest &properties,
81+
const goto_functionst &goto_functions)
82+
{
83+
// Iterate to find refuted fatal assertions. Anything reachalble
84+
// from there is a 'fatal loc'.
85+
loc_sett fatal_locs;
86+
87+
for(auto function_it = goto_functions.function_map.begin();
88+
function_it != goto_functions.function_map.end();
89+
function_it++)
90+
{
91+
auto &body = function_it->second.body;
92+
for(auto target = body.instructions.begin();
93+
target != body.instructions.end();
94+
target++)
95+
{
96+
if(target->is_assert() && target->source_location().property_fatal())
97+
{
98+
auto id = target->source_location().get_property_id();
99+
auto property = properties.find(id);
100+
CHECK_RETURN(property != properties.end());
101+
102+
// Status?
103+
if(property->second.status == property_statust::FAIL)
104+
{
105+
fatal_locs.emplace(function_it, target);
106+
}
107+
}
108+
}
109+
}
110+
111+
// Saturate fixpoint.
112+
reachable_fixpoint(fatal_locs, goto_functions);
113+
114+
// Now mark PASS assertions as UNKNOWN.
115+
for(auto &loc : fatal_locs)
116+
{
117+
if(loc.target->is_assert())
118+
{
119+
auto id = loc.target->source_location().get_property_id();
120+
auto property = properties.find(id);
121+
CHECK_RETURN(property != properties.end());
122+
123+
// Status?
124+
if(property->second.status == property_statust::PASS)
125+
{
126+
property->second.status = property_statust::UNKNOWN;
127+
}
128+
}
129+
}
130+
}

src/goto-checker/fatal_assertions.h

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*******************************************************************\
2+
3+
Module: Fatal Assertions
4+
5+
Author: Daniel Kroening, [email protected]
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Fatal Assertions
11+
12+
#ifndef CPROVER_GOTO_CHECKER_FATAL_ASSERTIONS_H
13+
#define CPROVER_GOTO_CHECKER_FATAL_ASSERTIONS_H
14+
15+
#include "properties.h"
16+
17+
/// Assertions after fatal assertions that are
18+
/// refuted do not have meaning, and passing ones
19+
/// are marked as UNKNOWN.
20+
/// Assertions that are independently refuted are left
21+
/// in that state, even though the counterexample
22+
/// might be invalid.
23+
/// This avoids ambiguity if two refuted fatal asserions
24+
/// may reach each other.
25+
void propagate_fatal_assertions(propertiest &, const abstract_goto_modelt &);
26+
27+
#endif // CPROVER_GOTO_CHECKER_FATAL_ASSERTIONS_H

src/util/irep_ids.def

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ IREP_ID_ONE(line)
2626
IREP_ID_ONE(column)
2727
IREP_ID_ONE(comment)
2828
IREP_ID_ONE(property_class)
29+
IREP_ID_ONE(property_fatal)
2930
IREP_ID_ONE(property_id)
3031
IREP_ID_ONE(function)
3132
IREP_ID_ONE(mathematical_function)

src/util/source_location.h

+13
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ class source_locationt:public irept
9696
return find(ID_basic_block_source_lines);
9797
}
9898

99+
bool property_fatal() const
100+
{
101+
return get_bool(ID_property_fatal);
102+
}
103+
99104
void set_file(const irep_idt &file)
100105
{
101106
set(ID_file, file);
@@ -163,6 +168,14 @@ class source_locationt:public irept
163168
add(ID_basic_block_source_lines, std::move(source_lines));
164169
}
165170

171+
void property_fatal(bool _property_fatal)
172+
{
173+
if(_property_fatal)
174+
set(ID_property_fatal, true);
175+
else
176+
remove(ID_property_fatal);
177+
}
178+
166179
void set_hide()
167180
{
168181
set(ID_hide, true);

0 commit comments

Comments
 (0)