diff --git a/src/plugins/value/domains/taint_domain.ml b/src/plugins/value/domains/taint_domain.ml index ae137312ca71732afc9d0a420e23fdd51aca29c4..a8eaf9900a164049d70e4a928ecf5b94f39b0efc 100644 --- a/src/plugins/value/domains/taint_domain.ml +++ b/src/plugins/value/domains/taint_domain.ml @@ -34,8 +34,11 @@ type taint = { (* Set of assume statements over a tainted expression. This set is needed to implement control-dependency: all left-values appearing in statements whose evaluation depends on at least one of the assume expressions is to be - tainted. *) + tainted. This set is restricted to statements of the current function. *) assume_stmts: Stmt.Set.t; + (* Whether the current call depends on a tainted assume statement: if true, + all assignments in the current call should be control tainted. *) + dependent_call: bool; } let dkey = Value_parameters.register_category "d-taint" @@ -59,10 +62,12 @@ module LatticeTaint = struct Format.fprintf fmt "@[<v 2>Locations (data):@ @[<hov>%a@]@]@\n\ @[<v 2>Locations (control):@ @[<hov>%a@]@]@\n\ - @[<v 2>Assume statements:@ @[<hov>%a@]@]" + @[<v 2>Assume statements:@ @[<hov>%a@]@\n\ + @[<v 2>Dependent call:@ %b@]" Zone.pretty t.locs_data Zone.pretty t.locs_control Stmt.Set.pretty t.assume_stmts + t.dependent_call (* Frama-C "datatype" for type [taint]. *) include Datatype.Make_with_collections(struct @@ -75,20 +80,18 @@ module LatticeTaint = struct let reprs = [ { locs_data = List.hd Zone.reprs; locs_control = List.hd Zone.reprs; - assume_stmts = Stmt.Set.empty; } ] + assume_stmts = Stmt.Set.empty; + dependent_call = false; } ] let structural_descr = Structural_descr.t_abstract (* TODO *) let compare t1 t2 = - let c = Zone.compare t1.locs_data t2.locs_data in - if c <> 0 - then c - else - let c = Zone.compare t1.locs_data t2.locs_data in - if c <> 0 - then c - else Stmt.Set.compare t1.assume_stmts t2.assume_stmts + let (<?>) c (cmp,x,y) = if c = 0 then cmp x y else c in + Zone.compare t1.locs_data t2.locs_data + <?> (Zone.compare, t1.locs_control, t2.locs_control) + <?> (Stmt.Set.compare, t1.assume_stmts, t2.assume_stmts) + <?> (Datatype.Bool.compare, t1.dependent_call, t2.dependent_call) let equal = Datatype.from_compare @@ -101,7 +104,8 @@ module LatticeTaint = struct Hashtbl.hash (Zone.hash t.locs_data, Zone.hash t.locs_control, - Stmt.Set.hash t.assume_stmts) + Stmt.Set.hash t.assume_stmts, + t.dependent_call) let copy c = c @@ -112,6 +116,7 @@ module LatticeTaint = struct locs_data = Zone.bottom; locs_control = Zone.bottom; assume_stmts = Stmt.Set.empty; + dependent_call = false; } (* Top state: everything is tainted. *) @@ -119,13 +124,15 @@ module LatticeTaint = struct locs_data = Zone.top; locs_control = Zone.top; assume_stmts = Stmt.Set.empty; + dependent_call = false; } (* Join: keep pointwise over-approximation. *) let join t1 t2 = { locs_data = Zone.join t1.locs_data t2.locs_data; locs_control = Zone.join t1.locs_control t2.locs_control; - assume_stmts = Stmt.Set.union t1.assume_stmts t2.assume_stmts; } + assume_stmts = Stmt.Set.union t1.assume_stmts t2.assume_stmts; + dependent_call = t1.dependent_call || t2.dependent_call; } (* The memory locations are finite, so the ascending chain property is already verified. We simply use a join. *) @@ -136,6 +143,7 @@ module LatticeTaint = struct locs_data = Zone.narrow t1.locs_data t2.locs_data; locs_control = Zone.narrow t1.locs_control t2.locs_control; assume_stmts = Stmt.Set.inter t1.assume_stmts t2.assume_stmts; + dependent_call = t1.dependent_call && t2.dependent_call; } (* Inclusion testing: pointwise, on locs only. *) @@ -192,7 +200,7 @@ module TransferTaint = struct (* Control-dependency: taint the left-value of an assign statement whose execution depends on the value of a tainted assume statement. *) let state = - if Stmt.Set.is_empty state.assume_stmts + if not state.dependent_call && Stmt.Set.is_empty state.assume_stmts then (* No active tainted assume statement means that there is no control-dependecy that applies on [lv]. *) @@ -225,7 +233,7 @@ module TransferTaint = struct let to_loc = loc_of_lval valuation in let exp_zone = Value_util.zone_of_expr to_loc exp in let state = - if LatticeTaint.intersects state exp_zone + if not state.dependent_call && LatticeTaint.intersects state exp_zone then { state with assume_stmts = Stmt.Set.add stmt state.assume_stmts; } else state in @@ -233,6 +241,10 @@ module TransferTaint = struct let start_call stmt call _recursion valuation state = let state = filter_active_tainted_assumes stmt state in + let dependent_call = + state.dependent_call || not (Stmt.Set.is_empty state.assume_stmts) + in + let state = { state with assume_stmts = Stmt.Set.empty; dependent_call } in let state = (* Add tainted actual parameters in [state]. *) let to_loc = loc_of_lval valuation in @@ -251,7 +263,8 @@ module TransferTaint = struct let finalize_call _stmt _call _recursion ~pre ~post = (* Recover assume statements from the [pre] abstract state: we assume the control-dependency does not extended beyond the function scope. *) - `Value { post with assume_stmts = pre.assume_stmts; } + `Value { post with assume_stmts = pre.assume_stmts; + dependent_call = pre.dependent_call; } let show_expr valuation state fmt exp = let to_loc = loc_of_lval valuation in @@ -326,14 +339,14 @@ module TaintDomain = struct let enter_scope _kind _vars state = state + let remove_bases bases state = + let remove = Zone.filter_base (fun b -> not (Base.Hptset.mem b bases)) in + { state with locs_data = remove state.locs_data; + locs_control = remove state.locs_control; } + let leave_scope _kf vars state = - let remove_unscoped_bases = - let bases = Base.Set.of_list (List.map Base.of_varinfo vars) in - Zone.filter_base (fun b -> not (Base.Set.mem b bases)) - in - { state with - locs_data = remove_unscoped_bases state.locs_data; - locs_control = remove_unscoped_bases state.locs_control; } + let bases = Base.Hptset.of_list (List.map Base.of_varinfo vars) in + remove_bases bases state (* Initial state: initializers are singletons, so we store nothing. *) @@ -342,8 +355,18 @@ module TaintDomain = struct let initialize_variable_using_type _ _ state = state - (* Misc. *) + (* MemExec cache. *) let relate _kf _bases _state = Base.SetLattice.empty + + let filter _kf _kind bases state = + let filter_base = Zone.filter_base (fun b -> Base.Hptset.mem b bases) in + { state with locs_data = filter_base state.locs_data; + locs_control = filter_base state.locs_control; + assume_stmts = Stmt.Set.empty; } + + let reuse _kf bases ~current_input ~previous_output = + let state = remove_bases bases current_input in + join state previous_output end include TaintDomain