Skip to content

Commit 928b706

Browse files
authored
Merge pull request #1915 from goblint/base-reachable-refactor
Refactor reachability computations in base analysis
2 parents 911f9d9 + 206bed3 commit 928b706

File tree

2 files changed

+43
-45
lines changed

2 files changed

+43
-45
lines changed

src/analyses/base.ml

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -564,18 +564,18 @@ struct
564564

565565
(* From a list of values, presumably arguments to a function, simply extract
566566
* the pointer arguments. *)
567-
let get_ptrs (vals: value list): address list =
567+
let get_ptrs (vals: value list): address =
568568
let f acc (x:value) = match x with
569569
| Address adrs when AD.is_top adrs ->
570570
M.info ~category:Unsound "Unknown address given as function argument"; acc
571571
| Address adrs when AD.to_var_may adrs = [] -> acc
572572
| Address adrs ->
573573
let typ = AD.type_of adrs in
574-
if isFunctionType typ then acc else adrs :: acc
574+
if isFunctionType typ then acc else AD.join adrs acc
575575
| Top -> M.info ~category:Unsound "Unknown value type given as function argument"; acc
576576
| _ -> acc
577577
in
578-
List.fold_left f [] vals
578+
List.fold_left f (AD.empty ()) vals
579579

580580
let rec reachable_from_value ask (value: value) (t: typ) (description: string) =
581581
let empty = AD.empty () in
@@ -605,37 +605,35 @@ struct
605605
(* Get the list of addresses accessable immediately from a given address, thus
606606
* all pointers within a structure should be considered, but we don't follow
607607
* pointers. We return a flattend representation, thus simply an address (set). *)
608-
let reachable_from_address ~man st (adr: address): address =
609-
if M.tracing then M.tracei "reachability" "Checking for %a" AD.pretty adr;
610-
let res = reachable_from_value (Analyses.ask_of_man man) (get ~man st adr None) (AD.type_of adr) (AD.show adr) in
608+
let reachable_from_addr ~man st (addr: Addr.t): address =
609+
if M.tracing then M.tracei "reachability" "Checking for %a" Addr.pretty addr;
610+
let res = reachable_from_value (Analyses.ask_of_man man) (get_addr ~man st addr None) (Addr.type_of addr) (Addr.show addr) in
611611
if M.tracing then M.traceu "reachability" "Reachable addresses: %a" AD.pretty res;
612612
res
613613

614614
(* The code for getting the variables reachable from the list of parameters.
615615
* This section is very confusing, because I use the same construct, a set of
616616
* addresses, as both AD elements abstracting individual (ambiguous) addresses
617617
* and the workset of visited addresses. *)
618-
let reachable_vars ~man (st: store) (args: address list): address list =
619-
if M.tracing then M.traceli "reachability" "Checking reachable arguments from [%a]!" (d_list ", " AD.pretty) args;
618+
let reachable_vars ~man (st: store) (args: address): address =
619+
if M.tracing then M.traceli "reachability" "Checking reachable arguments from %a!" AD.pretty args;
620620
let empty = AD.empty () in
621621
(* We begin looking at the parameters: *)
622-
let argset = List.fold_left (AD.join) empty args in
623-
let workset = ref argset in
622+
let workset = ref args in
624623
(* And we keep a set of already visited variables *)
625624
let visited = ref empty in
626625
while not (AD.is_empty !workset) do
627626
visited := AD.union !visited !workset;
628627
(* ok, let's visit all the variables in the workset and collect the new variables *)
629628
let visit_and_collect var (acc: address): address =
630-
let var = AD.singleton var in (* Very bad hack! Pathetic really! *)
631-
AD.union (reachable_from_address ~man st var) acc in
629+
AD.union (reachable_from_addr ~man st var) acc in
632630
let collected = AD.fold visit_and_collect !workset empty in
633631
(* And here we remove the already visited variables *)
634632
workset := AD.diff collected !visited
635633
done;
636634
(* Return the list of elements that have been visited. *)
637635
if M.tracing then M.traceu "reachability" "All reachable vars: %a" AD.pretty !visited;
638-
List.map AD.singleton (AD.elements !visited)
636+
!visited
639637

640638
let reachable_vars ~man st args = Timing.wrap "reachability" (reachable_vars ~man st) args
641639

@@ -1568,12 +1566,11 @@ struct
15681566
| Bot -> Queries.Result.bot q (* TODO: remove *)
15691567
| Address a ->
15701568
let a' = AD.remove Addr.UnknownPtr a in (* run reachable_vars without unknown just to be safe: TODO why? *)
1571-
let addrs = reachable_vars ~man man.local [a'] in
1572-
let addrs' = List.fold_left (AD.join) (AD.empty ()) addrs in
1569+
let addrs = reachable_vars ~man man.local a' in
15731570
if AD.may_be_unknown a then
1574-
AD.add UnknownPtr addrs' (* add unknown back *)
1571+
AD.add UnknownPtr addrs (* add unknown back *)
15751572
else
1576-
addrs'
1573+
addrs
15771574
| Int i ->
15781575
begin match Cilfacade.typeOf e with
15791576
| t when Cil.isPointerType t -> AD.of_int i (* integer used as pointer *)
@@ -1808,14 +1805,17 @@ struct
18081805
let set_var ~(man: _ man) ?invariant ?blob_destructive ?lval_raw ?rval_raw ?t_override (st: store) (x: Cil.varinfo) (lval_type: Cil.typ) (value: value): store =
18091806
set_mval ~man ?invariant ?blob_destructive ?lval_raw ?rval_raw ?t_override st (x, `NoOffset) lval_type value
18101807

1808+
let set_addr ~(man: _ man) ?invariant ?blob_destructive ?lval_raw ?rval_raw ?t_override (st: store) (x: Addr.t) (lval_type: Cil.typ) (value: value): store =
1809+
Option.map_default (fun x -> set_mval ~man ?invariant ?blob_destructive ?lval_raw ?rval_raw ?t_override st x lval_type value) st (Addr.to_mval x)
1810+
18111811
(** [set st addr val] returns a state where [addr] is set to [val]
18121812
* it is always ok to put None for lval_raw and rval_raw, this amounts to not using/maintaining
18131813
* precise information about arrays. *)
18141814
let set ~(man: _ man) ?invariant ?blob_destructive ?lval_raw ?rval_raw ?t_override (st: store) (lval: AD.t) (lval_type: Cil.typ) (value: value) : store =
18151815
let lval_raw = (Option.map (fun x -> Lval x) lval_raw) in
18161816
if M.tracing then M.tracel "set" "lval: %a\nvalue: %a\nstate: %a" AD.pretty lval VD.pretty value CPA.pretty st.cpa;
18171817
let update_one x store =
1818-
Option.map_default (fun x -> set_mval ~man ?invariant ?blob_destructive ?lval_raw ?rval_raw ?t_override store x lval_type value) store (Addr.to_mval x)
1818+
set_addr ~man ?invariant ?blob_destructive ?lval_raw ?rval_raw ?t_override store x lval_type value
18191819
in try
18201820
(* We start from the current state and an empty list of global deltas,
18211821
* and we assign to all the different possible places: *)
@@ -2100,53 +2100,51 @@ struct
21002100
(** From a list of expressions, collect a list of addresses that they might point to, or contain pointers to. *)
21012101
let collect_funargs ~man ?(warn=false) (st:store) (exps: exp list) =
21022102
let ask = Analyses.ask_of_man man in
2103-
let do_exp e =
2103+
let do_exp acc e =
21042104
let immediately_reachable = reachable_from_value ask (eval_rv ~man st e) (Cilfacade.typeOf e) (CilType.Exp.show e) in
2105-
reachable_vars ~man st [immediately_reachable]
2105+
AD.join acc (reachable_vars ~man st immediately_reachable)
21062106
in
2107-
List.concat_map do_exp exps
2107+
List.fold_left do_exp (AD.empty ()) exps
21082108

21092109
let collect_invalidate ~deep ~man ?(warn=false) (st:store) (exps: exp list) =
21102110
if deep then
21112111
collect_funargs ~man ~warn st exps
21122112
else (
2113-
let mpt e = match eval_rv_address ~man st e with
2114-
| Address a -> AD.remove NullPtr a
2115-
| _ -> AD.empty ()
2113+
let mpt acc e = match eval_rv_address ~man st e with
2114+
| Address a -> AD.join acc (AD.remove NullPtr a)
2115+
| _ -> acc
21162116
in
2117-
List.map mpt exps
2117+
List.fold_left mpt (AD.empty ()) exps
21182118
)
21192119

21202120
let invalidate ~(must: bool) ?(deep=true) ~man (st:store) (exps: exp list): store =
21212121
if M.tracing && exps <> [] then M.tracel "invalidate" "Will invalidate expressions [%a]" (d_list ", " d_plainexp) exps;
21222122
if exps <> [] then M.info ~category:Imprecise "Invalidating expressions: %a" (d_list ", " d_exp) exps;
21232123
(* To invalidate a single address, we create a pair with its corresponding
21242124
* top value. *)
2125-
let invalidate_address st a =
2126-
let t = try AD.type_of a with Not_found -> voidType in (* TODO: why is this called with empty a to begin with? *)
2127-
let v = get ~man st a None in (* None here is ok, just causes us to be a bit less precise *)
2125+
let invalidate_addr (a: Addr.t) =
2126+
let t = Addr.type_of a in
2127+
let v = get_addr ~man st a None in (* None here is ok, just causes us to be a bit less precise *)
21282128
let nv = VD.invalidate_value (Queries.to_value_domain_ask (Analyses.ask_of_man man)) t v in
21292129
(a, t, nv)
21302130
in
2131-
(* We define the function that invalidates all the values that an address
2132-
* expression e may point to *)
2133-
let invalidate_exp exps =
2131+
let invalids =
21342132
let args = collect_invalidate ~deep ~man ~warn:true st exps in
2135-
List.map (invalidate_address st) args
2133+
let args = AD.elements args in (* split all address sets up because each address of different type (and with different current value) should get a different invalidated value *)
2134+
List.map invalidate_addr args
21362135
in
2137-
let invalids = invalidate_exp exps in
21382136
let is_fav_addr x =
2139-
List.exists BaseUtil.is_excluded_from_invalidation (AD.to_var_may x)
2137+
GobOption.exists BaseUtil.is_excluded_from_invalidation (Addr.to_var_may x)
21402138
in
21412139
let invalids' = List.filter (fun (x,_,_) -> not (is_fav_addr x)) invalids in
21422140
if M.tracing && exps <> [] then (
21432141
let addrs = List.map (Tuple3.first) invalids' in
21442142
let vs = List.map (Tuple3.third) invalids' in
2145-
M.tracel "invalidate" "Setting addresses [%a] to values [%a]" (d_list ", " AD.pretty) addrs (d_list ", " VD.pretty) vs
2143+
M.tracel "invalidate" "Setting addresses [%a] to values [%a]" (d_list ", " Addr.pretty) addrs (d_list ", " VD.pretty) vs
21462144
);
21472145
(* copied from set_many *)
2148-
let f (acc: store) ((lval:AD.t),(typ:Cil.typ),(value:value)): store =
2149-
let acc' = set ~man acc lval typ value in
2146+
let f (acc: store) ((lval:Addr.t),(typ:Cil.typ),(value:value)): store =
2147+
let acc' = set_addr ~man acc lval typ value in
21502148
if must then
21512149
acc'
21522150
else
@@ -2178,7 +2176,7 @@ struct
21782176
add_to_array_map fundec pa;
21792177
let new_cpa = CPA.add_list pa st'.cpa in
21802178
(* List of reachable variables *)
2181-
let reachable = List.concat_map AD.to_var_may (reachable_vars ~man st (get_ptrs vals)) in
2179+
let reachable = AD.to_var_may (reachable_vars ~man st (get_ptrs vals)) in
21822180
let reachable = List.filter (fun v -> CPA.mem v st.cpa) reachable in
21832181
let new_cpa = CPA.add_list_fun reachable (fun v -> CPA.find v st.cpa) new_cpa in
21842182

@@ -2238,8 +2236,8 @@ struct
22382236
let deep_args = LibraryDesc.Accesses.find desc.accs { kind = Spawn; deep = true } args in
22392237
let shallow_flist = collect_invalidate ~deep:false ~man man.local shallow_args in
22402238
let deep_flist = collect_invalidate ~deep:true ~man man.local deep_args in
2241-
let flist = shallow_flist @ deep_flist in
2242-
let addrs = List.concat_map AD.to_var_may flist in
2239+
let flist = AD.join shallow_flist deep_flist in
2240+
let addrs = AD.to_var_may flist in
22432241
if addrs <> [] then M.debug ~category:Analyzer "Spawning non-unique functions from unknown function: %a" (d_list ", " CilType.Varinfo.pretty) addrs;
22442242
List.filter_map (create_thread ~multiple:true None None) addrs
22452243

tests/regression/02-base/87-casts-dep-on-param.t

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
$ goblint --set ana.activated "['base', 'mallocWrapper']" --set ana.ctx_insens[+] 'base' --set ana.ctx_insens[+] 'mallocWrapper' --set ana.base.privatization none --enable warn.debug 87-casts-dep-on-param.c 2>&1 | sed -r 's/sid:[0-9]+/sid:$SID/' | tee default-output.txt
22
[Warning] Without thread escape analysis, every local variable whose address is taken is considered escaped, i.e., global!
3-
[Info][Unsound] Unknown address in {&i} has escaped. (87-casts-dep-on-param.c:11:3-11:11)
4-
[Info][Unsound] Unknown value in {?} could be an escaped pointer address! (87-casts-dep-on-param.c:11:3-11:11)
3+
[Info][Unsound] Unknown address in i has escaped. (87-casts-dep-on-param.c:11:3-11:11)
4+
[Info][Unsound] Unknown value in ? could be an escaped pointer address! (87-casts-dep-on-param.c:11:3-11:11)
55
[Debug][Analyzer] Base EvalInt i query answering bot instead of {?, NULL, &(alloc@sid:$SID)} (87-casts-dep-on-param.c:13:7-13:15)
66
[Debug][Analyzer] Base EvalInt i query answering bot instead of {?, NULL, &(alloc@sid:$SID)} (87-casts-dep-on-param.c:14:3-14:11)
7-
[Info][Unsound] Unknown address in {&p} has escaped. (87-casts-dep-on-param.c:17:7-17:19)
8-
[Info][Unsound] Unknown address in {&i} has escaped. (87-casts-dep-on-param.c:17:7-17:19)
9-
[Info][Unsound] Unknown value in {?} could be an escaped pointer address! (87-casts-dep-on-param.c:17:7-17:19)
7+
[Info][Unsound] Unknown address in p has escaped. (87-casts-dep-on-param.c:17:7-17:19)
8+
[Info][Unsound] Unknown address in i has escaped. (87-casts-dep-on-param.c:17:7-17:19)
9+
[Info][Unsound] Unknown value in ? could be an escaped pointer address! (87-casts-dep-on-param.c:17:7-17:19)
1010
[Info][Deadcode] Logical lines of code (LLoC) summary:
1111
live: 15
1212
dead: 0

0 commit comments

Comments
 (0)