From 8d48c108ca845f826563e0f458d424d1357f1c28 Mon Sep 17 00:00:00 2001 From: Sam Blackshear Date: Wed, 16 Nov 2016 14:32:03 -0800 Subject: [PATCH] [quandary] add Errlog traces Summary: Generalizing jvillard's awesome work to include passthroughs in traces, then calling it from Quandary. Reviewed By: jvillard Differential Revision: D4172108 fbshipit-source-id: 0296c59 --- infer/src/checkers/Passthrough.ml | 3 ++ infer/src/checkers/Passthrough.mli | 2 + infer/src/checkers/Trace.ml | 64 +++++++++++++++++++++-------- infer/src/quandary/TaintAnalysis.ml | 3 +- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/infer/src/checkers/Passthrough.ml b/infer/src/checkers/Passthrough.ml index 2ffbc0dba..fe5902a04 100644 --- a/infer/src/checkers/Passthrough.ml +++ b/infer/src/checkers/Passthrough.ml @@ -21,6 +21,9 @@ type t = let make site = { site } +let site t = + t.site + let compare pt1 pt2 = (match pt1, pt2 with | {site=site1}, {site=site2} -> CallSite.compare site1 site2 diff --git a/infer/src/checkers/Passthrough.mli b/infer/src/checkers/Passthrough.mli index 0dfe560ef..37e84d443 100644 --- a/infer/src/checkers/Passthrough.mli +++ b/infer/src/checkers/Passthrough.mli @@ -13,6 +13,8 @@ type t val make : CallSite.t -> t +val site : t -> CallSite.t + val compare : t -> t -> int val pp : F.formatter -> t -> unit diff --git a/infer/src/checkers/Trace.ml b/infer/src/checkers/Trace.ml index eff3ad02b..9c6416159 100644 --- a/infer/src/checkers/Trace.ml +++ b/infer/src/checkers/Trace.ml @@ -239,23 +239,53 @@ module Make (Spec : Spec) = struct let callsite = Sink.call_site sink in Format.asprintf "call to %a" Procname.pp (CallSite.pname callsite)) ?(sink_should_nest=(fun _ -> true)) - (_, sources, sinks) = - let trace_elem_of_path_elem call_site desc should_nest = - let level = ref 0 in - fun (elem, _) -> - let lt_level = !level in - let desc = desc elem in - let callsite = call_site elem in - if should_nest elem then incr level; - Errlog.make_trace_element lt_level (CallSite.loc callsite) desc [] in - let trace_elem_of_source = - trace_elem_of_path_elem Source.call_site desc_of_source source_should_nest in - let trace_elem_of_sink = - trace_elem_of_path_elem Sink.call_site desc_of_sink sink_should_nest in - (* reverse sinks intentionally, do not reverse sources(?) *) - IList.rev_append - (IList.rev_map trace_elem_of_source sources) - (IList.map trace_elem_of_sink (IList.rev sinks)) + (passthroughs, sources, sinks) = + + let trace_elems_of_passthroughs lt_level passthroughs acc0 = + let trace_elem_of_passthrough passthrough acc = + let passthrough_site = Passthrough.site passthrough in + let desc = F.asprintf "flow through %a" Procname.pp (CallSite.pname passthrough_site) in + (Errlog.make_trace_element lt_level (CallSite.loc passthrough_site) desc []) :: acc in + (* sort passthroughs by ascending line number to create a coherent trace *) + let sorted_passthroughs = + IList.sort + (fun passthrough1 passthrough2 -> + let loc1 = CallSite.loc (Passthrough.site passthrough1) in + let loc2 = CallSite.loc (Passthrough.site passthrough2) in + Pervasives.compare loc1.Location.line loc2.Location.line) + (Passthroughs.elements passthroughs) in + IList.fold_right trace_elem_of_passthrough sorted_passthroughs acc0 in + + let get_nesting should_nest elems start_nesting = + let level = ref start_nesting in + let get_nesting_ ((elem, _) as pair) = + if should_nest elem + then incr level; + pair, !level in + IList.map get_nesting_ (IList.rev elems) in + + let trace_elems_of_path_elem call_site desc ~is_source ((elem, passthroughs), lt_level) acc = + let desc = desc elem in + let loc = CallSite.loc (call_site elem) in + if is_source + then + let trace_elem = Errlog.make_trace_element lt_level loc desc [] in + trace_elems_of_passthroughs (lt_level + 1) passthroughs (trace_elem :: acc) + else + let trace_elem = Errlog.make_trace_element (lt_level - 1) loc desc [] in + trace_elem :: (trace_elems_of_passthroughs lt_level passthroughs acc) in + + let trace_elems_of_source = + trace_elems_of_path_elem Source.call_site desc_of_source ~is_source:true in + let trace_elems_of_sink = + trace_elems_of_path_elem Sink.call_site desc_of_sink ~is_source:false in + let sources_with_level = get_nesting source_should_nest sources (-1) in + let sinks_with_level = get_nesting sink_should_nest sinks 0 in + let trace_prefix = + IList.fold_right trace_elems_of_sink sinks_with_level [] + |> trace_elems_of_passthroughs 0 passthroughs in + IList.fold_left + (fun acc source -> trace_elems_of_source source acc) trace_prefix sources_with_level let of_source source = let sources = Sources.singleton source in diff --git a/infer/src/quandary/TaintAnalysis.ml b/infer/src/quandary/TaintAnalysis.ml index 606b993e2..dfff71853 100644 --- a/infer/src/quandary/TaintAnalysis.ml +++ b/infer/src/quandary/TaintAnalysis.ml @@ -176,8 +176,9 @@ module Make (TaintSpec : TaintSpec.S) = struct let caller_pname = Procdesc.get_proc_name proc_data.pdesc in let msg = Localise.to_string Localise.quandary_taint_error in let trace_str = F.asprintf "%a" pp_path_short path in + let ltr = TraceDomain.to_loc_trace path in let exn = Exceptions.Checkers (msg, Localise.verbatim_desc trace_str) in - Reporting.log_error caller_pname ~loc:(CallSite.loc callee_site) exn in + Reporting.log_error caller_pname ~loc:(CallSite.loc callee_site) ~ltr exn in let reported_sinks = IList.map