Skip to content

Commit 7cc069d

Browse files
committed
goto-symex/concurrency: limit unsoundness-abort to dereferencing shared pointers
Reading and writing pointers is not not itself unsound, only the interaction with value sets may cause unsound results. Hence, do not reject programs that never dereference a shared pointer. Fixes: #8714
1 parent 4fe3ade commit 7cc069d

File tree

9 files changed

+61
-73
lines changed

9 files changed

+61
-73
lines changed

regression/cbmc-concurrency/global_pointer1/main.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ void *thread1(void * arg)
1212
void *thread2(void *arg)
1313
{
1414
assert(v == &g);
15+
#ifndef NO_DEREF
1516
*v = 1;
17+
#endif
1618
}
1719

1820
int main()
@@ -26,7 +28,9 @@ int main()
2628
pthread_join(t2, 0);
2729

2830
assert(v == &g);
31+
#ifndef NO_DEREF
2932
assert(*v == 1);
33+
#endif
3034

3135
return 0;
3236
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
KNOWNBUG
2+
main.c
3+
-DNO_DEREF
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
--
10+
Simplification with value sets causes unsound results despite the absence of
11+
dereferencing.

src/goto-symex/field_sensitivity.cpp

Lines changed: 12 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -359,15 +359,13 @@ void field_sensitivityt::field_assignments(
359359
goto_symex_statet &state,
360360
const ssa_exprt &lhs,
361361
const exprt &rhs,
362-
symex_targett &target,
363-
bool allow_pointer_unsoundness) const
362+
symex_targett &target) const
364363
{
365364
const exprt lhs_fs = get_fields(ns, state, lhs, false);
366365

367366
if(lhs != lhs_fs)
368367
{
369-
field_assignments_rec(
370-
ns, state, lhs_fs, rhs, target, allow_pointer_unsoundness);
368+
field_assignments_rec(ns, state, lhs_fs, rhs, target);
371369
// Erase the composite symbol from our working state. Note that we need to
372370
// have it in the propagation table and the value set while doing the field
373371
// assignments, thus we cannot skip putting it in there above.
@@ -388,22 +386,18 @@ void field_sensitivityt::field_assignments(
388386
/// \param lhs_fs: expanded symbol
389387
/// \param ssa_rhs: right-hand-side value to assign
390388
/// \param target: symbolic execution equation store
391-
/// \param allow_pointer_unsoundness: allow pointer unsoundness
392389
void field_sensitivityt::field_assignments_rec(
393390
const namespacet &ns,
394391
goto_symex_statet &state,
395392
const exprt &lhs_fs,
396393
const exprt &ssa_rhs,
397-
symex_targett &target,
398-
bool allow_pointer_unsoundness) const
394+
symex_targett &target) const
399395
{
400396
if(is_ssa_expr(lhs_fs))
401397
{
402398
const ssa_exprt &l1_lhs = to_ssa_expr(lhs_fs);
403399
const ssa_exprt ssa_lhs =
404-
state
405-
.assignment(l1_lhs, ssa_rhs, ns, true, true, allow_pointer_unsoundness)
406-
.get();
400+
state.assignment(l1_lhs, ssa_rhs, ns, true, true).get();
407401

408402
// do the assignment
409403
target.assignment(
@@ -454,16 +448,10 @@ void field_sensitivityt::field_assignments_rec(
454448
expr_try_dynamic_cast<field_sensitive_ssa_exprt>(member_lhs))
455449
{
456450
field_assignments_rec(
457-
ns,
458-
state,
459-
fs_ssa->get_object_ssa(),
460-
member_rhs,
461-
target,
462-
allow_pointer_unsoundness);
451+
ns, state, fs_ssa->get_object_ssa(), member_rhs, target);
463452
}
464453

465-
field_assignments_rec(
466-
ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness);
454+
field_assignments_rec(ns, state, member_lhs, member_rhs, target);
467455
++fs_it;
468456
}
469457
}
@@ -499,16 +487,10 @@ void field_sensitivityt::field_assignments_rec(
499487
expr_try_dynamic_cast<field_sensitive_ssa_exprt>(member_lhs))
500488
{
501489
field_assignments_rec(
502-
ns,
503-
state,
504-
fs_ssa->get_object_ssa(),
505-
member_rhs,
506-
target,
507-
allow_pointer_unsoundness);
490+
ns, state, fs_ssa->get_object_ssa(), member_rhs, target);
508491
}
509492

510-
field_assignments_rec(
511-
ns, state, member_lhs, member_rhs, target, allow_pointer_unsoundness);
493+
field_assignments_rec(ns, state, member_lhs, member_rhs, target);
512494
++fs_it;
513495
}
514496
}
@@ -540,16 +522,10 @@ void field_sensitivityt::field_assignments_rec(
540522
expr_try_dynamic_cast<field_sensitive_ssa_exprt>(index_lhs))
541523
{
542524
field_assignments_rec(
543-
ns,
544-
state,
545-
fs_ssa->get_object_ssa(),
546-
index_rhs,
547-
target,
548-
allow_pointer_unsoundness);
525+
ns, state, fs_ssa->get_object_ssa(), index_rhs, target);
549526
}
550527

551-
field_assignments_rec(
552-
ns, state, index_lhs, index_rhs, target, allow_pointer_unsoundness);
528+
field_assignments_rec(ns, state, index_lhs, index_rhs, target);
553529
++fs_it;
554530
}
555531
}
@@ -565,17 +541,10 @@ void field_sensitivityt::field_assignments_rec(
565541
{
566542
if(auto fs_ssa = expr_try_dynamic_cast<field_sensitive_ssa_exprt>(*fs_it))
567543
{
568-
field_assignments_rec(
569-
ns,
570-
state,
571-
fs_ssa->get_object_ssa(),
572-
op,
573-
target,
574-
allow_pointer_unsoundness);
544+
field_assignments_rec(ns, state, fs_ssa->get_object_ssa(), op, target);
575545
}
576546

577-
field_assignments_rec(
578-
ns, state, *fs_it, op, target, allow_pointer_unsoundness);
547+
field_assignments_rec(ns, state, *fs_it, op, target);
579548
++fs_it;
580549
}
581550
}

src/goto-symex/field_sensitivity.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,12 @@ class field_sensitivityt
142142
/// \param rhs: right-hand-side value that was used in the preceding update of
143143
/// the full object
144144
/// \param target: symbolic execution equation store
145-
/// \param allow_pointer_unsoundness: allow pointer unsoundness
146145
void field_assignments(
147146
const namespacet &ns,
148147
goto_symex_statet &state,
149148
const ssa_exprt &lhs,
150149
const exprt &rhs,
151-
symex_targett &target,
152-
bool allow_pointer_unsoundness) const;
150+
symex_targett &target) const;
153151

154152
/// Turn an expression \p expr into a field-sensitive SSA expression.
155153
/// Field-sensitive SSA expressions have individual symbols for each
@@ -215,8 +213,7 @@ class field_sensitivityt
215213
goto_symex_statet &state,
216214
const exprt &lhs_fs,
217215
const exprt &ssa_rhs,
218-
symex_targett &target,
219-
bool allow_pointer_unsoundness) const;
216+
symex_targett &target) const;
220217

221218
[[nodiscard]] exprt simplify_opt(
222219
exprt e,

src/goto-symex/goto_symex_state.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,7 @@ renamedt<ssa_exprt, L2> goto_symex_statet::assignment(
7979
const exprt &rhs, // L2
8080
const namespacet &ns,
8181
bool rhs_is_simplified,
82-
bool record_value,
83-
bool allow_pointer_unsoundness)
82+
bool record_value)
8483
{
8584
// identifier should be l0 or l1, make sure it's l1
8685
lhs = rename_ssa<L1>(std::move(lhs), ns).get();
@@ -110,11 +109,6 @@ renamedt<ssa_exprt, L2> goto_symex_statet::assignment(
110109
DATA_INVARIANT(!check_renaming(rhs), "rhs renaming failed on l2");
111110
}
112111

113-
// see #305 on GitHub for a simple example and possible discussion
114-
if(is_shared && lhs.type().id() == ID_pointer && !allow_pointer_unsoundness)
115-
throw unsupported_operation_exceptiont(
116-
"pointer handling for concurrency is unsound");
117-
118112
// Update constant propagation map -- the RHS is L2
119113
if(!is_shared && record_value && goto_symex_can_forward_propagatet(ns)(rhs))
120114
{

src/goto-symex/goto_symex_state.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,7 @@ class goto_symex_statet final : public goto_statet
116116
const exprt &rhs, // L2
117117
const namespacet &ns,
118118
bool rhs_is_simplified,
119-
bool record_value,
120-
bool allow_pointer_unsoundness = false);
119+
bool record_value);
121120

122121
field_sensitivityt field_sensitivity;
123122

src/goto-symex/symex_assign.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,7 @@ void symex_assignt::assign_non_struct_symbol(
217217
assignment.rhs,
218218
ns,
219219
symex_config.simplify_opt,
220-
symex_config.constant_propagation,
221-
symex_config.allow_pointer_unsoundness)
220+
symex_config.constant_propagation)
222221
.get();
223222

224223
state.record_events.push(false);
@@ -254,12 +253,7 @@ void symex_assignt::assign_non_struct_symbol(
254253
{
255254
// Split composite symbol lhs into its components
256255
state.field_sensitivity.field_assignments(
257-
ns,
258-
state,
259-
l1_lhs,
260-
assignment.rhs,
261-
target,
262-
symex_config.allow_pointer_unsoundness);
256+
ns, state, l1_lhs, assignment.rhs, target);
263257
}
264258
}
265259

src/goto-symex/symex_dereference.cpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@ Author: Daniel Kroening, kroening@kroening.com
99
/// \file
1010
/// Symbolic Execution of ANSI-C
1111

12-
#include "goto_symex.h"
13-
1412
#include <util/arith_tools.h>
1513
#include <util/byte_operators.h>
1614
#include <util/c_types.h>
1715
#include <util/exception_utils.h>
1816
#include <util/expr_util.h>
17+
#include <util/find_symbols.h>
1918
#include <util/fresh_symbol.h>
2019
#include <util/invariant.h>
2120
#include <util/pointer_offset_size.h>
2221

2322
#include <pointer-analysis/value_set_dereference.h>
2423

2524
#include "expr_skeleton.h"
25+
#include "goto_symex.h"
2626
#include "path_storage.h"
2727
#include "symex_assign.h"
2828
#include "symex_dereference_state.h"
@@ -307,6 +307,28 @@ void goto_symext::dereference_rec(
307307
// dereference an L2 pointer, which it would not have an entry for.
308308

309309
tmp1 = state.rename<L1_WITH_CONSTANT_PROPAGATION>(tmp1, ns).get();
310+
if(
311+
tmp1.id() != ID_address_of && state.threads.size() > 1 &&
312+
!symex_config.allow_pointer_unsoundness)
313+
{
314+
// Make sure we are not trying to dereference a shared pointer, as this
315+
// may be unsound: see #305 on GitHub for a simple example and possible
316+
// discussion.
317+
for(const auto &sym_expr : find_symbols(tmp1))
318+
{
319+
const irep_idt obj_name = is_ssa_expr(sym_expr)
320+
? to_ssa_expr(sym_expr).get_object_name()
321+
: sym_expr.get_identifier();
322+
if(
323+
obj_name != goto_symex_statet::guard_identifier() &&
324+
(ns.lookup(obj_name).is_shared() || path_storage.dirty(obj_name)))
325+
{
326+
throw unsupported_operation_exceptiont(
327+
"pointer handling for concurrency is unsound: " +
328+
id2string(obj_name));
329+
}
330+
}
331+
}
310332

311333
do_simplify(tmp1, state.value_set);
312334

unit/goto-symex/goto_symex_state.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ SCENARIO(
6565
WHEN("Symbol `foo` is assigned constant integer `475`")
6666
{
6767
const exprt rhs1 = from_integer(475, int_type);
68-
const auto result =
69-
state.assignment(ssa_foo, rhs1, ns, true, true, false);
68+
const auto result = state.assignment(ssa_foo, rhs1, ns, true, true);
7069
THEN("The result is `foo` renamed to L2")
7170
{
7271
REQUIRE(result.get().get_identifier() == "foo!0#1");
@@ -88,8 +87,7 @@ SCENARIO(
8887
THEN("Symbol `foo` is assigned another integer 1834")
8988
{
9089
const exprt rhs2 = from_integer(1834, int_type);
91-
const auto result2 =
92-
state.assignment(ssa_foo, rhs2, ns, true, true, false);
90+
const auto result2 = state.assignment(ssa_foo, rhs2, ns, true, true);
9391

9492
THEN("The level 2 index of `foo` is incremented")
9593
{
@@ -128,7 +126,7 @@ SCENARIO(
128126
{
129127
const null_pointer_exprt null_pointer{int_pointer_type};
130128
const auto result =
131-
state.assignment(ssa_foo, null_pointer, ns, true, true, false);
129+
state.assignment(ssa_foo, null_pointer, ns, true, true);
132130
THEN("The result is `foo` renamed to L2")
133131
{
134132
REQUIRE(result.get().get_identifier() == "foo!0#1");
@@ -155,7 +153,7 @@ SCENARIO(
155153
const address_of_exprt rhs2{int_value};
156154
const renamedt<exprt, L2> l2_rhs2 = state.rename(rhs2, ns);
157155
const auto result2 =
158-
state.assignment(ssa_foo, l2_rhs2.get(), ns, true, true, false);
156+
state.assignment(ssa_foo, l2_rhs2.get(), ns, true, true);
159157

160158
THEN("The level 2 index of `foo` is incremented")
161159
{

0 commit comments

Comments
 (0)