You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
3.6 KiB

(*
* 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 F = Format
module L = Logging
module type Spec = sig
module Source : Source.S
module Sink : Sink.S
(** should a flow originating at source and entering sink be reported? *)
val should_report : Source.t -> Sink.t -> bool
end
module type S = sig
include Spec
type t
type astate = t
include AbstractDomain.S with type astate := astate
(** get the sources of the trace. *)
val sources : t -> Source.Set.t
(** get the sinks of the trace *)
val sinks : t -> Sink.Set.t
(** 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 : Sink.t -> t -> t
(** return true if this trace has no source or sink data *)
val is_empty : t -> bool
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 =
{
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 *)
}
type astate = t
let compare t1 t2 =
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 =
F.fprintf
fmt
"%a -> %a via %a"
Sources.pp t.sources Sinks.pp t.sinks Passthrough.Set.pp t.passthroughs
let sources t =
t.sources
let sinks t =
t.sinks
let is_empty t =
(* sources empty => sinks empty and passthroughs empty *)
Sources.is_empty t.sources
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 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 sink t =
let sinks = Sinks.add sink t.sinks in
{ t with sinks; }
let initial =
let sources = Sources.empty in
let sinks = Sinks.empty in
let passthroughs = Passthrough.Set.empty in
{ sources; sinks; passthroughs; }
let (<=) ~lhs ~rhs =
lhs == rhs ||
(Sources.subset lhs.sources rhs.sources &&
Sinks.subset lhs.sinks rhs.sinks &&
Passthrough.Set.subset lhs.passthroughs rhs.passthroughs)
let join t1 t2 =
if t1 == t2
then t1
else
let sources = Sources.union t1.sources t2.sources in
let sinks = Sinks.union t1.sinks t2.sinks in
let passthroughs = Passthrough.Set.union t1.passthroughs t2.passthroughs in
{ sources; sinks; passthroughs; }
let widen ~prev ~next ~num_iters:_ =
join prev next
end