Skip to content

Commit 8537fcf

Browse files
authored
Merge pull request #833 from goblint/yaml-witness-global-local
Privatized location invariants in YAML witnesses
2 parents 3be0822 + 7862e4c commit 8537fcf

File tree

9 files changed

+221
-64
lines changed

9 files changed

+221
-64
lines changed

src/analyses/apron/apronAnalysis.apron.ml

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,28 @@ struct
9191
in
9292
(apr', e', v_ins)
9393

94+
let read_globals_to_locals_inv (ask: Queries.ask) getg st vs =
95+
let v_ins_inv = VH.create (List.length vs) in
96+
List.iter (fun v ->
97+
let v_in = Goblintutil.create_var @@ makeVarinfo false (v.vname ^ "#in") v.vtype in (* temporary local g#in for global g *)
98+
VH.replace v_ins_inv v_in v;
99+
) vs;
100+
let apr = AD.add_vars st.apr (List.map V.local (VH.keys v_ins_inv |> List.of_enum)) in (* add temporary g#in-s *)
101+
let apr' = VH.fold (fun v_in v apr ->
102+
read_global ask getg {st with apr = apr} v v_in (* g#in = g; *)
103+
) v_ins_inv apr
104+
in
105+
let visitor_inv = object
106+
inherit nopCilVisitor
107+
method! vvrbl (v: varinfo) =
108+
match VH.find_option v_ins_inv v with
109+
| Some v' -> ChangeTo v'
110+
| None -> SkipChildren
111+
end
112+
in
113+
let e_inv e = visitCilExpr visitor_inv e in
114+
(apr', e_inv, v_ins_inv)
115+
94116
let read_from_globals_wrapper ask getg st e f =
95117
let (apr', e', _) = read_globals_to_locals ask getg st e in
96118
f apr' e' (* no need to remove g#in-s *)
@@ -227,7 +249,7 @@ struct
227249
(* Also, a local *)
228250
let vname = Var.to_string var in
229251
let locals = fundec.sformals @ fundec.slocals in
230-
match List.find_opt (fun v -> V.local_name v = vname) locals with
252+
match List.find_opt (fun v -> VM.var_name (Local v) = vname) locals with (* TODO: optimize *)
231253
| None -> true
232254
| Some v -> any_local_reachable
233255

@@ -257,8 +279,8 @@ struct
257279
let any_local_reachable = any_local_reachable fundec reachable_from_args in
258280
AD.remove_filter_with new_apr (fun var ->
259281
match V.find_metadata var with
260-
| Some Local when not (pass_to_callee fundec any_local_reachable var) -> true (* remove caller locals provided they are unreachable *)
261-
| Some Arg when not (List.mem_cmp Var.compare var arg_vars) -> true (* remove caller args, but keep just added args *)
282+
| Some (Local _) when not (pass_to_callee fundec any_local_reachable var) -> true (* remove caller locals provided they are unreachable *)
283+
| Some (Arg _) when not (List.mem_cmp Var.compare var arg_vars) -> true (* remove caller args, but keep just added args *)
262284
| _ -> false (* keep everything else (just added args, globals, global privs) *)
263285
);
264286
if M.tracing then M.tracel "combine" "apron enter newd: %a\n" AD.pretty new_apr;
@@ -340,8 +362,8 @@ struct
340362
AD.remove_vars_with new_fun_apr arg_vars; (* fine to remove arg vars that also exist in caller because unify from new_apr adds them back with proper constraints *)
341363
let new_apr = AD.keep_filter st.apr (fun var ->
342364
match V.find_metadata var with
343-
| Some Local when not (pass_to_callee fundec any_local_reachable var) -> true (* keep caller locals, provided they were not passed to the function *)
344-
| Some Arg -> true (* keep caller args *)
365+
| Some (Local _) when not (pass_to_callee fundec any_local_reachable var) -> true (* keep caller locals, provided they were not passed to the function *)
366+
| Some (Arg _) -> true (* keep caller args *)
345367
| _ -> false (* remove everything else (globals, global privs, reachable things from the caller) *)
346368
)
347369
in
@@ -392,9 +414,8 @@ struct
392414
match reachables ask es with
393415
| None ->
394416
(* top reachable, so try to invalidate everything *)
395-
let fd = Node.find_fundec ctx.node in
396417
AD.vars st.apr
397-
|> List.filter_map (V.to_cil_varinfo fd)
418+
|> List.filter_map V.to_cil_varinfo
398419
|> List.map Cil.var
399420
| Some rs ->
400421
Queries.LS.elements rs
@@ -480,25 +501,50 @@ struct
480501
let query_invariant ctx context =
481502
let keep_local = GobConfig.get_bool "ana.apron.invariant.local" in
482503
let keep_global = GobConfig.get_bool "ana.apron.invariant.global" in
483-
484-
let apr = ctx.local.apr in
485-
(* filter variables *)
486-
let var_filter v = match V.find_metadata v with
487-
| Some (Global _) -> keep_global
488-
| Some Local -> keep_local
489-
| _ -> false
490-
in
491-
let apr = AD.keep_filter apr var_filter in
492-
493504
let one_var = GobConfig.get_bool "ana.apron.invariant.one-var" in
505+
506+
let ask = Analyses.ask_of_ctx ctx in
494507
let scope = Node.find_fundec ctx.node in
495508

509+
let (apr, e_inv) =
510+
if ThreadFlag.is_multi ask then (
511+
let priv_vars =
512+
if keep_global then
513+
Priv.invariant_vars ask ctx.global ctx.local
514+
else
515+
[] (* avoid pointless queries *)
516+
in
517+
let (apr, e_inv, v_ins_inv) = read_globals_to_locals_inv ask ctx.global ctx.local priv_vars in
518+
(* filter variables *)
519+
let var_filter v = match V.find_metadata v with
520+
| Some (Local v) ->
521+
if VH.mem v_ins_inv v then
522+
keep_global
523+
else
524+
keep_local
525+
| _ -> false
526+
in
527+
let apr = AD.keep_filter apr var_filter in
528+
(apr, e_inv)
529+
)
530+
else (
531+
(* filter variables *)
532+
let var_filter v = match V.find_metadata v with
533+
| Some (Global _) -> keep_global
534+
| Some (Local _) -> keep_local
535+
| _ -> false
536+
in
537+
let apr = AD.keep_filter ctx.local.apr var_filter in
538+
(apr, Fun.id)
539+
)
540+
in
496541
AD.invariant ~scope apr
497542
|> List.enum
498543
|> Enum.filter_map (fun (lincons1: Lincons1.t) ->
499544
(* filter one-vars *)
500545
if one_var || Apron.Linexpr0.get_size lincons1.lincons0.linexpr0 >= 2 then
501-
CilOfApron.cil_exp_of_lincons1 scope lincons1
546+
CilOfApron.cil_exp_of_lincons1 lincons1
547+
|> Option.map e_inv
502548
|> Option.filter (fun exp -> not (InvariantCil.exp_contains_tmp exp) && InvariantCil.exp_is_in_scope scope exp)
503549
else
504550
None
@@ -583,7 +629,7 @@ struct
583629
(* filter variables *)
584630
let var_filter v = match V.find_metadata v with
585631
| Some (Global _) -> keep_global
586-
| Some Local -> keep_local
632+
| Some (Local _) -> keep_local
587633
| _ -> false
588634
in
589635
let st = keep_filter ctx.local.apr var_filter in

src/analyses/apron/apronPriv.apron.ml

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ module type S =
4141
val thread_return: Q.ask -> (V.t -> G.t) -> (V.t -> G.t -> unit) -> ThreadIdDomain.Thread.t -> apron_components_t -> apron_components_t
4242
val iter_sys_vars: (V.t -> G.t) -> VarQuery.t -> V.t VarQuery.f -> unit (** [Queries.IterSysVars] for apron. *)
4343

44+
val invariant_vars: Q.ask -> (V.t -> G.t) -> apron_components_t -> varinfo list
45+
(** Returns global variables which are privatized. *)
46+
4447
val init: unit -> unit
4548
val finalize: unit -> unit
4649
end
@@ -80,12 +83,7 @@ struct
8083
let apr = st.apr in
8184
let esc_vars = List.filter (fun var -> match AV.find_metadata var with
8285
| Some (Global _) -> false
83-
| Some Local ->
84-
(let fundec = Node.find_fundec node in
85-
let r = AV.to_cil_varinfo fundec var in
86-
match r with
87-
| Some r -> EscapeDomain.EscapedVars.mem r escaped
88-
| _ -> false)
86+
| Some (Local r) -> EscapeDomain.EscapedVars.mem r escaped
8987
| _ -> false
9088
) (AD.vars apr)
9189
in
@@ -127,6 +125,7 @@ struct
127125
{apr = AD.bot (); priv = startstate ()}
128126

129127
let iter_sys_vars getg vq vf = ()
128+
let invariant_vars ask getg st = []
130129

131130
let init () = ()
132131
let finalize () = ()
@@ -404,6 +403,7 @@ struct
404403
{apr = getg (); priv = startstate ()}
405404

406405
let iter_sys_vars getg vq vf = () (* TODO: or report singleton global for any Global query? *)
406+
let invariant_vars ask getg st = protected_vars ask (* TODO: is this right? *)
407407

408408
let finalize () = ()
409409
end
@@ -428,11 +428,25 @@ struct
428428
AD.keep_filter oct protected
429429
end
430430

431+
module PerMutexMeetPrivBase (AD: ApronDomain.S3) =
432+
struct
433+
let invariant_vars ask getg (st: (AD.t, _) ApronDomain.aproncomponents_t) =
434+
(* Mutex-meet local states contain precisely the protected global variables,
435+
so we can do fewer queries than {!protected_vars}. *)
436+
AD.vars st.apr
437+
|> List.filter_map (fun var ->
438+
match ApronDomain.V.find_metadata var with
439+
| Some (Global g) -> Some g
440+
| _ -> None
441+
)
442+
end
443+
431444
(** Per-mutex meet. *)
432445
module PerMutexMeetPriv : S = functor (AD: ApronDomain.S3) ->
433446
struct
434447
open CommonPerMutex(AD)
435448
include MutexGlobals
449+
include PerMutexMeetPrivBase (AD)
436450

437451
module D = Lattice.Unit
438452
module G = AD
@@ -832,6 +846,7 @@ module PerMutexMeetPrivTID (Cluster: ClusterArg): S = functor (AD: ApronDomain.
832846
struct
833847
open CommonPerMutex(AD)
834848
include MutexGlobals
849+
include PerMutexMeetPrivBase (AD)
835850

836851
module NC = Cluster(AD)
837852
module Cluster = NC

src/analyses/base.ml

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,34 +1106,51 @@ struct
11061106

11071107
let query_invariant ctx context =
11081108
let cpa = ctx.local.BaseDomain.cpa in
1109+
let ask = Analyses.ask_of_ctx ctx in
11091110

11101111
let module Arg =
11111112
struct
11121113
let context = context
11131114
let scope = Node.find_fundec ctx.node
1114-
let find v = CPA.find v cpa
1115+
let find v = get_var ask ctx.global ctx.local v
11151116
end
11161117
in
11171118
let module I = ValueDomain.ValueInvariant (Arg) in
11181119

1119-
let cpa_invariant =
1120-
match context.lval with
1121-
| None ->
1122-
CPA.fold (fun k v a ->
1123-
let i =
1124-
if not (InvariantCil.var_is_heap k) then
1125-
I.key_invariant k v
1126-
else
1127-
Invariant.none
1128-
in
1129-
Invariant.(a && i)
1130-
) cpa Invariant.none
1131-
| Some (Var k, _) when not (InvariantCil.var_is_heap k) ->
1132-
(try I.key_invariant k (CPA.find k cpa) with Not_found -> Invariant.none)
1133-
| _ -> Invariant.none
1120+
let var_invariant v =
1121+
if not (InvariantCil.var_is_heap v) then
1122+
I.key_invariant v (Arg.find v)
1123+
else
1124+
Invariant.none
11341125
in
11351126

1136-
cpa_invariant
1127+
match context.lval with
1128+
| None ->
1129+
if !GU.earlyglobs || ThreadFlag.is_multi ask then (
1130+
let cpa_invariant =
1131+
CPA.fold (fun k v a ->
1132+
if not (is_global ask k) then
1133+
Invariant.(a && var_invariant k)
1134+
else
1135+
a
1136+
) cpa Invariant.none
1137+
in
1138+
let priv_vars = Priv.invariant_vars ask (priv_getg ctx.global) ctx.local in
1139+
let priv_invariant =
1140+
List.fold_left (fun acc v ->
1141+
Invariant.(var_invariant v && acc)
1142+
) Invariant.none priv_vars
1143+
in
1144+
Invariant.(cpa_invariant && priv_invariant)
1145+
)
1146+
else (
1147+
CPA.fold (fun k v a ->
1148+
Invariant.(a && var_invariant k)
1149+
) cpa Invariant.none
1150+
)
1151+
| Some (Var k, _) ->
1152+
(try var_invariant k with Not_found -> Invariant.none)
1153+
| _ -> Invariant.none
11371154

11381155
let query_invariant ctx context =
11391156
if GobConfig.get_bool "ana.base.invariant.enabled" then

0 commit comments

Comments
 (0)