Skip to content

Commit 5bfca05

Browse files
authored
Merge pull request #830 from goblint/yaml-witness-global
Flow-insensitive YAML witness invariants
2 parents 85dc84e + 77ab055 commit 5bfca05

File tree

9 files changed

+329
-105
lines changed

9 files changed

+329
-105
lines changed

src/analyses/base.ml

Lines changed: 26 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,109 +1106,30 @@ struct
11061106

11071107
let query_invariant ctx context =
11081108
let cpa = ctx.local.BaseDomain.cpa in
1109-
let scope = Node.find_fundec ctx.node in
11101109

1111-
(* VS is used to detect and break cycles in deref_invariant calls *)
1112-
let module VS = Set.Make (Basetype.Variables) in
1113-
1114-
let rec ad_invariant ~vs ~offset c x =
1115-
let c_exp = Cil.(Lval (BatOption.get c.Invariant.lval)) in
1116-
let i_opt = AD.fold (fun addr acc_opt ->
1117-
BatOption.bind acc_opt (fun acc ->
1118-
match addr with
1119-
| Addr.UnknownPtr ->
1120-
None
1121-
| Addr.Addr (vi, offs) when Addr.Offs.is_definite offs ->
1122-
let rec offs_to_offset = function
1123-
| `NoOffset -> NoOffset
1124-
| `Field (f, offs) -> Field (f, offs_to_offset offs)
1125-
| `Index (i, offs) ->
1126-
(* Addr.Offs.is_definite implies Idx.to_int returns Some *)
1127-
let i_definite = BatOption.get (ValueDomain.IndexDomain.to_int i) in
1128-
let i_exp = Cil.(kinteger64 ILongLong (IntOps.BigIntOps.to_int64 i_definite)) in
1129-
Index (i_exp, offs_to_offset offs)
1130-
in
1131-
let offset = offs_to_offset offs in
1132-
1133-
let i =
1134-
if InvariantCil.(not (exp_contains_tmp c_exp) && exp_is_in_scope scope c_exp && not (var_is_tmp vi) && var_is_in_scope scope vi && not (var_is_heap vi)) then
1135-
let addr_exp = AddrOf (Var vi, offset) in (* AddrOf or Lval? *)
1136-
Invariant.of_exp Cil.(BinOp (Eq, c_exp, addr_exp, intType))
1137-
else
1138-
Invariant.none
1139-
in
1140-
let i_deref = deref_invariant ~vs c vi offset (Mem c_exp, NoOffset) in
1141-
1142-
Some (Invariant.(acc || (i && i_deref)))
1143-
| Addr.NullPtr ->
1144-
let i =
1145-
let addr_exp = integer 0 in
1146-
if InvariantCil.(not (exp_contains_tmp c_exp) && exp_is_in_scope scope c_exp) then
1147-
Invariant.of_exp Cil.(BinOp (Eq, c_exp, addr_exp, intType))
1148-
else
1149-
Invariant.none
1150-
in
1151-
Some (Invariant.(acc || i))
1152-
(* TODO: handle Addr.StrPtr? *)
1153-
| _ ->
1154-
None
1155-
)
1156-
) x (Some (Invariant.bot ()))
1157-
in
1158-
match i_opt with
1159-
| Some i -> i
1160-
| None -> Invariant.none
1161-
1162-
and blob_invariant ~vs ~offset c (v, _, _) =
1163-
vd_invariant ~vs ~offset c v
1164-
1165-
and vd_invariant ~vs ~offset c = function
1166-
| `Int n ->
1167-
let e = Lval (BatOption.get c.Invariant.lval) in
1168-
if InvariantCil.(not (exp_contains_tmp e) && exp_is_in_scope scope e) then
1169-
ID.invariant e n
1170-
else
1171-
Invariant.none
1172-
| `Float n ->
1173-
let e = Lval (BatOption.get c.Invariant.lval) in
1174-
if InvariantCil.(not (exp_contains_tmp e) && exp_is_in_scope scope e) then
1175-
FD.invariant e n
1176-
else
1177-
Invariant.none
1178-
| `Address n -> ad_invariant ~vs ~offset c n
1179-
| `Blob n -> blob_invariant ~vs ~offset c n
1180-
| `Struct n -> ValueDomain.Structs.invariant ~value_invariant:(vd_invariant ~vs) ~offset c n
1181-
| `Union n -> ValueDomain.Unions.invariant ~value_invariant:(vd_invariant ~vs) ~offset c n
1182-
| _ -> Invariant.none (* TODO *)
1183-
1184-
and deref_invariant ~vs c vi offset lval =
1185-
let v = CPA.find vi cpa in
1186-
key_invariant_lval ~vs c vi offset lval v
1187-
1188-
and key_invariant_lval ~vs c k offset lval v =
1189-
if not (VS.mem k vs) then
1190-
let vs' = VS.add k vs in
1191-
let key_context: Invariant.context = {c with lval=Some lval} in
1192-
vd_invariant ~vs:vs' ~offset key_context v
1193-
else
1194-
Invariant.none
1110+
let module Arg =
1111+
struct
1112+
let context = context
1113+
let scope = Node.find_fundec ctx.node
1114+
let find v = CPA.find v cpa
1115+
end
11951116
in
1117+
let module I = ValueDomain.ValueInvariant (Arg) in
11961118

11971119
let cpa_invariant =
1198-
let key_invariant k v = key_invariant_lval ~vs:VS.empty context k NoOffset (var k) v in
11991120
match context.lval with
12001121
| None ->
12011122
CPA.fold (fun k v a ->
12021123
let i =
12031124
if not (InvariantCil.var_is_heap k) then
1204-
key_invariant k v
1125+
I.key_invariant k v
12051126
else
12061127
Invariant.none
12071128
in
12081129
Invariant.(a && i)
12091130
) cpa Invariant.none
12101131
| Some (Var k, _) when not (InvariantCil.var_is_heap k) ->
1211-
(try key_invariant k (CPA.find k cpa) with Not_found -> Invariant.none)
1132+
(try I.key_invariant k (CPA.find k cpa) with Not_found -> Invariant.none)
12121133
| _ -> Invariant.none
12131134
in
12141135

@@ -1220,6 +1141,20 @@ struct
12201141
else
12211142
Invariant.none
12221143

1144+
let query_invariant_global ctx g =
1145+
if GobConfig.get_bool "ana.base.invariant.enabled" && get_bool "exp.earlyglobs" then (
1146+
(* Currently these global invariants are only sound with earlyglobs enabled for both single- and multi-threaded programs.
1147+
Otherwise, the values of globals in single-threaded mode are not accounted for. *)
1148+
(* TODO: account for single-threaded values without earlyglobs. *)
1149+
match g with
1150+
| `Left g' -> (* priv *)
1151+
Priv.invariant_global (priv_getg ctx.global) g'
1152+
| `Right _ -> (* thread return *)
1153+
Invariant.none
1154+
)
1155+
else
1156+
Invariant.none
1157+
12231158
let query ctx (type a) (q: a Q.t): a Q.result =
12241159
match q with
12251160
| Q.EvalFunvar e ->
@@ -1331,6 +1266,9 @@ struct
13311266
let vf' x = vf (Obj.repr (V.priv x)) in
13321267
Priv.iter_sys_vars (priv_getg ctx.global) vq vf'
13331268
| Q.Invariant context -> query_invariant ctx context
1269+
| Q.InvariantGlobal g ->
1270+
let g: V.t = Obj.obj g in
1271+
query_invariant_global ctx g
13341272
| _ -> Q.Result.top q
13351273

13361274
let update_variable variable typ value cpa =

src/analyses/basePriv.ml

Lines changed: 95 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ sig
4040
val thread_join: ?force:bool -> Q.ask -> (V.t -> G.t) -> Cil.exp -> BaseComponents (D).t -> BaseComponents (D).t
4141
val thread_return: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> ThreadIdDomain.Thread.t -> BaseComponents (D).t -> BaseComponents (D).t
4242

43+
val invariant_global: (V.t -> G.t) -> V.t -> Invariant.t
44+
4345
val init: unit -> unit
4446
val finalize: unit -> unit
4547
end
@@ -124,6 +126,9 @@ struct
124126

125127
let thread_join ?(force=false) ask get e st = st
126128
let thread_return ask get set tid st = st
129+
130+
let invariant_global getg g =
131+
ValueDomain.invariant_global getg g
127132
end
128133

129134
module PerMutexPrivBase =
@@ -156,6 +161,11 @@ struct
156161
| None, Some v -> Some v
157162
| None, None -> None
158163

164+
let read_unprotected_global getg x =
165+
let get_mutex_global_x = get_mutex_global_x_with_mutex_inits getg x in
166+
(* None is VD.top () *)
167+
get_mutex_global_x |? VD.bot ()
168+
159169
let escape ask getg sideg (st: BaseComponents (D).t) escaped =
160170
let escaped_cpa = CPA.filter (fun x _ -> EscapeDomain.EscapedVars.mem x escaped) st.cpa in
161171
sideg V.mutex_inits escaped_cpa;
@@ -193,6 +203,13 @@ struct
193203

194204
let thread_join ?(force=false) ask get e st = st
195205
let thread_return ask get set tid st = st
206+
207+
let invariant_global getg g =
208+
match g with
209+
| `Left _ -> (* mutex *)
210+
Invariant.none
211+
| `Right g' -> (* global *)
212+
ValueDomain.invariant_global (read_unprotected_global getg) g'
196213
end
197214

198215
module PerMutexOplusPriv: S =
@@ -201,8 +218,7 @@ struct
201218

202219
let read_global ask getg (st: BaseComponents (D).t) x =
203220
if is_unprotected ask x then
204-
let get_mutex_global_x = get_mutex_global_x_with_mutex_inits getg x in
205-
get_mutex_global_x |? VD.bot ()
221+
read_unprotected_global getg x
206222
else
207223
CPA.find x st.cpa
208224
(* let read_global ask getg cpa x =
@@ -271,9 +287,8 @@ struct
271287

272288
let read_global ask getg (st: BaseComponents (D).t) x =
273289
if is_unprotected ask x then (
274-
let get_mutex_global_x = get_mutex_global_x_with_mutex_inits getg x in
275290
(* If the global is unprotected, all appropriate information should come via the appropriate globals, local value may be too small due to stale values surviving widening *)
276-
get_mutex_global_x |? VD.bot ()
291+
read_unprotected_global getg x
277292
)
278293
else
279294
CPA.find x st.cpa
@@ -550,6 +565,21 @@ struct
550565
let _,lmust,l = st.priv in
551566
{st with cpa = new_cpa; priv = (W.bot (),lmust,l)}
552567

568+
let read_unprotected_global getg x =
569+
let get_mutex_global_x = merge_all @@ G.mutex @@ getg (V.global x) in
570+
let get_mutex_global_x' = CPA.find x get_mutex_global_x in
571+
let get_mutex_inits = merge_all @@ G.mutex @@ getg V.mutex_inits in
572+
let get_mutex_inits' = CPA.find x get_mutex_inits in
573+
VD.join get_mutex_global_x' get_mutex_inits'
574+
575+
let invariant_global getg g =
576+
match g with
577+
| `Left (`Left _) -> (* mutex *)
578+
Invariant.none
579+
| `Left (`Right g') -> (* global *)
580+
ValueDomain.invariant_global (read_unprotected_global getg) g'
581+
| `Right _ -> (* thread *)
582+
Invariant.none
553583
end
554584

555585

@@ -690,6 +720,13 @@ struct
690720
vf (V.unprotected g);
691721
vf (V.protected g);
692722
| _ -> ()
723+
724+
let invariant_global getg g =
725+
match g with
726+
| `Left g' -> (* unprotected *)
727+
ValueDomain.invariant_global (fun g -> getg (V.unprotected g)) g'
728+
| `Right g -> (* protected *)
729+
Invariant.none
693730
end
694731

695732
module AbstractLockCenteredGBase (WeakRange: Lattice.S) (SyncRange: Lattice.S) =
@@ -725,11 +762,42 @@ struct
725762
end
726763
end
727764

728-
module LockCenteredGBase =
765+
module type WeakRangeS =
766+
sig
767+
include Lattice.S
768+
val fold_weak: (VD.t -> 'a -> 'a) -> t -> 'a -> 'a
769+
(** Fold over all values represented by weak range. *)
770+
end
771+
772+
module AbstractLockCenteredBase (WeakRange: WeakRangeS) (SyncRange: Lattice.S) =
729773
struct
774+
include AbstractLockCenteredGBase (WeakRange) (SyncRange)
775+
include MutexGlobals
776+
777+
let invariant_global getg g =
778+
match g with
779+
| `Left _ -> (* mutex *)
780+
Invariant.none
781+
| `Right g' -> (* global *)
782+
ValueDomain.invariant_global (fun x ->
783+
GWeak.fold (fun s' tm acc ->
784+
WeakRange.fold_weak VD.join tm acc
785+
) (G.weak (getg (V.global x))) (VD.bot ())
786+
) g'
787+
end
788+
789+
module LockCenteredBase =
790+
struct
791+
module VD =
792+
struct
793+
include VD
794+
795+
let fold_weak f v a = f v a
796+
end
797+
730798
(* weak: G -> (2^M -> D) *)
731799
(* sync: M -> (2^M -> (G -> D)) *)
732-
include AbstractLockCenteredGBase (VD) (CPA)
800+
include AbstractLockCenteredBase (VD) (CPA)
733801
end
734802

735803
module MinePrivBase =
@@ -760,11 +828,16 @@ struct
760828
open Locksets
761829

762830
module Thread = ThreadIdDomain.Thread
763-
module ThreadMap = MapDomain.MapBot (Thread) (VD)
831+
module ThreadMap =
832+
struct
833+
include MapDomain.MapBot (Thread) (VD)
834+
835+
let fold_weak f m a = fold (fun _ v a -> f v a) m a
836+
end
764837

765838
(* weak: G -> (2^M -> (T -> D)) *)
766839
(* sync: M -> (2^M -> (G -> D)) *)
767-
include AbstractLockCenteredGBase (ThreadMap) (CPA)
840+
include AbstractLockCenteredBase (ThreadMap) (CPA)
768841

769842
let global_init_thread = RichVarinfo.single ~name:"global_init"
770843
let current_thread (ask: Q.ask): Thread.t =
@@ -831,7 +904,7 @@ end
831904
module MineNoThreadPriv: S =
832905
struct
833906
include MineNaivePrivBase
834-
include LockCenteredGBase
907+
include LockCenteredBase
835908
open Locksets
836909

837910
let read_global ask getg (st: BaseComponents (D).t) x =
@@ -893,7 +966,7 @@ end
893966
module MineWPriv (Param: MineWPrivParam): S =
894967
struct
895968
include MinePrivBase
896-
include LockCenteredGBase
969+
include LockCenteredBase
897970
open Locksets
898971

899972
module W =
@@ -996,7 +1069,7 @@ end
9961069
module LockCenteredPriv: S =
9971070
struct
9981071
include MinePrivBase
999-
include LockCenteredGBase
1072+
include LockCenteredBase
10001073
open Locksets
10011074

10021075
open LockCenteredD
@@ -1124,23 +1197,28 @@ struct
11241197
let threadenter = startstate_threadenter startstate
11251198
end
11261199

1127-
module WriteCenteredGBase =
1200+
module WriteCenteredBase =
11281201
struct
11291202
open Locksets
11301203

1131-
module GWeakW = MapDomain.MapBot (Lockset) (VD)
1204+
module GWeakW =
1205+
struct
1206+
include MapDomain.MapBot (Lockset) (VD)
1207+
1208+
let fold_weak f m a = fold (fun _ v a -> f v a) m a
1209+
end
11321210
module GSyncW = MapDomain.MapBot (Lockset) (CPA)
11331211

11341212
(* weak: G -> (S:2^M -> (W:2^M -> D)) *)
11351213
(* sync: M -> (S:2^M -> (W:2^M -> (G -> D))) *)
1136-
include AbstractLockCenteredGBase (GWeakW) (GSyncW)
1214+
include AbstractLockCenteredBase (GWeakW) (GSyncW)
11371215
end
11381216

11391217
(** Write-Centered Reading. *)
11401218
module WriteCenteredPriv: S =
11411219
struct
11421220
include MinePrivBase
1143-
include WriteCenteredGBase
1221+
include WriteCenteredBase
11441222
open Locksets
11451223

11461224
open WriteCenteredD
@@ -1280,7 +1358,7 @@ end
12801358
module WriteAndLockCenteredPriv: S =
12811359
struct
12821360
include MinePrivBase
1283-
include WriteCenteredGBase
1361+
include WriteCenteredBase
12841362
open Locksets
12851363

12861364
open LockCenteredD
@@ -1462,6 +1540,7 @@ struct
14621540
let enter_multithreaded ask getg sideg st = time "enter_multithreaded" (Priv.enter_multithreaded ask getg sideg) st
14631541
let threadenter ask st = time "threadenter" (Priv.threadenter ask) st
14641542
let iter_sys_vars getg vq vf = time "iter_sys_vars" (Priv.iter_sys_vars getg vq) vf
1543+
let invariant_global getg v = time "invariant_global" (Priv.invariant_global getg) v
14651544

14661545
let thread_join ?(force=false) ask get e st = time "thread_join" (Priv.thread_join ~force ask get e) st
14671546
let thread_return ask get set tid st = time "thread_return" (Priv.thread_return ask get set tid) st

0 commit comments

Comments
 (0)