[traces] expose source-sink paths so users of traces can custom-print them

Reviewed By: jvillard

Differential Revision: D4136948

fbshipit-source-id: 5ceffeb
master
Sam Blackshear 8 years ago committed by Facebook Github Bot
parent b3e406ddf3
commit a8129be763

@ -30,6 +30,11 @@ module type S = sig
module Sinks = Sink.Set module Sinks = Sink.Set
module Passthroughs = Passthrough.Set module Passthroughs = Passthrough.Set
(** path from a source to a sink with passthroughs at each step in the call stack. the first set
of passthroughs are the ones in the "reporting" procedure that calls the first function in
both the source and sink stack *)
type path = Passthroughs.t * (Source.t * Passthroughs.t) list * (Sink.t * Passthroughs.t) list
(** get the sources of the trace. *) (** get the sources of the trace. *)
val sources : t -> Sources.t val sources : t -> Sources.t
@ -42,13 +47,8 @@ module type S = sig
(** get the reportable source-sink flows in this trace *) (** get the reportable source-sink flows in this trace *)
val get_reports : t -> (Source.t * Sink.t * Passthroughs.t) list val get_reports : t -> (Source.t * Sink.t * Passthroughs.t) list
(** get logging-ready trace strings for the reportable source-sink flows in this trace *) (** get a path for each of the reportable source -> sink flows in this trace *)
val get_reportable_traces : val get_reportable_paths : t -> trace_of_pname:(Procname.t -> t) -> path list
t ->
Procname.t ->
?expand_trace:bool ->
trace_of_pname:(Procname.t -> t) ->
(Source.t * Sink.t * string) list
(** create a trace from a source *) (** create a trace from a source *)
val of_source : Source.t -> t val of_source : Source.t -> t
@ -73,6 +73,9 @@ module type S = sig
val equal : t -> t -> bool val equal : t -> t -> bool
val pp : F.formatter -> t -> unit val pp : F.formatter -> t -> unit
(** pretty-print a path in the context of the given procname *)
val pp_path : F.formatter -> Procname.t -> path -> unit
end end
(** Expand a trace element (i.e., a source or sink) into a list of trace elements bottoming out in (** Expand a trace element (i.e., a source or sink) into a list of trace elements bottoming out in
@ -124,6 +127,8 @@ module Make (Spec : Spec) = struct
type astate = t type astate = t
type path = Passthroughs.t * (Source.t * Passthroughs.t) list * (Sink.t * Passthroughs.t) list
let compare t1 t2 = let compare t1 t2 =
Sources.compare t1.sources t2.sources Sources.compare t1.sources t2.sources
|> next Sinks.compare t1.sinks t2.sinks |> next Sinks.compare t1.sinks t2.sinks
@ -161,57 +166,52 @@ module Make (Spec : Spec) = struct
else acc in else acc in
Sources.fold (fun source acc -> Sinks.fold (report_one source) t.sinks acc) t.sources [] Sources.fold (fun source acc -> Sinks.fold (report_one source) t.sinks acc) t.sources []
let get_reportable_traces t cur_pname ?(expand_trace=true) ~trace_of_pname = let pp_path fmt cur_pname (cur_passthroughs, sources_passthroughs, sinks_passthroughs) =
let pp_passthroughs fmt passthroughs = let pp_passthroughs fmt passthroughs =
if not (Passthrough.Set.is_empty passthroughs) if not (Passthrough.Set.is_empty passthroughs)
then F.fprintf fmt "(via %a)" Passthrough.Set.pp passthroughs in then F.fprintf fmt "(via %a)" Passthrough.Set.pp passthroughs in
let get_expanded_trace_string let pp_elems elem_to_callsite fmt elems_passthroughs =
cur_pname cur_passthroughs sources_passthroughs sinks_passthroughs = let pp_sep fmt () = F.fprintf fmt "@." in
let pp_elems elem_to_callsite fmt elems_passthroughs = let pp_elem fmt (elem, passthroughs) =
let pp_sep fmt () = F.fprintf fmt "@." in F.fprintf
let pp_elem fmt (elem, passthroughs) = fmt
F.fprintf "|=> %a %a"
fmt CallSite.pp (elem_to_callsite elem) pp_passthroughs passthroughs in
"|=> %a %a" (F.pp_print_list ~pp_sep) pp_elem fmt elems_passthroughs in
CallSite.pp (elem_to_callsite elem) pp_passthroughs passthroughs in let pp_sources = pp_elems Source.call_site in
(F.pp_print_list ~pp_sep) pp_elem fmt elems_passthroughs in let pp_sinks = pp_elems Sink.call_site in
let pp_sources = pp_elems Source.call_site in
let pp_sinks = pp_elems Sink.call_site in let original_source = fst (IList.hd sources_passthroughs) in
let original_source = fst (IList.hd sources_passthroughs) in let final_sink = fst (IList.hd sinks_passthroughs) in
let final_sink = fst (IList.hd sinks_passthroughs) in F.fprintf
F.asprintf fmt
"Error: %a -> %a. Full trace:@.%a@.Current procedure %a %a@.%a" "Error: %a -> %a. Full trace:@.%a@.Current procedure %a %a@.%a"
Source.pp original_source Source.pp original_source
Sink.pp final_sink Sink.pp final_sink
pp_sources sources_passthroughs pp_sources sources_passthroughs
Procname.pp cur_pname Procname.pp cur_pname
pp_passthroughs cur_passthroughs pp_passthroughs cur_passthroughs
pp_sinks (IList.rev sinks_passthroughs) in pp_sinks (IList.rev sinks_passthroughs)
let get_trace_string source sink cur_passthroughs = let get_reportable_paths t ~trace_of_pname =
if expand_trace let expand_path source sink =
then let sources_of_pname pname =
let sources_of_pname pname = let trace = trace_of_pname pname in
let trace = trace_of_pname pname in Sources.elements (sources trace), passthroughs trace in
Sources.elements (sources trace), passthroughs trace in let sinks_of_pname pname =
let sinks_of_pname pname = let trace = trace_of_pname pname in
let trace = trace_of_pname pname in Sinks.elements (sinks trace), passthroughs trace in
Sinks.elements (sinks trace), passthroughs trace in let sources_passthroughs =
let sources_passthroughs = SourceExpander.expand source ~elems_passthroughs_of_pname:sources_of_pname in
SourceExpander.expand source ~elems_passthroughs_of_pname:sources_of_pname in let sinks_passthroughs =
let sinks_passthroughs = SinkExpander.expand sink ~elems_passthroughs_of_pname:sinks_of_pname in
SinkExpander.expand sink ~elems_passthroughs_of_pname:sinks_of_pname in sources_passthroughs, sinks_passthroughs in
get_expanded_trace_string cur_pname cur_passthroughs sources_passthroughs sinks_passthroughs
else
F.asprintf
"Error: %a -> %a %a"
Source.pp source Sink.pp sink pp_passthroughs cur_passthroughs in
IList.map IList.map
(fun (source, sink, passthroughs) -> (fun (source, sink, passthroughs) ->
let trace_string = get_trace_string source sink passthroughs in let sources_passthroughs, sinks_passthroughs = expand_path source sink in
source, sink, trace_string) passthroughs, sources_passthroughs, sinks_passthroughs)
(get_reports t) (get_reports t)
let of_source source = let of_source source =

@ -30,6 +30,11 @@ module type S = sig
module Sinks = Sink.Set module Sinks = Sink.Set
module Passthroughs = Passthrough.Set module Passthroughs = Passthrough.Set
(** path from a source to a sink with passthroughs at each step in the call stack. the first set
of passthroughs are the ones in the "reporting" procedure that calls the first function in
both the source and sink stack *)
type path = Passthroughs.t * (Source.t * Passthroughs.t) list * (Sink.t * Passthroughs.t) list
(** get the sources of the trace. *) (** get the sources of the trace. *)
val sources : t -> Sources.t val sources : t -> Sources.t
@ -42,13 +47,8 @@ module type S = sig
(** get the reportable source-sink flows in this trace *) (** get the reportable source-sink flows in this trace *)
val get_reports : t -> (Source.t * Sink.t * Passthroughs.t) list val get_reports : t -> (Source.t * Sink.t * Passthroughs.t) list
(** get logging-ready trace strings for the reportable source-sink flows in this trace *) (** get a path for each of the reportable source -> sink flows in this trace *)
val get_reportable_traces : val get_reportable_paths : t -> trace_of_pname:(Procname.t -> t) -> path list
t ->
Procname.t ->
?expand_trace:bool ->
trace_of_pname:(Procname.t -> t) ->
(Source.t * Sink.t * string) list
(** create a trace from a source *) (** create a trace from a source *)
val of_source : Source.t -> t val of_source : Source.t -> t
@ -73,6 +73,9 @@ module type S = sig
val equal : t -> t -> bool val equal : t -> t -> bool
val pp : F.formatter -> t -> unit val pp : F.formatter -> t -> unit
(** pretty-print a path in the context of the given procname *)
val pp_path : F.formatter -> Procname.t -> path -> unit
end end
module Make (Spec : Spec) : S with module Source = Spec.Source and module Sink = Spec.Sink module Make (Spec : Spec) : S with module Source = Spec.Source and module Sink = Spec.Sink

@ -157,20 +157,33 @@ module Make (TaintSpec : TaintSpec.S) = struct
| None -> | None ->
TraceDomain.initial in TraceDomain.initial in
let caller_pname = Cfg.Procdesc.get_proc_name proc_data.pdesc in let pp_path_short fmt (cur_passthroughs, sources_passthroughs, sinks_passthroughs) =
let expand_trace = false in let pp_passthroughs fmt passthroughs =
match TraceDomain.get_reportable_traces trace caller_pname ~expand_trace ~trace_of_pname with if not (Passthrough.Set.is_empty passthroughs)
then F.fprintf fmt "(via %a)" Passthrough.Set.pp passthroughs in
let source = fst (IList.hd (IList.rev sources_passthroughs)) in
let sink = fst (IList.hd (IList.rev sinks_passthroughs)) in
F.fprintf
fmt
"Error: %a -> %a %a"
TraceDomain.Source.pp source
TraceDomain.Sink.pp sink
pp_passthroughs cur_passthroughs in
match TraceDomain.get_reportable_paths trace ~trace_of_pname with
| [] -> | [] ->
trace trace
| reportable_trace_strs -> | paths ->
let caller_pname = Cfg.Procdesc.get_proc_name proc_data.pdesc in
let reported_sinks = let reported_sinks =
IList.map IList.map
(fun (_, sink, trace_str) -> (fun ((_, _, sinks) as path) ->
let msg = Localise.to_string Localise.quandary_taint_error in let msg = Localise.to_string Localise.quandary_taint_error in
let trace_str = F.asprintf "%a" pp_path_short path in
let exn = Exceptions.Checkers (msg, Localise.verbatim_desc trace_str) in let exn = Exceptions.Checkers (msg, Localise.verbatim_desc trace_str) in
Reporting.log_error caller_pname ~loc:(CallSite.loc callee_site) exn; Reporting.log_error caller_pname ~loc:(CallSite.loc callee_site) exn;
sink) fst (IList.hd (IList.rev sinks)))
reportable_trace_strs in paths in
(* got new source -> sink flow. report it, but don't add the sink to the trace. if we do, (* got new source -> sink flow. report it, but don't add the sink to the trace. if we do,
we will double-report later on. *) we will double-report later on. *)
TraceDomain.filter_sinks trace reported_sinks TraceDomain.filter_sinks trace reported_sinks

Loading…
Cancel
Save