From 514107ec888b3ff98b4a08b09ce0105751c2a07b Mon Sep 17 00:00:00 2001 From: Sam Blackshear Date: Sat, 20 Aug 2016 12:47:36 -0700 Subject: [PATCH] track sinks Reviewed By: mbouaziz Differential Revision: D3725051 fbshipit-source-id: 294203a --- infer/src/quandary/TaintAnalysis.ml | 38 ++++++++++++++++++--- infer/src/unit/TaintTests.ml | 51 +++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/infer/src/quandary/TaintAnalysis.ml b/infer/src/quandary/TaintAnalysis.ml index 2fea1ecf0..ab5a102ac 100644 --- a/infer/src/quandary/TaintAnalysis.ml +++ b/infer/src/quandary/TaintAnalysis.ml @@ -112,7 +112,29 @@ module Make (TraceDomain : Trace.S) = struct let id_ap = AccessPath.Exact (AccessPath.of_id ret_id ret_typ) in TaintDomain.add_trace id_ap trace access_tree - let exec_instr ({ Domain.access_tree; id_map; } as astate) proc_data _ instr = + let add_sinks sinks actuals ({ Domain.access_tree; id_map; } as astate) proc_data loc = + let f_resolve_id = resolve_id id_map in + (* add [sink] to the trace associated with the [formal_num]th actual *) + let add_sink_to_actual access_tree_acc (formal_num, sink) = + let actual_exp, actual_typ = IList.nth actuals formal_num in + match AccessPath.of_exp actual_exp actual_typ ~f_resolve_id with + | Some actual_ap -> + let actual_ap = AccessPath.Exact actual_ap in + begin + match access_path_get_node actual_ap access_tree_acc proc_data loc with + | Some (actual_trace, _) -> + (* add callee_pname to actual trace as a sink *) + let actual_trace' = TraceDomain.add_sink sink actual_trace in + TaintDomain.add_trace actual_ap actual_trace' access_tree_acc + | None -> + access_tree_acc + end + | None -> + access_tree_acc in + let access_tree' = IList.fold_left add_sink_to_actual access_tree sinks in + { astate with Domain.access_tree = access_tree'; } + + let exec_instr ({ Domain.id_map; } as astate) proc_data _ instr = let f_resolve_id = resolve_id id_map in match instr with | Sil.Letderef (lhs_id, rhs_exp, rhs_typ, _loc) -> @@ -153,8 +175,14 @@ module Make (TraceDomain : Trace.S) = struct | _ -> astate' end - | Sil.Call (ret_ids, Const (Cfun callee_pname), _, callee_loc, _) -> + | Sil.Call (ret_ids, Const (Cfun callee_pname), actuals, callee_loc, _) -> let call_site = CallSite.make callee_pname callee_loc in + + let astate_with_sink = + match TraceDomain.Sink.get call_site with + | [] -> astate + | sinks -> add_sinks sinks actuals astate proc_data callee_loc in + let ret_typ = match callee_pname with | Procname.Java java_pname -> @@ -173,10 +201,10 @@ module Make (TraceDomain : Trace.S) = struct let astate_with_source = match TraceDomain.Source.get call_site, ret_ids with | [(0, source)], [ret_id] -> - let access_tree' = add_source source ret_id ret_typ access_tree in - { astate with Domain.access_tree = access_tree'; } + let access_tree = add_source source ret_id ret_typ astate_with_sink.access_tree in + { astate_with_sink with access_tree; } | [], _ | _, [] -> - astate + astate_with_sink | _ -> (* this is allowed by SIL, but not currently used in any frontends *) failwith "Unimp: handling multiple return ids" in diff --git a/infer/src/unit/TaintTests.ml b/infer/src/unit/TaintTests.ml index 005e6fab3..2e49514cc 100644 --- a/infer/src/unit/TaintTests.ml +++ b/infer/src/unit/TaintTests.ml @@ -112,6 +112,11 @@ let tests = let assign_to_non_source ret_str = let procname = Procname.from_string_c_fun "NON-SOURCE" in make_call ~procname [ident_of_str ret_str] [] in + let call_sink_with_exp exp = + let procname = Procname.from_string_c_fun "SINK" in + make_call ~procname [] [(exp, dummy_typ)] in + let call_sink actual_str = + call_sink_with_exp (Exp.Var (ident_of_str actual_str)) in let assign_id_to_field root_str fld_str rhs_id_str = let rhs_exp = Exp.Var (ident_of_str rhs_id_str) in make_store ~rhs_typ:Typ.Tvoid (Exp.Var (ident_of_str root_str)) fld_str ~rhs_exp in @@ -208,5 +213,51 @@ let tests = invariant "{ source_id$0 => (SOURCE -> ?), &var1.g.f => (SOURCE -> ?), &var2 => (SOURCE -> ?) }"; ]; + "sink without source not tracked", + [ + assign_to_non_source "ret_id"; + call_sink "ret_id"; + assert_empty; + ]; + "source -> sink direct", + [ + assign_to_source "ret_id"; + call_sink "ret_id"; + invariant "{ ret_id$0 => (SOURCE -> SINK) }"; + ]; + "source -> sink via var", + [ + assign_to_source "ret_id"; + var_assign_id "actual" "ret_id"; + call_sink_with_exp (var_of_str "actual"); + invariant "{ ret_id$0 => (SOURCE -> ?), &actual => (SOURCE -> SINK) }"; + ]; + "source -> sink via var then ident", + [ + assign_to_source "ret_id"; + var_assign_id "x" "ret_id"; + id_assign_var "actual_id" "x"; + call_sink "actual_id"; + invariant "{ ret_id$0 => (SOURCE -> ?), &x => (SOURCE -> SINK) }"; + ]; + "source -> sink via field", + [ + assign_to_source "ret_id"; + assign_id_to_field "base_id" "f" "ret_id"; + read_field_to_id "actual_id" "base_id" "f"; + call_sink "actual_id"; + invariant "{ base_id$0.f => (SOURCE -> SINK), ret_id$0 => (SOURCE -> ?) }"; + ]; + "source -> sink via field read from var", + [ + assign_to_source "ret_id"; + assign_id_to_field "base_id" "f" "ret_id"; + var_assign_id "var" "base_id"; + id_assign_var "var_id" "var"; + read_field_to_id "read_id" "var_id" "f"; + call_sink "read_id"; + invariant + "{ base_id$0.f => (SOURCE -> ?), ret_id$0 => (SOURCE -> ?), &var.f => (SOURCE -> SINK) }"; + ]; ] |> TestInterpreter.create_tests ~pp_opt:pp_sparse [] in "taint_test_suite">:::test_list