Skip to content

Commit f214ec5

Browse files
authored
Merge pull request #1656 from goblint/traces-vojdani-invariant
Fix Vojdani privatization `invariant_global`
2 parents 2955c1f + 457bf48 commit f214ec5

File tree

6 files changed

+393
-9
lines changed

6 files changed

+393
-9
lines changed

src/analyses/basePriv.ml

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -829,8 +829,30 @@ struct
829829
vf g;
830830
| _ -> ()
831831

832-
let invariant_global ask getg g =
833-
ValueDomain.invariant_global getg g
832+
let invariant_global (ask: Q.ask) getg g =
833+
let locks = ask.f (Q.MustProtectingLocks {global = g; write = false}) in
834+
if LockDomain.MustLockset.is_all locks then
835+
Invariant.none
836+
else (
837+
(* Only read g as protected, everything else (e.g. pointed to variables) may be unprotected.
838+
See 56-witness/69-ghost-ptr-protection and https://github.com/goblint/analyzer/pull/1394#discussion_r1698136411. *)
839+
let read_global g' = if CilType.Varinfo.equal g' g then getg g' else VD.top () in (* TODO: Could be more precise for at least those which might not have all same protecting locks? *)
840+
let inv = ValueDomain.invariant_global read_global g in
841+
(* Very conservative about multiple protecting mutexes: invariant is not claimed when any of them is held.
842+
It should be possible to be more precise because writes only happen with all of them held,
843+
but conjunction is unsound when one of the mutexes is temporarily unlocked.
844+
Hypothetical read-protection is also somehow relevant. *)
845+
LockDomain.MustLockset.fold (fun m acc ->
846+
if LockDomain.MustLock.equal m (LockDomain.MustLock.of_var LibraryFunctions.verifier_atomic_var) then
847+
acc
848+
else if ask.f (GhostVarAvailable (Locked m)) then (
849+
let var = WitnessGhost.to_varinfo (Locked m) in
850+
Invariant.(of_exp (Lval (GoblintCil.var var)) || acc) [@coverage off] (* bisect_ppx cannot handle redefined (||) *)
851+
)
852+
else
853+
Invariant.none
854+
) locks inv
855+
)
834856

835857
let invariant_vars ask getg st = protected_vars ask
836858
end
@@ -1010,7 +1032,7 @@ struct
10101032
| `Left g' -> (* unprotected *)
10111033
ValueDomain.invariant_global (fun g -> getg (V.unprotected g)) g'
10121034
| `Right g' -> (* protected *)
1013-
let locks = ask.f (Q.MustProtectingLocks g') in
1035+
let locks = ask.f (Q.MustProtectingLocks {global = g'; write = true}) in
10141036
if LockDomain.MustLockset.is_all locks || LockDomain.MustLockset.is_empty locks then
10151037
Invariant.none
10161038
else if VD.equal (getg (V.protected g')) (getg (V.unprotected g')) then

src/analyses/mutexAnalysis.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,8 @@ struct
226226
true
227227
else *)
228228
MustLockset.mem ml protecting
229-
| Queries.MustProtectingLocks g ->
230-
protecting ~write:true Strong g
229+
| Queries.MustProtectingLocks {global; write} ->
230+
protecting ~write Strong global
231231
| Queries.MustLockset ->
232232
let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in
233233
held_locks

src/domains/queries.ml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type maybepublic = {global: CilType.Varinfo.t; write: bool; protection: Protecti
5353
type maybepublicwithout = {global: CilType.Varinfo.t; write: bool; without_mutex: LockDomain.MustLock.t; protection: Protection.t} [@@deriving ord, hash]
5454
type mustbeprotectedby = {mutex: LockDomain.MustLock.t; global: CilType.Varinfo.t; write: bool; protection: Protection.t} [@@deriving ord, hash]
5555
type mustprotectedvars = {mutex: LockDomain.MustLock.t; write: bool} [@@deriving ord, hash]
56+
type mustprotectinglocks = {global: CilType.Varinfo.t; write: bool} [@@deriving ord, hash]
5657
type access =
5758
| Memory of {exp: CilType.Exp.t; var_opt: CilType.Varinfo.t option; kind: AccessKind.t} (** Memory location access (race). *)
5859
| Point (** Program point and state access (MHP), independent of memory location. *)
@@ -117,7 +118,7 @@ type _ t =
117118
| MustJoinedThreads: ConcDomain.MustThreadSet.t t
118119
| ThreadsJoinedCleanly: MustBool.t t
119120
| MustProtectedVars: mustprotectedvars -> VS.t t
120-
| MustProtectingLocks: CilType.Varinfo.t -> LockDomain.MustLockset.t t
121+
| MustProtectingLocks: mustprotectinglocks -> LockDomain.MustLockset.t t
121122
| Invariant: invariant_context -> Invariant.t t
122123
| InvariantGlobal: Obj.t -> Invariant.t t (** Argument must be of corresponding [Spec.V.t]. *)
123124
| WarnGlobal: Obj.t -> Unit.t t (** Argument must be of corresponding [Spec.V.t]. *)
@@ -405,7 +406,7 @@ struct
405406
| Any (IterSysVars (vq1, vf1)), Any (IterSysVars (vq2, vf2)) -> VarQuery.compare vq1 vq2 (* not comparing fs *)
406407
| Any (MutexType m1), Any (MutexType m2) -> Mval.Unit.compare m1 m2
407408
| Any (MustProtectedVars m1), Any (MustProtectedVars m2) -> compare_mustprotectedvars m1 m2
408-
| Any (MustProtectingLocks g1), Any (MustProtectingLocks g2) -> CilType.Varinfo.compare g1 g2
409+
| Any (MustProtectingLocks g1), Any (MustProtectingLocks g2) -> compare_mustprotectinglocks g1 g2
409410
| Any (MayBeModifiedSinceSetjmp e1), Any (MayBeModifiedSinceSetjmp e2) -> JmpBufDomain.BufferEntry.compare e1 e2
410411
| Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2
411412
| Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Mval.Exp.compare lv1 lv2
@@ -451,7 +452,7 @@ struct
451452
| Any (InvariantGlobal vi) -> Hashtbl.hash vi
452453
| Any (YamlEntryGlobal (vi, task)) -> Hashtbl.hash vi (* TODO: hash task *)
453454
| Any (MustProtectedVars m) -> hash_mustprotectedvars m
454-
| Any (MustProtectingLocks g) -> CilType.Varinfo.hash g
455+
| Any (MustProtectingLocks g) -> hash_mustprotectinglocks g
455456
| Any (MayBeModifiedSinceSetjmp e) -> JmpBufDomain.BufferEntry.hash e
456457
| Any (MustBeSingleThreaded {since_start}) -> Hashtbl.hash since_start
457458
| Any (TmpSpecial lv) -> Mval.Exp.hash lv

tests/regression/13-privatized/74-mutex.t

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,102 @@ Same with ghost_instrumentation and invariant_set entries.
277277
value: '! multithreaded || (m_locked || used == 0)'
278278
format: c_expression
279279

280-
Same with mutex-meet.
280+
Same protected invariant with vojdani but no unprotected invariant.
281+
282+
$ goblint --enable ana.sv-comp.functions --set ana.base.privatization vojdani --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 74-mutex.c
283+
[Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29)
284+
[Warning][Deadcode] Function 'producer' has dead code:
285+
on line 26 (74-mutex.c:26-26)
286+
[Warning][Deadcode] Logical lines of code (LLoC) summary:
287+
live: 14
288+
dead: 1
289+
total lines: 15
290+
[Warning][Deadcode][CWE-571] condition '1' (possibly inserted by CIL) is always true (74-mutex.c:19:10-19:11)
291+
[Info][Witness] witness generation summary:
292+
location invariants: 0
293+
loop invariants: 0
294+
flow-insensitive invariants: 1
295+
total generation entries: 2
296+
[Info][Race] Memory locations race summary:
297+
safe: 1
298+
vulnerable: 0
299+
unsafe: 0
300+
total memory locations: 1
301+
302+
$ yamlWitnessStrip < witness.yml
303+
- entry_type: ghost_instrumentation
304+
content:
305+
ghost_variables:
306+
- name: m_locked
307+
scope: global
308+
type: int
309+
initial:
310+
value: "0"
311+
format: c_expression
312+
- name: multithreaded
313+
scope: global
314+
type: int
315+
initial:
316+
value: "0"
317+
format: c_expression
318+
ghost_updates:
319+
- location:
320+
file_name: 74-mutex.c
321+
file_hash: $FILE_HASH
322+
line: 20
323+
column: 5
324+
function: producer
325+
updates:
326+
- variable: m_locked
327+
value: "1"
328+
format: c_expression
329+
- location:
330+
file_name: 74-mutex.c
331+
file_hash: $FILE_HASH
332+
line: 23
333+
column: 5
334+
function: producer
335+
updates:
336+
- variable: m_locked
337+
value: "0"
338+
format: c_expression
339+
- location:
340+
file_name: 74-mutex.c
341+
file_hash: $FILE_HASH
342+
line: 34
343+
column: 3
344+
function: main
345+
updates:
346+
- variable: multithreaded
347+
value: "1"
348+
format: c_expression
349+
- location:
350+
file_name: 74-mutex.c
351+
file_hash: $FILE_HASH
352+
line: 36
353+
column: 3
354+
function: main
355+
updates:
356+
- variable: m_locked
357+
value: "1"
358+
format: c_expression
359+
- location:
360+
file_name: 74-mutex.c
361+
file_hash: $FILE_HASH
362+
line: 38
363+
column: 3
364+
function: main
365+
updates:
366+
- variable: m_locked
367+
value: "0"
368+
format: c_expression
369+
- entry_type: flow_insensitive_invariant
370+
flow_insensitive_invariant:
371+
string: '! multithreaded || (m_locked || used == 0)'
372+
type: assertion
373+
format: C
374+
375+
Same as protection with mutex-meet.
281376

282377
$ goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 74-mutex.c
283378
[Success][Assert] Assertion "used == 0" will succeed (74-mutex.c:37:3-37:29)

tests/regression/56-witness/64-ghost-multiple-protecting.t

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,168 @@ protection-read has precise protected invariant for g2.
337337
type: assertion
338338
format: C
339339
340+
$ goblint --set ana.base.privatization vojdani --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 64-ghost-multiple-protecting.c
341+
[Info][Deadcode] Logical lines of code (LLoC) summary:
342+
live: 19
343+
dead: 0
344+
total lines: 19
345+
[Info][Witness] witness generation summary:
346+
location invariants: 0
347+
loop invariants: 0
348+
flow-insensitive invariants: 2
349+
total generation entries: 3
350+
[Info][Race] Memory locations race summary:
351+
safe: 2
352+
vulnerable: 0
353+
unsafe: 0
354+
total memory locations: 2
355+
356+
vojdani has precise protected invariant for g2, but no unprotected invariants.
357+
358+
$ yamlWitnessStrip < witness.yml
359+
- entry_type: ghost_instrumentation
360+
content:
361+
ghost_variables:
362+
- name: m1_locked
363+
scope: global
364+
type: int
365+
initial:
366+
value: "0"
367+
format: c_expression
368+
- name: m2_locked
369+
scope: global
370+
type: int
371+
initial:
372+
value: "0"
373+
format: c_expression
374+
- name: multithreaded
375+
scope: global
376+
type: int
377+
initial:
378+
value: "0"
379+
format: c_expression
380+
ghost_updates:
381+
- location:
382+
file_name: 64-ghost-multiple-protecting.c
383+
file_hash: $FILE_HASH
384+
line: 9
385+
column: 3
386+
function: t_fun
387+
updates:
388+
- variable: m1_locked
389+
value: "1"
390+
format: c_expression
391+
- location:
392+
file_name: 64-ghost-multiple-protecting.c
393+
file_hash: $FILE_HASH
394+
line: 10
395+
column: 3
396+
function: t_fun
397+
updates:
398+
- variable: m2_locked
399+
value: "1"
400+
format: c_expression
401+
- location:
402+
file_name: 64-ghost-multiple-protecting.c
403+
file_hash: $FILE_HASH
404+
line: 13
405+
column: 3
406+
function: t_fun
407+
updates:
408+
- variable: m2_locked
409+
value: "0"
410+
format: c_expression
411+
- location:
412+
file_name: 64-ghost-multiple-protecting.c
413+
file_hash: $FILE_HASH
414+
line: 14
415+
column: 3
416+
function: t_fun
417+
updates:
418+
- variable: m1_locked
419+
value: "0"
420+
format: c_expression
421+
- location:
422+
file_name: 64-ghost-multiple-protecting.c
423+
file_hash: $FILE_HASH
424+
line: 16
425+
column: 3
426+
function: t_fun
427+
updates:
428+
- variable: m1_locked
429+
value: "1"
430+
format: c_expression
431+
- location:
432+
file_name: 64-ghost-multiple-protecting.c
433+
file_hash: $FILE_HASH
434+
line: 17
435+
column: 3
436+
function: t_fun
437+
updates:
438+
- variable: m2_locked
439+
value: "1"
440+
format: c_expression
441+
- location:
442+
file_name: 64-ghost-multiple-protecting.c
443+
file_hash: $FILE_HASH
444+
line: 19
445+
column: 3
446+
function: t_fun
447+
updates:
448+
- variable: m2_locked
449+
value: "0"
450+
format: c_expression
451+
- location:
452+
file_name: 64-ghost-multiple-protecting.c
453+
file_hash: $FILE_HASH
454+
line: 20
455+
column: 3
456+
function: t_fun
457+
updates:
458+
- variable: m2_locked
459+
value: "1"
460+
format: c_expression
461+
- location:
462+
file_name: 64-ghost-multiple-protecting.c
463+
file_hash: $FILE_HASH
464+
line: 22
465+
column: 3
466+
function: t_fun
467+
updates:
468+
- variable: m2_locked
469+
value: "0"
470+
format: c_expression
471+
- location:
472+
file_name: 64-ghost-multiple-protecting.c
473+
file_hash: $FILE_HASH
474+
line: 23
475+
column: 3
476+
function: t_fun
477+
updates:
478+
- variable: m1_locked
479+
value: "0"
480+
format: c_expression
481+
- location:
482+
file_name: 64-ghost-multiple-protecting.c
483+
file_hash: $FILE_HASH
484+
line: 29
485+
column: 3
486+
function: main
487+
updates:
488+
- variable: multithreaded
489+
value: "1"
490+
format: c_expression
491+
- entry_type: flow_insensitive_invariant
492+
flow_insensitive_invariant:
493+
string: '! multithreaded || (m2_locked || (m1_locked || g2 == 0))'
494+
type: assertion
495+
format: C
496+
- entry_type: flow_insensitive_invariant
497+
flow_insensitive_invariant:
498+
string: '! multithreaded || (m2_locked || (m1_locked || g1 == 0))'
499+
type: assertion
500+
format: C
501+
340502
$ goblint --set ana.base.privatization mutex-meet --enable witness.yaml.enabled --set ana.activated[+] mutexGhosts --set witness.yaml.entry-types '["flow_insensitive_invariant", "ghost_instrumentation"]' 64-ghost-multiple-protecting.c
341503
[Info][Deadcode] Logical lines of code (LLoC) summary:
342504
live: 19

0 commit comments

Comments
 (0)