From f99b30c4cd2a132b2dcab9a09d5bc049314430d3 Mon Sep 17 00:00:00 2001 From: Sam Blackshear Date: Mon, 15 Aug 2016 14:47:46 -0700 Subject: [PATCH] abstracting passthroughs more aggressively Reviewed By: jberdine Differential Revision: D3706040 fbshipit-source-id: 9d0f305 --- infer/src/Makefile | 2 +- infer/src/quandary/Sink.ml | 1 + infer/src/quandary/Trace.ml | 75 ++++++++++++---------- infer/src/quandary/TraceElem.ml | 4 +- infer/src/unit/TraceTests.ml | 107 ++++++++++++++++++++++++++++++++ infer/src/unit/inferunit.ml | 3 +- 6 files changed, 156 insertions(+), 36 deletions(-) create mode 100644 infer/src/unit/TraceTests.ml diff --git a/infer/src/Makefile b/infer/src/Makefile index 43d86423e..21e828ce8 100644 --- a/infer/src/Makefile +++ b/infer/src/Makefile @@ -128,7 +128,7 @@ else EXTRA_DEPS = opensource endif -DEPENDENCIES = IR backend checkers eradicate harness $(EXTRA_DEPS) +DEPENDENCIES = IR backend checkers eradicate harness quandary $(EXTRA_DEPS) # ocamlbuild command with options common to all build targets OCAMLBUILD_BASE = rebuild $(OCAMLBUILD_OPTIONS) -j $(NCPU) $(addprefix -I , $(DEPENDENCIES)) diff --git a/infer/src/quandary/Sink.ml b/infer/src/quandary/Sink.ml index 591f0ae88..0b4a0fa1d 100644 --- a/infer/src/quandary/Sink.ml +++ b/infer/src/quandary/Sink.ml @@ -11,6 +11,7 @@ module type S = sig include TraceElem.S val to_callee : t -> CallSite.t -> t + (** ith param * ith source kind *) val get : CallSite.t -> (int * t) list end diff --git a/infer/src/quandary/Trace.ml b/infer/src/quandary/Trace.ml index 4065d6b17..df927cf69 100644 --- a/infer/src/quandary/Trace.ml +++ b/infer/src/quandary/Trace.ml @@ -24,70 +24,81 @@ module type S = sig include Spec type t - (** get the source of the trace *) - val source : t -> Source.t + (** get the sources of the trace. this should never be empty *) + val sources : t -> Source.Set.t - (** get the sink of the trace, if any *) - val sink : t -> Sink.t option + (** get the sinks of the trace *) + val sinks : t -> Sink.Set.t - (** return true if this trace represent a dangerous flow *) - val should_report : t -> bool + (** get the reportable source-sink flows in this trace *) + val get_reports : t -> (Source.t * Sink.t * Passthrough.Set.t) list (** create a trace from a source *) val of_source : Source.t -> t + (** ad a source to the current trace *) + val add_source : Source.t -> t -> t + (** add a sink to the current trace. *) - val add_sink : t -> Sink.t -> t + val add_sink : Sink.t -> t -> t val compare : t -> t -> int + val pp : F.formatter -> t -> unit end module Make (Spec : Spec) = struct include Spec + module Sources = Source.Set + module Sinks = Sink.Set + type t = { - source : Source.t; (** last function in the trace that returned tainted data *) - sink : Sink.t option; - (** last callee in the trace that transitively called a tainted function (if any) *) + sources : Sources.t; (** last functions in the trace that returned tainted data *) + sinks : Sinks.t; + (** last callees in the trace that transitively called a tainted function (if any) *) passthroughs : Passthrough.Set.t; (** calls that occurred between source and sink *) } let compare t1 t2 = - let compare_sink_opts sink1_opt sink2_opt = match sink1_opt, sink2_opt with - | Some sink1, Some sink2 -> Sink.compare sink1 sink2 - | None, None -> 0 - | Some _, None -> (-1) - | None, Some _ -> 1 in - Source.compare t1.source t2.source - |> next compare_sink_opts t1.sink t2.sink + Sources.compare t1.sources t2.sources + |> next Sinks.compare t1.sinks t2.sinks |> next Passthrough.Set.compare t1.passthroughs t2.passthroughs let pp fmt t = - let pp_sink_opt fmt = function - | None -> F.fprintf fmt "?" - | Some sink -> Sink.pp fmt sink in F.fprintf fmt "%a -> %a via %a" - Source.pp t.source pp_sink_opt t.sink Passthrough.Set.pp t.passthroughs + Sources.pp t.sources Sinks.pp t.sinks Passthrough.Set.pp t.passthroughs - let source t = - t.source + let sources t = + t.sources - let sink t = - t.sink + let sinks t = + t.sinks - let should_report t = match t.sink with - | Some sink -> Spec.should_report t.source sink - | None -> false + let get_reports t = + if Sinks.is_empty t.sinks + then [] + else + let report_one source sink acc = + if Spec.should_report source sink + then (source, sink, t.passthroughs) :: acc + else acc in + Sources.fold (fun source acc -> Sinks.fold (report_one source) t.sinks acc) t.sources [] let of_source source = + let sources = Sources.singleton source in let passthroughs = Passthrough.Set.empty in - let sink = None in - { source; passthroughs; sink; } + let sinks = Sinks.empty in + { sources; passthroughs; sinks; } + + let add_source source t = + let sources = Sources.add source t.sources in + { t with sources; } - let add_sink t sink = - { t with sink = Some sink; } + let add_sink sink t = + let sinks = Sinks.add sink t.sinks in + { t with sinks; } end diff --git a/infer/src/quandary/TraceElem.ml b/infer/src/quandary/TraceElem.ml index 233a253b1..38f141b2b 100644 --- a/infer/src/quandary/TraceElem.ml +++ b/infer/src/quandary/TraceElem.ml @@ -15,12 +15,12 @@ module type S = sig val call_site : t -> CallSite.t val kind : t -> kind - val is_empty : t -> bool val make : kind -> CallSite.t -> t - val make_empty : CallSite.t -> t val compare : t -> t -> int val equal : t -> t -> bool val pp : F.formatter -> t -> unit + + module Set : PrettyPrintable.PPSet with type elt = t end diff --git a/infer/src/unit/TraceTests.ml b/infer/src/unit/TraceTests.ml new file mode 100644 index 000000000..a39b0a85e --- /dev/null +++ b/infer/src/unit/TraceTests.ml @@ -0,0 +1,107 @@ +(* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + *) + +open! Utils + +module L = Logging +module F = Format + +module MockTraceElem = struct + type kind = + | Kind1 + | Kind2 + + type t = kind + + let call_site _ = assert false + + let kind t = t + + let make kind _ = kind + + let compare t1 t2 = + match t1, t2 with + | Kind1, Kind1 -> 0 + | Kind1, _ -> (-1) + | _, Kind1 -> 1 + | Kind2, Kind2 -> 0 + + let equal t1 t2 = + compare t1 t2 = 0 + + let pp fmt = function + | Kind1 -> F.fprintf fmt "Kind1" + | Kind2 -> F.fprintf fmt "Kind2" + + module Set = PrettyPrintable.MakePPSet(struct + type nonrec t = t + let compare = compare + let pp_element = pp + end) + + let to_callee _ _ = assert false +end + +module MockSource = struct + include MockTraceElem + + let make : kind -> CallSite.t -> t = MockTraceElem.make + + let get _ = assert false + let is_footprint _ = assert false + let make_footprint _ = assert false + let get_footprint_access_path _ = assert false + let to_return _ _ = assert false +end + +module MockSink = struct + include MockTraceElem + + + let get _ = assert false +end + + +module MockTrace = Trace.Make(struct + module Source = MockSource + module Sink = MockSink + + let should_report source sink = + Source.kind source = Sink.kind sink + end) + +let tests = + let open OUnit2 in + let get_reports = + let get_reports_ _ = + let source1 = MockSource.make MockTraceElem.Kind1 CallSite.dummy in + let source2 = MockSource.make MockTraceElem.Kind2 CallSite.dummy in + let sink1 = MockSink.make MockTraceElem.Kind1 CallSite.dummy in + let sink2 = MockSink.make MockTraceElem.Kind2 CallSite.dummy in + let trace = + MockTrace.of_source source1 + |> MockTrace.add_source source2 + |> MockTrace.add_sink sink1 + |> MockTrace.add_sink sink2 in + let reports = MockTrace.get_reports trace in + + assert_equal (IList.length reports) 2; + assert_bool + "Reports should contain source1 -> sink1" + (IList.exists + (fun (source, sink, _) -> MockSource.equal source source1 && MockSink.equal sink sink1) + reports); + assert_bool + "Reports should contain source2 -> sink2" + (IList.exists + (fun (source, sink, _) -> MockSource.equal source source2 && MockSink.equal sink sink2) + reports) in + "get_reports">::get_reports_ in + + "trace_domain_suite">:::[get_reports] diff --git a/infer/src/unit/inferunit.ml b/infer/src/unit/inferunit.ml index 8a0107aea..5a7a52525 100644 --- a/infer/src/unit/inferunit.ml +++ b/infer/src/unit/inferunit.ml @@ -18,12 +18,13 @@ let () = AccessPathTests.tests; AccessTreeTests.tests; AddressTakenTests.tests; + BoundedCallTreeTests.tests; CopyPropagationTests.tests; ProcCfgTests.tests; LivenessTests.tests; SchedulerTests.tests; - BoundedCallTreeTests.tests; StacktraceTests.tests; + TraceTests.tests; ] in let test_suite = "all" >::: tests in OUnit2.run_test_tt_main test_suite