Skip to content

Commit f982bc9

Browse files
authored
Merge pull request #1144 from goblint/queries-ad-cont
Use `AddressDomain` for queries
2 parents 7f9ec9a + e718562 commit f982bc9

10 files changed

+113
-139
lines changed

src/analyses/base.ml

+47-48
Original file line numberDiff line numberDiff line change
@@ -1097,20 +1097,15 @@ struct
10971097
| Int x -> ValueDomain.ID.to_int x
10981098
| _ -> None
10991099

1100-
let eval_funvar ctx fval: varinfo list =
1101-
let exception OnlyUnknown in
1102-
try
1103-
let fp = eval_fv (Analyses.ask_of_ctx ctx) ctx.global ctx.local fval in
1104-
if AD.mem Addr.UnknownPtr fp then begin
1105-
let others = AD.to_var_may fp in
1106-
if others = [] then raise OnlyUnknown;
1107-
M.warn ~category:Imprecise ~tags:[Category Call] "Function pointer %a may contain unknown functions." d_exp fval;
1108-
dummyFunDec.svar :: others
1109-
end else
1110-
AD.to_var_may fp
1111-
with SetDomain.Unsupported _ | OnlyUnknown ->
1112-
M.warn ~category:Imprecise ~tags:[Category Call] "Unknown call to function %a." d_exp fval;
1113-
[dummyFunDec.svar]
1100+
let eval_funvar ctx fval: Queries.AD.t =
1101+
let fp = eval_fv (Analyses.ask_of_ctx ctx) ctx.global ctx.local fval in
1102+
if AD.is_top fp then (
1103+
if AD.cardinal fp = 1 then
1104+
M.warn ~category:Imprecise ~tags:[Category Call] "Unknown call to function %a." d_exp fval
1105+
else
1106+
M.warn ~category:Imprecise ~tags:[Category Call] "Function pointer %a may contain unknown functions." d_exp fval
1107+
);
1108+
fp
11141109

11151110
(** Evaluate expression as address.
11161111
Avoids expensive Apron EvalInt if the Int result would be useless to us anyway. *)
@@ -1204,10 +1199,7 @@ struct
12041199
let query ctx (type a) (q: a Q.t): a Q.result =
12051200
match q with
12061201
| Q.EvalFunvar e ->
1207-
begin
1208-
let fs = eval_funvar ctx e in
1209-
List.fold_left (fun xs v -> Q.LS.add (v,`NoOffset) xs) (Q.LS.empty ()) fs
1210-
end
1202+
eval_funvar ctx e
12111203
| Q.EvalJumpBuf e ->
12121204
begin match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with
12131205
| Address jmp_buf ->
@@ -2411,34 +2403,38 @@ struct
24112403
in
24122404
if get_bool "sem.noreturn.dead_code" && Cil.hasAttribute "noreturn" f.vattr then raise Deadcode else st
24132405

2414-
let combine_st ctx (local_st : store) (fun_st : store) (tainted_lvs : Q.LS.t) : store =
2406+
let combine_st ctx (local_st : store) (fun_st : store) (tainted_lvs : AD.t) : store =
24152407
let ask = (Analyses.ask_of_ctx ctx) in
2416-
Q.LS.fold (fun (v, o) st ->
2417-
if CPA.mem v fun_st.cpa then
2418-
let lval = Mval.Exp.to_cil (v,o) in
2419-
let address = eval_lv ask ctx.global st lval in
2420-
let lval_type = (AD.type_of address) in
2421-
if M.tracing then M.trace "taintPC" "updating %a; type: %a\n" Mval.Exp.pretty (v, o) d_type lval_type;
2422-
match (CPA.find_opt v (fun_st.cpa)), lval_type with
2423-
| None, _ -> st
2424-
(* partitioned arrays cannot be copied by individual lvalues, so if tainted just copy the whole callee value for the array variable *)
2425-
| Some (Array a), _ when (CArrays.domain_of_t a) = PartitionedDomain -> {st with cpa = CPA.add v (Array a) st.cpa}
2426-
(* "get" returned "unknown" when applied to a void type, so special case void types. This caused problems with some sv-comps (e.g. regtest 64 11) *)
2427-
| Some voidVal, TVoid _ -> {st with cpa = CPA.add v voidVal st.cpa}
2428-
| _, _ -> begin
2429-
let new_val = get ask ctx.global fun_st address None in
2430-
if M.tracing then M.trace "taintPC" "update val: %a\n\n" VD.pretty new_val;
2431-
let st' = set_savetop ~ctx ask ctx.global st address lval_type new_val in
2432-
let partDep = Dep.find_opt v fun_st.deps in
2433-
match partDep with
2434-
| None -> st'
2435-
(* if a var partitions an array, all cpa-info for arrays it may partition are added from callee to caller *)
2436-
| Some deps -> {st' with cpa = (Dep.VarSet.fold (fun v accCPA -> let val_opt = CPA.find_opt v fun_st.cpa in
2437-
match val_opt with
2438-
| None -> accCPA
2439-
| Some new_val -> CPA.add v new_val accCPA ) deps st'.cpa)}
2440-
end
2441-
else st) tainted_lvs local_st
2408+
AD.fold (fun addr st ->
2409+
match addr with
2410+
| Addr.Addr (v,o) ->
2411+
if CPA.mem v fun_st.cpa then
2412+
let lval = Addr.Mval.to_cil (v,o) in
2413+
let address = eval_lv ask ctx.global st lval in
2414+
let lval_type = Addr.type_of addr in
2415+
if M.tracing then M.trace "taintPC" "updating %a; type: %a\n" Addr.Mval.pretty (v,o) d_type lval_type;
2416+
match (CPA.find_opt v (fun_st.cpa)), lval_type with
2417+
| None, _ -> st
2418+
(* partitioned arrays cannot be copied by individual lvalues, so if tainted just copy the whole callee value for the array variable *)
2419+
| Some (Array a), _ when (CArrays.domain_of_t a) = PartitionedDomain -> {st with cpa = CPA.add v (Array a) st.cpa}
2420+
(* "get" returned "unknown" when applied to a void type, so special case void types. This caused problems with some sv-comps (e.g. regtest 64 11) *)
2421+
| Some voidVal, TVoid _ -> {st with cpa = CPA.add v voidVal st.cpa}
2422+
| _, _ -> begin
2423+
let new_val = get ask ctx.global fun_st address None in
2424+
if M.tracing then M.trace "taintPC" "update val: %a\n\n" VD.pretty new_val;
2425+
let st' = set_savetop ~ctx ask ctx.global st address lval_type new_val in
2426+
let partDep = Dep.find_opt v fun_st.deps in
2427+
match partDep with
2428+
| None -> st'
2429+
(* if a var partitions an array, all cpa-info for arrays it may partition are added from callee to caller *)
2430+
| Some deps -> {st' with cpa = (Dep.VarSet.fold (fun v accCPA -> let val_opt = CPA.find_opt v fun_st.cpa in
2431+
match val_opt with
2432+
| None -> accCPA
2433+
| Some new_val -> CPA.add v new_val accCPA ) deps st'.cpa)}
2434+
end
2435+
else st
2436+
| _ -> st
2437+
) tainted_lvs local_st
24422438

24432439
let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) =
24442440
let combine_one (st: D.t) (fun_st: D.t) =
@@ -2453,9 +2449,9 @@ struct
24532449
let cpa_noreturn = CPA.remove (return_varinfo ()) fun_st.cpa in
24542450
let ask = (Analyses.ask_of_ctx ctx) in
24552451
let tainted = f_ask.f Q.MayBeTainted in
2456-
if M.tracing then M.trace "taintPC" "combine for %s in base: tainted: %a\n" f.svar.vname Q.LS.pretty tainted;
2452+
if M.tracing then M.trace "taintPC" "combine for %s in base: tainted: %a\n" f.svar.vname AD.pretty tainted;
24572453
if M.tracing then M.trace "taintPC" "combine base:\ncaller: %a\ncallee: %a\n" CPA.pretty st.cpa CPA.pretty fun_st.cpa;
2458-
if Q.LS.is_top tainted then
2454+
if AD.is_top tainted then
24592455
let cpa_local = CPA.filter (fun x _ -> not (is_global ask x)) st.cpa in
24602456
let cpa' = CPA.fold CPA.add cpa_noreturn cpa_local in (* add cpa_noreturn to cpa_local *)
24612457
if M.tracing then M.trace "taintPC" "combined: %a\n" CPA.pretty cpa';
@@ -2470,7 +2466,10 @@ struct
24702466
let cpa_caller' = CPA.fold CPA.add cpa_new cpa_caller in
24712467
if M.tracing then M.trace "taintPC" "cpa_caller': %a\n" CPA.pretty cpa_caller';
24722468
(* remove lvals from the tainted set that correspond to variables for which we just added a new mapping from the callee*)
2473-
let tainted = Q.LS.filter (fun (v, _) -> not (CPA.mem v cpa_new)) tainted in
2469+
let tainted = AD.filter (function
2470+
| Addr.Addr (v,_) -> not (CPA.mem v cpa_new)
2471+
| _ -> false
2472+
) tainted in
24742473
let st_combined = combine_st ctx {st with cpa = cpa_caller'} fun_st tainted in
24752474
if M.tracing then M.trace "taintPC" "combined: %a\n" CPA.pretty st_combined.cpa;
24762475
{ fun_st with cpa = st_combined.cpa }

src/analyses/commonPriv.ml

+6-12
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,10 @@ struct
6060
ask.f (Q.MustBeProtectedBy {mutex=m; global=x; write=true; protection})
6161

6262
let protected_vars (ask: Q.ask): varinfo list =
63-
let module VS = Set.Make (CilType.Varinfo) in
64-
Q.LS.fold (fun (v, _) acc ->
65-
let m = ValueDomain.Addr.of_var v in (* TODO: don't ignore offsets *)
66-
Q.LS.fold (fun l acc ->
67-
VS.add (fst l) acc (* always `NoOffset from mutex analysis *)
68-
) (ask.f (Q.MustProtectedVars {mutex = m; write = true})) acc
69-
) (ask.f Q.MustLockset) VS.empty
70-
|> VS.elements
63+
Q.AD.fold (fun m acc ->
64+
Q.VS.join (ask.f (Q.MustProtectedVars {mutex = m; write = true})) acc
65+
) (ask.f Q.MustLockset) (Q.VS.empty ())
66+
|> Q.VS.elements
7167
end
7268

7369
module MutexGlobals =
@@ -126,10 +122,8 @@ struct
126122
if !AnalysisState.global_initialization then
127123
Lockset.empty ()
128124
else
129-
let ls = ask.f Queries.MustLockset in
130-
Q.LS.fold (fun (var, offs) acc ->
131-
Lockset.add (Lock.of_mval (var, Lock.Offs.of_exp offs)) acc
132-
) ls (Lockset.empty ())
125+
let ad = ask.f Queries.MustLockset in
126+
Q.AD.fold (fun mls acc -> Lockset.add mls acc) ad (Lockset.empty ()) (* TODO: use AD as Lockset *)
133127

134128
(* TODO: reversed SetDomain.Hoare *)
135129
module MinLocksets = HoareDomain.Set_LiftTop (MustLockset) (struct let topname = "All locksets" end) (* reverse Lockset because Hoare keeps maximal, but we need minimal *)

src/analyses/modifiedSinceLongjmp.ml

+7-13
Original file line numberDiff line numberDiff line change
@@ -23,29 +23,23 @@ struct
2323
(* Only checks for v.vglob on purpose, acessing espaced locals after longjmp is UB like for any local *)
2424
not v.vglob (* *) && not (BaseUtil.is_volatile v) && v.vstorage <> Static
2525

26-
let relevants_from_ls ls =
27-
if Queries.LS.is_top ls then
28-
VS.top ()
29-
else
30-
Queries.LS.fold (fun (v, _) acc -> if is_relevant v then VS.add v acc else acc) ls (VS.empty ())
31-
32-
let relevants_from_ad ad =
26+
let relevants_from_ad ls =
3327
(* TODO: what about AD with both known and unknown pointers? *)
34-
if Queries.AD.is_top ad then
28+
if Queries.AD.is_top ls then
3529
VS.top ()
3630
else
37-
Queries.AD.fold (fun addr vs ->
31+
Queries.AD.fold (fun addr acc ->
3832
match addr with
39-
| Queries.AD.Addr.Addr (v,_) -> if is_relevant v then VS.add v vs else vs
40-
| _ -> vs
41-
) ad (VS.empty ())
33+
| Queries.AD.Addr.Addr (v, _) when is_relevant v -> VS.add v acc
34+
| _ -> acc
35+
) ls (VS.empty ())
4236

4337
(* transfer functions *)
4438
let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list =
4539
[ctx.local, D.bot ()] (* enter with bot as opposed to IdentitySpec *)
4640

4741
let combine_env ctx lval fexp f args fc au (f_ask: Queries.ask) =
48-
let taintedcallee = relevants_from_ls (f_ask.f Queries.MayBeTainted) in
42+
let taintedcallee = relevants_from_ad (f_ask.f Queries.MayBeTainted) in
4943
add_to_all_defined taintedcallee ctx.local
5044

5145
let combine_assign ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) (f_ask:Queries.ask) : D.t =

src/analyses/mutexAnalysis.ml

+3-9
Original file line numberDiff line numberDiff line change
@@ -233,21 +233,15 @@ struct
233233
Mutexes.leq mutex_lockset protecting
234234
| Queries.MustLockset ->
235235
let held_locks = Lockset.export_locks (Lockset.filter snd ls) in
236-
let ls = Mutexes.fold (fun addr ls ->
237-
match Addr.to_mval addr with
238-
| Some (var, offs) -> Queries.LS.add (var, Addr.Offs.to_exp offs) ls
239-
| None -> ls
240-
) held_locks (Queries.LS.empty ())
241-
in
242-
ls
236+
Mutexes.fold (fun addr ls -> Queries.AD.add addr ls) held_locks (Queries.AD.empty ())
243237
| Queries.MustBeAtomic ->
244238
let held_locks = Lockset.export_locks (Lockset.filter snd ls) in
245239
Mutexes.mem (LockDomain.Addr.of_var LF.verifier_atomic_var) held_locks
246240
| Queries.MustProtectedVars {mutex = m; write} ->
247241
let protected = GProtected.get ~write Strong (G.protected (ctx.global (V.protected m))) in
248242
VarSet.fold (fun v acc ->
249-
Queries.LS.add (v, `NoOffset) acc
250-
) protected (Queries.LS.empty ())
243+
Queries.VS.add v acc
244+
) protected (Queries.VS.empty ())
251245
| Queries.IterSysVars (Global g, f) ->
252246
f (Obj.repr (V.protecting g)) (* TODO: something about V.protected? *)
253247
| WarnGlobal g ->

src/analyses/poisonVariables.ml

+12-13
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,16 @@ struct
1515

1616
let context _ _ = ()
1717

18-
let check_mval tainted ((v, offset): Queries.LS.elt) =
19-
if not v.vglob && VS.mem v tainted then
20-
M.warn ~category:(Behavior (Undefined Other)) "Reading poisonous variable %a" CilType.Varinfo.pretty v
18+
let check_mval tainted (addr: Queries.AD.elt) =
19+
match addr with
20+
| Queries.AD.Addr.Addr (v,_) ->
21+
if not v.vglob && VS.mem v tainted then
22+
M.warn ~category:(Behavior (Undefined Other)) "Reading poisonous variable %a" CilType.Varinfo.pretty v
23+
| _ -> ()
2124

22-
let rem_mval tainted ((v, offset): Queries.LS.elt) = match offset with
23-
| `NoOffset -> VS.remove v tainted
25+
let rem_mval tainted (addr: Queries.AD.elt) =
26+
match addr with
27+
| Queries.AD.Addr.Addr (v,`NoOffset) -> VS.remove v tainted
2428
| _ -> tainted (* If there is an offset, it is a bit harder to remove, as we don't know where the indeterminate value is *)
2529

2630

@@ -88,11 +92,8 @@ struct
8892
| ad when Queries.AD.is_top ad && not (VS.is_empty octx.local) ->
8993
M.warn ~category:(Behavior (Undefined Other)) "reading unknown memory location, may be tainted!"
9094
| ad ->
91-
Queries.AD.iter (function
92-
(* Use original access state instead of current with removed written vars. *)
93-
| Queries.AD.Addr.Addr (v,o) -> check_mval octx.local (v, ValueDomain.Offs.to_exp o)
94-
| _ -> ()
95-
) ad
95+
(* Use original access state instead of current with removed written vars. *)
96+
Queries.AD.iter (check_mval octx.local) ad
9697
end;
9798
ctx.local
9899
| Access {ad; kind = Write; _} ->
@@ -102,9 +103,7 @@ struct
102103
ctx.local
103104
| ad ->
104105
Queries.AD.fold (fun addr vs ->
105-
match addr with
106-
| Queries.AD.Addr.Addr (v,o) -> rem_mval vs (v, ValueDomain.Offs.to_exp o) (* TODO: use unconverted addrs in domain? *)
107-
| _ -> vs
106+
rem_mval vs addr
108107
) ad ctx.local
109108
end
110109
| _ -> ctx.local

src/analyses/taintPartialContexts.ml

+16-26
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,19 @@
66
open GoblintCil
77
open Analyses
88

9-
module D = SetDomain.ToppedSet (Mval.Exp) (struct let topname = "All" end)
10-
11-
let to_mvals ad =
12-
(* TODO: should one handle ad with unknown pointers separately like in (all) other analyses? *)
13-
Queries.AD.fold (fun addr mvals ->
14-
match addr with
15-
| Queries.AD.Addr.Addr (v,o) -> D.add (v, ValueDomain.Offs.to_exp o) mvals (* TODO: use unconverted addrs in domain? *)
16-
| _ -> mvals
17-
) ad (D.empty ())
9+
module AD = ValueDomain.AD
1810

1911
module Spec =
2012
struct
2113
include Analyses.IdentitySpec
2214

2315
let name () = "taintPartialContexts"
24-
module D = D
16+
module D = AD
2517
module C = Lattice.Unit
2618

2719
(* Add Lval or any Lval which it may point to to the set *)
2820
let taint_lval ctx (lval:lval) : D.t =
29-
let d = ctx.local in
30-
(match lval with
31-
| (Var v, offs) -> D.add (v, Offset.Exp.of_cil offs) d
32-
| (Mem e, _) -> D.union (to_mvals (ctx.ask (Queries.MayPointTo e))) d
33-
)
21+
D.union (ctx.ask (Queries.MayPointTo (AddrOf lval))) ctx.local
3422

3523
(* this analysis is context insensitive*)
3624
let context _ _ = ()
@@ -45,14 +33,12 @@ struct
4533
let d_return =
4634
if D.is_top d then
4735
d
48-
else (
36+
else
4937
let locals = f.sformals @ f.slocals in
50-
D.filter (fun (v, _) ->
51-
not (List.exists (fun local ->
52-
CilType.Varinfo.equal v local && not (ctx.ask (Queries.IsMultiple local))
53-
) locals)
38+
D.filter (function
39+
| AD.Addr.Addr (v,_) -> not (List.exists (fun local -> CilType.Varinfo.equal v local && not (ctx.ask (Queries.IsMultiple local))) locals)
40+
| _ -> false
5441
) d
55-
)
5642
in
5743
if M.tracing then M.trace "taintPC" "returning from %s: tainted vars: %a\n without locals: %a\n" f.svar.vname D.pretty d D.pretty d_return;
5844
d_return
@@ -94,9 +80,10 @@ struct
9480
else
9581
deep_addrs
9682
in
97-
let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.MayPointTo addr)))) d shallow_addrs
83+
(* TODO: should one handle ad with unknown pointers separately like in (all) other analyses? *)
84+
let d = List.fold_left (fun accD addr -> D.union accD (ctx.ask (Queries.MayPointTo addr))) d shallow_addrs
9885
in
99-
let d = List.fold_left (fun accD addr -> D.union accD (to_mvals (ctx.ask (Queries.ReachableFrom addr)))) d deep_addrs
86+
let d = List.fold_left (fun accD addr -> D.union accD (ctx.ask (Queries.ReachableFrom addr))) d deep_addrs
10087
in
10188
d
10289

@@ -111,7 +98,7 @@ struct
11198

11299
let query ctx (type a) (q: a Queries.t) : a Queries.result =
113100
match q with
114-
| MayBeTainted -> (ctx.local : Queries.LS.t)
101+
| MayBeTainted -> (ctx.local : Queries.AD.t)
115102
| _ -> Queries.Result.top q
116103

117104
end
@@ -122,5 +109,8 @@ let _ =
122109
module VS = SetDomain.ToppedSet(Basetype.Variables) (struct let topname = "All" end)
123110

124111
(* Convert Lval set to (less precise) Varinfo set. *)
125-
let conv_varset (lval_set : Spec.D.t) : VS.t =
126-
if Spec.D.is_top lval_set then VS.top () else VS.of_list (List.map (fun (v, _) -> v) (Spec.D.elements lval_set))
112+
let conv_varset (addr_set : Spec.D.t) : VS.t =
113+
if Spec.D.is_top addr_set then
114+
VS.top ()
115+
else
116+
VS.of_list (Spec.D.to_var_may addr_set)

src/analyses/varEq.ml

+6-2
Original file line numberDiff line numberDiff line change
@@ -437,10 +437,14 @@ struct
437437
let d_local =
438438
(* if we are multithreaded, we run the risk, that some mutex protected variables got unlocked, so in this case caller state goes to top
439439
TODO: !!Unsound, this analysis does not handle this case -> regtest 63 08!! *)
440-
if Queries.LS.is_top tainted || not (ctx.ask (Queries.MustBeSingleThreaded {since_start = true})) then
440+
if Queries.AD.is_top tainted || not (ctx.ask (Queries.MustBeSingleThreaded {since_start = true})) then
441441
D.top ()
442442
else
443-
let taint_exp = Queries.ES.of_list (List.map Mval.Exp.to_cil_exp (Queries.LS.elements tainted)) in
443+
let taint_exp =
444+
Queries.AD.to_mval tainted
445+
|> List.map Addr.Mval.to_cil_exp
446+
|> Queries.ES.of_list
447+
in
444448
D.filter (fun exp -> not (Queries.ES.mem exp taint_exp)) ctx.local
445449
in
446450
let d = D.meet au d_local in

src/cdomains/lockDomain.ml

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module IdxDom = ValueDomain.IndexDomain
77

88
open GoblintCil
99

10-
module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO HoareDomain? *)
10+
module Mutexes = SetDomain.ToppedSet (Addr) (struct let topname = "All mutexes" end) (* TODO: AD? *)
1111
module Simple = Lattice.Reverse (Mutexes)
1212
module Priorities = IntDomain.Lifted
1313

0 commit comments

Comments
 (0)