Skip to content

Commit 88e3983

Browse files
committed
Modular analysis: Collect graphs
1 parent 52ee10b commit 88e3983

File tree

6 files changed

+127
-30
lines changed

6 files changed

+127
-30
lines changed

src/analyses/base.ml

+53-28
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ struct
514514
in
515515
List.fold_right f vals []
516516

517-
let rec reachable_from_value (ask: Q.ask) (gs:glob_fun) st (value: value) (t: typ) (description: string) =
517+
let rec reachable_from_value (ask: ValueDomainQueries.t) (gs:glob_fun) st (value: value) (t: typ) (description: string) =
518518
let empty = AD.empty () in
519519
if M.tracing then M.trace "reachability" "Checking value %a\n" VD.pretty value;
520520
match value with
@@ -530,7 +530,7 @@ struct
530530
ValueDomain.Unions.fold (fun k v acc -> AD.join (reachable_from_value ask gs st v t description) acc) u empty
531531
(* For arrays, we ask to read from an unknown index, this will cause it
532532
* join all its values. *)
533-
| Array a -> reachable_from_value ask gs st (ValueDomain.CArrays.get (Queries.to_value_domain_ask ask) a (None, ValueDomain.ArrIdxDomain.top ())) t description
533+
| Array a -> reachable_from_value ask gs st (ValueDomain.CArrays.get ask a (None, ValueDomain.ArrIdxDomain.top ())) t description
534534
| Blob (e,_,_) -> reachable_from_value ask gs st e t description
535535
| Struct s -> ValueDomain.Structs.fold (fun k v acc -> AD.join (reachable_from_value ask gs st v t description) acc) s empty
536536
| Int _ -> empty
@@ -545,7 +545,8 @@ struct
545545
* pointers. We return a flattend representation, thus simply an address (set). *)
546546
let reachable_from_address (ask: Q.ask) (gs:glob_fun) st (adr: address): address =
547547
if M.tracing then M.tracei "reachability" "Checking for %a\n" AD.pretty adr;
548-
let res = reachable_from_value ask gs st (get ask gs st adr None) (AD.type_of adr) (AD.show adr) in
548+
let value_domain_ask = Queries.to_value_domain_ask ask in
549+
let res = reachable_from_value value_domain_ask gs st (get ask gs st adr None) (AD.type_of adr) (AD.show adr) in
549550
if M.tracing then M.traceu "reachability" "Reachable addresses: %a\n" AD.pretty res;
550551
res
551552

@@ -1230,33 +1231,54 @@ struct
12301231
Invariant.none
12311232

12321233

1233-
module ADGraph = MapDomain.MapBot_LiftTop (Addr) (AD)
1234+
let is_prefix_of m1 m2 =
1235+
let m1 = Addr.to_mval m1 in
1236+
let m2 = Addr.to_mval m2 in
1237+
match m1, m2 with
1238+
| Some m1, Some m2 -> Option.is_some (Addr.Mval.prefix m1 m2)
1239+
| _ -> false
12341240

12351241
(* Given a set of addresses, collect graph
12361242
that contains all paths with which these addresses are reachable with a depth-first search *)
1237-
let collect_graph ctx (start: AD.t) (goal: AD.t) =
1238-
let rec dfs node (visited, graph) : bool * ADGraph.t =
1239-
let node = node in
1240-
let visited = AD.join visited (AD.singleton node) in
1241-
let reachable_from_node = reachable_from_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local (AD.singleton node) in
1242-
let dfs_add_edge n found =
1243-
let goal_reached = AD.subset (AD.singleton n) goal in
1244-
let found, graph = dfs n (visited, graph) in
1245-
let found = found || goal_reached in
1246-
if found then
1247-
let graph = ADGraph.add node (AD.singleton n) graph in
1248-
(true, graph)
1249-
else
1250-
(found, graph)
1243+
let collect_graph (local: CPA.t) (start: AD.t) (goal: AD.t) =
1244+
if AD.is_empty start || AD.is_empty goal then
1245+
ValueDomain.ADGraph.bot ()
1246+
else
1247+
let ask: Queries.ask = { Queries.f = (fun (type a) (q: a Queries.t) -> Queries.Result.top q)} in
1248+
let state = D.top () in
1249+
let local = {state with cpa = local} in
1250+
let rec dfs node (visited, graph) : bool * ValueDomain.ADGraph.t =
1251+
let node = node in
1252+
let visited = AD.join visited (AD.singleton node) in
1253+
let glob_fun = fun _ -> failwith "Should not lookup globals." in
1254+
let reachable_from_node = reachable_from_address ask glob_fun local (AD.singleton node) in
1255+
let dfs_add_edge n found =
1256+
let goal_reached = AD.exists (fun g -> is_prefix_of n g) goal in
1257+
let already_visited = AD.subset (AD.singleton n) visited in
1258+
if already_visited then
1259+
false, graph
1260+
else if goal_reached then
1261+
let graph = ValueDomain.ADGraph.add node (AD.singleton n) graph in
1262+
(true, graph)
1263+
else begin
1264+
let found, graph = dfs n (visited, graph) in
1265+
if found then
1266+
let graph = ValueDomain.ADGraph.add node (AD.singleton n) graph in
1267+
(true, graph)
1268+
else
1269+
(found, graph)
1270+
end
1271+
in
1272+
AD.fold dfs_add_edge reachable_from_node (false, ValueDomain.ADGraph.bot ())
12511273
in
1252-
AD.fold dfs_add_edge reachable_from_node (false, ADGraph.bot ())
1253-
in
1254-
let dfs_combine node graph =
1255-
let _, graph = dfs node (AD.empty (), graph) in
1256-
graph
1257-
in
1258-
let empty_graph = ADGraph.empty () in
1259-
AD.fold dfs_combine start empty_graph
1274+
let dfs_combine node graph =
1275+
let _, graph = dfs node (AD.empty (), graph) in
1276+
graph
1277+
in
1278+
let empty_graph = ValueDomain.ADGraph.empty () in
1279+
let result = AD.fold dfs_combine start empty_graph in
1280+
if M.tracing then M.tracel "collect_graph" "From %a to %a, in graph %a\n" AD.pretty start AD.pretty goal ValueDomain.ADGraph.pretty result;
1281+
result
12601282

12611283
let query ctx (type a) (q: a Q.t): a Q.result =
12621284
match q with
@@ -1442,6 +1464,8 @@ struct
14421464
query_invariant_global ctx g
14431465
| Q.BaseCPA ->
14441466
ctx.local.cpa
1467+
| Q.CollectGraph (cpa,start, goal) ->
1468+
collect_graph cpa start goal
14451469
| _ -> Q.Result.top q
14461470

14471471
let update_variable variable typ value cpa =
@@ -1916,9 +1940,10 @@ struct
19161940
**************************************************************************)
19171941

19181942
(** From a list of expressions, collect a list of addresses that they might point to, or contain pointers to. *)
1919-
let collect_funargs ask ?(warn=false) (gs:glob_fun) (st:store) (exps: exp list) =
1943+
let collect_funargs (ask: Q.ask) ?(warn=false) (gs:glob_fun) (st:store) (exps: exp list) =
1944+
let value_domain_ask = Queries.to_value_domain_ask ask in
19201945
let do_exp e =
1921-
let immediately_reachable = reachable_from_value ask gs st (eval_rv ask gs st e) (Cilfacade.typeOf e) (CilType.Exp.show e) in
1946+
let immediately_reachable = reachable_from_value value_domain_ask gs st (eval_rv ask gs st e) (Cilfacade.typeOf e) (CilType.Exp.show e) in
19221947
reachable_vars ask [immediately_reachable] gs st
19231948
in
19241949
List.concat_map do_exp exps

src/analyses/startStateAnalysis.ml

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@ struct
1616
let ask = Analyses.ask_of_ctx ctx in
1717
let cpa = ask.f Queries.BaseCPA in
1818
ctx.sideg f cpa;
19+
if Messages.tracing then Messages.tracel "startstate" "For %a, side-effecting state %a \n" CilType.Fundec.pretty f G.pretty cpa;
1920
()
2021

2122
let name () = "startstate"
2223

23-
let is_modular () = true
24+
let modular_support () = Modular
2425

2526
let query ctx (type a) (q: a Queries.t): a Queries.result =
2627
match q with
2728
| Queries.StartCPA f ->
2829
let r : G.t = ctx.global f in
30+
if Messages.tracing then Messages.tracel "startstate" "For %a, startstate analysis answering: %a\n" CilType.Fundec.pretty f G.pretty r;
2931
r
3032
| _ -> Queries.Result.top q
3133

src/analyses/written.ml

+20-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ struct
1515
(* Value of entries not in mapping: bot, LiftTop such that there is a `Top map. *)
1616
module D = WrittenDomain.Written
1717
module C = Lattice.Unit
18+
module V = struct
19+
include CilType.Fundec
20+
let is_write_only _ = false
21+
end
22+
module G = ValueDomain.ADGraph
1823

1924
let context _ _ = C.bot ()
2025

@@ -38,7 +43,20 @@ struct
3843
let body ctx (f:fundec) : D.t =
3944
ctx.local
4045

46+
let join_address_list (a : AD.t list) =
47+
List.fold AD.join (AD.bot ()) a
48+
4149
let return ctx (exp:exp option) (f:fundec) : D.t =
50+
let ask = Analyses.ask_of_ctx ctx in
51+
let start_state = ask.f (Queries.StartCPA f) in
52+
let written = List.map Tuple2.first (D.bindings ctx.local) in
53+
let written = join_address_list written in
54+
let params = f.sformals in
55+
let params = List.map (AD.of_var ~is_modular:true) params in
56+
let params = join_address_list params in
57+
let graph = ask.f (CollectGraph (start_state, params, written)) in (* TODO: Adapt start and end sets *)
58+
M.tracel "startstate" "Looking for path from %a to %a in state %a\n" AD.pretty params AD.pretty written BaseDomain.CPA.pretty start_state;
59+
ctx.sideg f graph;
4260
ctx.local
4361

4462
let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list =
@@ -98,4 +116,5 @@ struct
98116
end
99117

100118
let _ =
101-
MCP.register_analysis (module Spec : MCPSpec)
119+
let dep = ["startstate"] in
120+
MCP.register_analysis ~dep (module Spec : MCPSpec)

src/cdomain/value/cdomains/valueDomain.ml

+2
Original file line numberDiff line numberDiff line change
@@ -1477,3 +1477,5 @@ let invariant_global find g =
14771477
in
14781478
let module I = ValueInvariant (Arg) in
14791479
I.key_invariant g (find g)
1480+
1481+
module ADGraph = MapDomain.MapBot_LiftTop (Addr) (AD)

src/domains/queries.ml

+16
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ type _ t =
135135
| IsModular: MustBool.t t
136136
| BaseCPA: BaseDomain.CPA.t t
137137
| StartCPA: CilType.Fundec.t -> BaseDomain.CPA.t t
138+
| CollectGraph: BaseDomain.CPA.t * AD.t * AD.t -> ValueDomain.ADGraph.t t
138139

139140
type 'a result = 'a
140141

@@ -212,6 +213,7 @@ struct
212213
| TmpSpecial _ -> (module ML)
213214
| BaseCPA -> (module BaseDomain.CPA)
214215
| StartCPA _ -> (module BaseDomain.CPA)
216+
| CollectGraph _ -> (module ValueDomain.ADGraph)
215217

216218
(** Get bottom result for query. *)
217219
let bot (type a) (q: a t): a result =
@@ -288,6 +290,7 @@ struct
288290
| TmpSpecial _ -> ML.top ()
289291
| BaseCPA -> BaseDomain.CPA.top ()
290292
| StartCPA _ -> BaseDomain.CPA.top ()
293+
| CollectGraph _ -> ValueDomain.ADGraph.top ()
291294
end
292295

293296
(* The type any_query can't be directly defined in Any as t,
@@ -361,6 +364,7 @@ struct
361364
| Any Read -> 63
362365
| Any BaseCPA -> 64
363366
| Any (StartCPA _) -> 65
367+
| Any (CollectGraph _) -> 66
364368

365369
let rec compare a b =
366370
let r = Stdlib.compare (order a) (order b) in
@@ -417,6 +421,17 @@ struct
417421
| Any (MustBeSingleThreaded {since_start=s1;}), Any (MustBeSingleThreaded {since_start=s2;}) -> Stdlib.compare s1 s2
418422
| Any (TmpSpecial lv1), Any (TmpSpecial lv2) -> Mval.Exp.compare lv1 lv2
419423
| Any (StartCPA f1), Any (StartCPA f2) -> CilType.Fundec.compare f1 f2
424+
| Any (CollectGraph (c, a1, b1)), Any (CollectGraph (c2, a2, b2)) ->
425+
let r = BaseDomain.CPA.compare c c2 in
426+
if r <> 0 then
427+
r
428+
else begin
429+
let r = AD.compare a1 a2 in
430+
if r <> 0 then
431+
r
432+
else
433+
AD.compare b1 b2
434+
end
420435
(* only argumentless queries should remain *)
421436
| _, _ -> Stdlib.compare (order a) (order b)
422437

@@ -530,6 +545,7 @@ struct
530545
| Any IsModular -> Pretty.dprintf "IsModular"
531546
| Any BaseCPA -> Pretty.dprintf "BaseCPA"
532547
| Any (StartCPA f) -> Pretty.dprintf "StartCPA %a" CilType.Fundec.pretty f
548+
| Any (CollectGraph (c, s, g)) -> Pretty.dprintf "CollectGraph (%a, %a, %a)" BaseDomain.CPA.pretty c AD.pretty s AD.pretty g
533549
end
534550

535551
let to_value_domain_ask (ask: ask) =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//PARAM: --enable modular --enable ana.modular.auto-funs --set ana.activated[+] "'modular_queries'" --set ana.activated[+] "'is_modular'" --set ana.activated[+] "'written'" --set ana.activated[+] "'read'" --set ana.activated[+] "'used_globals'" --set ana.activated[+] "'startstate'"
2+
#include <stdlib.h>
3+
4+
int foo(int *p){
5+
*p = 23;
6+
return 0;
7+
}
8+
9+
struct node {
10+
struct node* next;
11+
int value;
12+
};
13+
14+
int modify_node_ptr(struct node** n){
15+
*n = NULL;
16+
return 0;
17+
}
18+
19+
int modify_node_ptr_indirect(struct node** n){
20+
(*n)->next = NULL;
21+
return 0;
22+
}
23+
24+
25+
int modify_node(struct node* n){
26+
n->next = NULL;
27+
return 0;
28+
}
29+
30+
int modify_node_indirect(struct node* n){
31+
n->next->next = NULL;
32+
return 0;
33+
}

0 commit comments

Comments
 (0)