Skip to content

Commit 393f897

Browse files
authored
Merge pull request #1394 from goblint/yaml-witness-ghost
Generate flow-insensitive YAML witness invariants with ghosts for privatized variables
2 parents 68cd952 + ffe255b commit 393f897

49 files changed

Lines changed: 3293 additions & 77 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

conf/svcomp-ghost.json

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
{
2+
"ana": {
3+
"sv-comp": {
4+
"enabled": true,
5+
"functions": true
6+
},
7+
"int": {
8+
"def_exc": true,
9+
"enums": false,
10+
"interval": true
11+
},
12+
"float": {
13+
"interval": true,
14+
"evaluate_math_functions": true
15+
},
16+
"activated": [
17+
"base",
18+
"threadid",
19+
"threadflag",
20+
"threadreturn",
21+
"mallocWrapper",
22+
"mutexEvents",
23+
"mutex",
24+
"access",
25+
"race",
26+
"escape",
27+
"expRelation",
28+
"mhp",
29+
"assert",
30+
"var_eq",
31+
"symb_locks",
32+
"region",
33+
"thread",
34+
"threadJoins",
35+
"abortUnless",
36+
"mutexGhosts",
37+
"pthreadMutexType"
38+
],
39+
"path_sens": [
40+
"mutex",
41+
"malloc_null",
42+
"uninit",
43+
"expsplit",
44+
"activeSetjmp",
45+
"memLeak",
46+
"threadflag"
47+
],
48+
"context": {
49+
"widen": false
50+
},
51+
"base": {
52+
"arrays": {
53+
"domain": "partitioned"
54+
},
55+
"invariant": {
56+
"local": false,
57+
"global": true
58+
}
59+
},
60+
"relation": {
61+
"invariant": {
62+
"local": false,
63+
"global": true,
64+
"one-var": false
65+
}
66+
},
67+
"apron": {
68+
"invariant": {
69+
"diff-box": true
70+
}
71+
},
72+
"var_eq": {
73+
"invariant": {
74+
"enabled": false
75+
}
76+
},
77+
"race": {
78+
"free": false,
79+
"call": false
80+
},
81+
"autotune": {
82+
"enabled": true,
83+
"activated": [
84+
"singleThreaded",
85+
"mallocWrappers",
86+
"noRecursiveIntervals",
87+
"enums",
88+
"congruence",
89+
"octagon",
90+
"wideningThresholds",
91+
"loopUnrollHeuristic",
92+
"memsafetySpecification",
93+
"noOverflows",
94+
"termination",
95+
"tmpSpecialAnalysis"
96+
]
97+
}
98+
},
99+
"exp": {
100+
"region-offsets": true
101+
},
102+
"solver": "td3",
103+
"sem": {
104+
"unknown_function": {
105+
"spawn": false
106+
},
107+
"int": {
108+
"signed_overflow": "assume_none"
109+
},
110+
"null-pointer": {
111+
"dereference": "assume_none"
112+
}
113+
},
114+
"witness": {
115+
"graphml": {
116+
"enabled": false
117+
},
118+
"yaml": {
119+
"enabled": true,
120+
"format-version": "2.1",
121+
"entry-types": [
122+
"flow_insensitive_invariant",
123+
"ghost_instrumentation"
124+
]
125+
},
126+
"invariant": {
127+
"loop-head": true,
128+
"after-lock": true,
129+
"other": true,
130+
"accessed": false,
131+
"exact": true,
132+
"all-locals": false,
133+
"flow_insensitive-as": "invariant_set-location_invariant"
134+
}
135+
},
136+
"pre": {
137+
"enabled": false
138+
}
139+
}

scripts/goblint-lib-modules.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848

4949
"MessageCategory", # included in Messages
5050
"PreValueDomain", # included in ValueDomain
51+
"WitnessGhostVar", # included in WitnessGhost
5152

5253
"ConfigVersion",
5354
"ConfigProfile",

src/analyses/apron/relationAnalysis.apron.ml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,15 @@ struct
634634
)
635635
|> Enum.fold (fun acc x -> Invariant.(acc && of_exp x)) Invariant.none
636636

637+
let query_invariant_global ctx g =
638+
if GobConfig.get_bool "ana.relation.invariant.global" && ctx.ask (GhostVarAvailable Multithreaded) then (
639+
let var = WitnessGhost.to_varinfo Multithreaded in
640+
let inv = Priv.invariant_global (Analyses.ask_of_ctx ctx) ctx.global g in
641+
Invariant.(of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *)
642+
)
643+
else
644+
Invariant.none
645+
637646
let query ctx (type a) (q: a Queries.t): a Queries.result =
638647
let open Queries in
639648
let st = ctx.local in
@@ -655,6 +664,9 @@ struct
655664
let vf' x = vf (Obj.repr x) in
656665
Priv.iter_sys_vars ctx.global vq vf'
657666
| Queries.Invariant context -> query_invariant ctx context
667+
| Queries.InvariantGlobal g ->
668+
let g: V.t = Obj.obj g in
669+
query_invariant_global ctx g
658670
| _ -> Result.top q
659671

660672

src/analyses/apron/relationPriv.apron.ml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ module type S =
4646
val thread_return: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> ThreadIdDomain.Thread.t -> relation_components_t -> relation_components_t
4747
val iter_sys_vars: (V.t -> G.t) -> VarQuery.t -> V.t VarQuery.f -> unit (** [Queries.IterSysVars] for apron. *)
4848

49+
val invariant_global: Q.ask -> (V.t -> G.t) -> V.t -> Invariant.t
50+
(** Returns flow-insensitive invariant for global unknown. *)
51+
4952
val invariant_vars: Q.ask -> (V.t -> G.t) -> relation_components_t -> varinfo list
5053
(** Returns global variables which are privatized. *)
5154

@@ -137,6 +140,7 @@ struct
137140
{rel = RD.top (); priv = startstate ()}
138141

139142
let iter_sys_vars getg vq vf = ()
143+
let invariant_global ask getg g = Invariant.none
140144
let invariant_vars ask getg st = []
141145

142146
let init () = ()
@@ -424,6 +428,7 @@ struct
424428
{rel = getg (); priv = startstate ()}
425429

426430
let iter_sys_vars getg vq vf = () (* TODO: or report singleton global for any Global query? *)
431+
let invariant_global ask getg g = Invariant.none
427432
let invariant_vars ask getg st = protected_vars ask (* TODO: is this right? *)
428433

429434
let finalize () = ()
@@ -708,6 +713,41 @@ struct
708713

709714
let init () = ()
710715
let finalize () = ()
716+
717+
let invariant_global (ask: Q.ask) (getg: V.t -> G.t): V.t -> Invariant.t = function
718+
| `Left m' -> (* mutex *)
719+
let atomic = LockDomain.MustLock.equal m' (LockDomain.MustLock.of_var LibraryFunctions.verifier_atomic_var) in
720+
if atomic || ask.f (GhostVarAvailable (Locked m')) then (
721+
(* filters like query_invariant *)
722+
let one_var = GobConfig.get_bool "ana.relation.invariant.one-var" in
723+
let exact = GobConfig.get_bool "witness.invariant.exact" in
724+
725+
let rel = keep_only_protected_globals ask m' (get_m_with_mutex_inits ask getg m') in (* Could be more precise if mutex_inits invariant is added by disjunction instead of joining abstract values. *)
726+
let inv =
727+
RD.invariant rel
728+
|> List.enum
729+
|> Enum.filter_map (fun (lincons1: Apron.Lincons1.t) ->
730+
(* filter one-vars and exact *)
731+
(* TODO: exact filtering doesn't really work with octagon because it returns two SUPEQ constraints instead *)
732+
if (one_var || GobApron.Lincons1.num_vars lincons1 >= 2) && (exact || Apron.Lincons1.get_typ lincons1 <> EQ) then
733+
RD.cil_exp_of_lincons1 lincons1
734+
|> Option.filter (fun exp -> not (InvariantCil.exp_contains_tmp exp))
735+
else
736+
None
737+
)
738+
|> Enum.fold (fun acc x -> Invariant.(acc && of_exp x)) Invariant.none
739+
in
740+
if atomic then
741+
inv
742+
else (
743+
let var = WitnessGhost.to_varinfo (Locked m') in
744+
Invariant.(of_exp (Lval (GoblintCil.var var)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *)
745+
)
746+
)
747+
else
748+
Invariant.none
749+
| g -> (* global *)
750+
Invariant.none (* Could output unprotected one-variable (so non-relational) invariants, but probably not very useful. [BasePriv] does those anyway. *)
711751
end
712752

713753
(** May written variables. *)
@@ -1275,6 +1315,8 @@ struct
12751315
| _ -> ()
12761316

12771317
let finalize () = ()
1318+
1319+
let invariant_global ask getg g = Invariant.none
12781320
end
12791321

12801322
module TracingPriv = functor (Priv: S) -> functor (RD: RelationDomain.RD) ->

src/analyses/base.ml

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,6 +1181,9 @@ struct
11811181
not is_alloc || (is_alloc && not (ctx.ask (Queries.IsHeapVar v)))
11821182

11831183
let query_invariant ctx context =
1184+
let keep_local = GobConfig.get_bool "ana.base.invariant.local" in
1185+
let keep_global = GobConfig.get_bool "ana.base.invariant.global" in
1186+
11841187
let cpa = ctx.local.BaseDomain.cpa in
11851188
let ask = Analyses.ask_of_ctx ctx in
11861189

@@ -1193,6 +1196,13 @@ struct
11931196
in
11941197
let module I = ValueDomain.ValueInvariant (Arg) in
11951198

1199+
let var_filter v =
1200+
if is_global ask v then
1201+
keep_global
1202+
else
1203+
keep_local
1204+
in
1205+
11961206
let var_invariant ?offset v =
11971207
if not (InvariantCil.var_is_heap v) then
11981208
I.key_invariant v ?offset (Arg.find v)
@@ -1203,14 +1213,23 @@ struct
12031213
if Lval.Set.is_top context.Invariant.lvals then (
12041214
if !earlyglobs || ThreadFlag.has_ever_been_multi ask then (
12051215
let cpa_invariant =
1206-
CPA.fold (fun k v a ->
1207-
if not (is_global ask k) then
1208-
Invariant.(a && var_invariant k)
1209-
else
1210-
a
1211-
) cpa Invariant.none
1216+
if keep_local then (
1217+
CPA.fold (fun k v a ->
1218+
if not (is_global ask k) then
1219+
Invariant.(a && var_invariant k)
1220+
else
1221+
a
1222+
) cpa Invariant.none
1223+
)
1224+
else
1225+
Invariant.none
1226+
in
1227+
let priv_vars =
1228+
if keep_global then
1229+
Priv.invariant_vars ask (priv_getg ctx.global) ctx.local
1230+
else
1231+
[]
12121232
in
1213-
let priv_vars = Priv.invariant_vars ask (priv_getg ctx.global) ctx.local in
12141233
let priv_invariant =
12151234
List.fold_left (fun acc v ->
12161235
Invariant.(var_invariant v && acc)
@@ -1220,15 +1239,18 @@ struct
12201239
)
12211240
else (
12221241
CPA.fold (fun k v a ->
1223-
Invariant.(a && var_invariant k)
1242+
if var_filter k then
1243+
Invariant.(a && var_invariant k)
1244+
else
1245+
a
12241246
) cpa Invariant.none
12251247
)
12261248
)
12271249
else (
12281250
Lval.Set.fold (fun k a ->
12291251
let i =
12301252
match k with
1231-
| (Var v, offset) when not (InvariantCil.var_is_heap v) ->
1253+
| (Var v, offset) when var_filter v && not (InvariantCil.var_is_heap v) ->
12321254
(try I.key_invariant_lval v ~offset ~lval:k (Arg.find v) with Not_found -> Invariant.none)
12331255
| _ -> Invariant.none
12341256
in
@@ -1243,13 +1265,23 @@ struct
12431265
Invariant.none
12441266

12451267
let query_invariant_global ctx g =
1246-
if GobConfig.get_bool "ana.base.invariant.enabled" && get_bool "exp.earlyglobs" then (
1268+
if GobConfig.get_bool "ana.base.invariant.enabled" && GobConfig.get_bool "ana.base.invariant.global" then (
12471269
(* Currently these global invariants are only sound with earlyglobs enabled for both single- and multi-threaded programs.
1248-
Otherwise, the values of globals in single-threaded mode are not accounted for. *)
1249-
(* TODO: account for single-threaded values without earlyglobs. *)
1270+
Otherwise, the values of globals in single-threaded mode are not accounted for.
1271+
They are also made sound without earlyglobs using the multithreaded mode ghost variable. *)
12501272
match g with
12511273
| `Left g' -> (* priv *)
1252-
Priv.invariant_global (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) g'
1274+
let inv = Priv.invariant_global (Analyses.ask_of_ctx ctx) (priv_getg ctx.global) g' in
1275+
if get_bool "exp.earlyglobs" then
1276+
inv
1277+
else (
1278+
if ctx.ask (GhostVarAvailable Multithreaded) then (
1279+
let var = WitnessGhost.to_varinfo Multithreaded in
1280+
Invariant.(of_exp (UnOp (LNot, Lval (GoblintCil.var var), GoblintCil.intType)) || inv) [@coverage off] (* bisect_ppx cannot handle redefined (||) *)
1281+
)
1282+
else
1283+
Invariant.none
1284+
)
12531285
| `Right _ -> (* thread return *)
12541286
Invariant.none
12551287
)

0 commit comments

Comments
 (0)