[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 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. *)
val sources : t -> Sources.t
@ -42,13 +47,8 @@ module type S = sig
(** get the reportable source-sink flows in this trace *)
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 *)
val get_reportable_traces :
t ->
Procname.t ->
?expand_trace:bool ->
trace_of_pname:(Procname.t -> t) ->
(Source.t * Sink.t * string) list
(** get a path for each of the reportable source -> sink flows in this trace *)
val get_reportable_paths : t -> trace_of_pname:(Procname.t -> t) -> path list
(** create a trace from a source *)
val of_source : Source.t -> t
@ -73,6 +73,9 @@ module type S = sig
val equal : t -> t -> bool
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
(** 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 path = Passthroughs.t * (Source.t * Passthroughs.t) list * (Sink.t * Passthroughs.t) list
let compare t1 t2 =
Sources.compare t1.sources t2.sources
|> next Sinks.compare t1.sinks t2.sinks
@ -161,13 +166,11 @@ module Make (Spec : Spec) = struct
else acc in
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 =
if not (Passthrough.Set.is_empty passthroughs)
then F.fprintf fmt "(via %a)" Passthrough.Set.pp passthroughs in
let get_expanded_trace_string
cur_pname cur_passthroughs sources_passthroughs sinks_passthroughs =
let pp_elems elem_to_callsite fmt elems_passthroughs =
let pp_sep fmt () = F.fprintf fmt "@." in
let pp_elem fmt (elem, passthroughs) =
@ -178,20 +181,21 @@ module Make (Spec : Spec) = struct
(F.pp_print_list ~pp_sep) pp_elem fmt elems_passthroughs 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 final_sink = fst (IList.hd sinks_passthroughs) in
F.asprintf
F.fprintf
fmt
"Error: %a -> %a. Full trace:@.%a@.Current procedure %a %a@.%a"
Source.pp original_source
Sink.pp final_sink
pp_sources sources_passthroughs
Procname.pp cur_pname
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 =
if expand_trace
then
let get_reportable_paths t ~trace_of_pname =
let expand_path source sink =
let sources_of_pname pname =
let trace = trace_of_pname pname in
Sources.elements (sources trace), passthroughs trace in
@ -202,16 +206,12 @@ module Make (Spec : Spec) = struct
SourceExpander.expand source ~elems_passthroughs_of_pname:sources_of_pname in
let sinks_passthroughs =
SinkExpander.expand sink ~elems_passthroughs_of_pname:sinks_of_pname 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
sources_passthroughs, sinks_passthroughs in
IList.map
(fun (source, sink, passthroughs) ->
let trace_string = get_trace_string source sink passthroughs in
source, sink, trace_string)
let sources_passthroughs, sinks_passthroughs = expand_path source sink in
passthroughs, sources_passthroughs, sinks_passthroughs)
(get_reports t)
let of_source source =

@ -30,6 +30,11 @@ module type S = sig
module Sinks = Sink.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. *)
val sources : t -> Sources.t
@ -42,13 +47,8 @@ module type S = sig
(** get the reportable source-sink flows in this trace *)
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 *)
val get_reportable_traces :
t ->
Procname.t ->
?expand_trace:bool ->
trace_of_pname:(Procname.t -> t) ->
(Source.t * Sink.t * string) list
(** get a path for each of the reportable source -> sink flows in this trace *)
val get_reportable_paths : t -> trace_of_pname:(Procname.t -> t) -> path list
(** create a trace from a source *)
val of_source : Source.t -> t
@ -73,6 +73,9 @@ module type S = sig
val equal : t -> t -> bool
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
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 ->
TraceDomain.initial in
let caller_pname = Cfg.Procdesc.get_proc_name proc_data.pdesc in
let expand_trace = false in
match TraceDomain.get_reportable_traces trace caller_pname ~expand_trace ~trace_of_pname with
let pp_path_short fmt (cur_passthroughs, sources_passthroughs, sinks_passthroughs) =
let pp_passthroughs fmt passthroughs =
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
| reportable_trace_strs ->
| paths ->
let caller_pname = Cfg.Procdesc.get_proc_name proc_data.pdesc in
let reported_sinks =
IList.map
(fun (_, sink, trace_str) ->
(fun ((_, _, sinks) as path) ->
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
Reporting.log_error caller_pname ~loc:(CallSite.loc callee_site) exn;
sink)
reportable_trace_strs in
fst (IList.hd (IList.rev sinks)))
paths in
(* 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. *)
TraceDomain.filter_sinks trace reported_sinks

Loading…
Cancel
Save