intraprocedural taint analysis that records sources

Reviewed By: jvillard

Differential Revision: D3723819

fbshipit-source-id: 941e1fc
master
Sam Blackshear 8 years ago committed by Facebook Github Bot 8
parent dda4921786
commit 3b12208e1b

@ -13,6 +13,8 @@ module F = Format
(** Wrappers for making pretty-printable modules *)
val pp_collection : pp_item:(F.formatter -> 'a -> unit) -> F.formatter -> 'a list -> unit
module type SetOrderedType = sig
type t
val compare : t -> t -> int

@ -58,4 +58,64 @@ module Make (TraceDomain : Trace.S) = struct
F.fprintf fmt "(%a, %a)" TaintDomain.pp access_tree IdMapDomain.pp id_map
end
module TransferFunctions (CFG : ProcCfg.S) = struct
module CFG = CFG
module Domain = Domain
type formal_list = AccessPath.base list
type extras = formal_list
let add_source source ret_id ret_typ access_tree =
let trace = TraceDomain.of_source source in
let id_ap = AccessPath.Exact (AccessPath.of_id ret_id ret_typ) in
TaintDomain.add_trace id_ap trace access_tree
let exec_instr ({ Domain.access_tree; } as astate) proc_data _ instr =
match instr with
| Sil.Call (ret_ids, Const (Cfun callee_pname), _, callee_loc, _) ->
let call_site = CallSite.make callee_pname callee_loc in
let ret_typ =
match callee_pname with
| Procname.Java java_pname ->
let ret_typ_str = Procname.java_get_return_type java_pname in
Option.default
Typ.Tvoid
(Tenv.lookup_java_typ_from_string (proc_data.ProcData.tenv) ret_typ_str)
| Procname.C _ ->
Typ.Tvoid (* for tests only, since tests use C-style procnames *)
| _ ->
failwith "Unimp: looking up return type for non-Java procedure" in
let astate_with_source =
match TraceDomain.Source.get call_site, ret_ids with
| [(0, source)], [ret_id] ->
let access_tree' = add_source source ret_id ret_typ access_tree in
{ astate with Domain.access_tree = access_tree'; }
| [], _ | _, [] ->
astate
| _ ->
(* this is allowed by SIL, but not currently used in any frontends *)
failwith "Unimp: handling multiple return ids" in
astate_with_source
| Sil.Call _ ->
failwith "Unimp: non-pname call expressions"
| Sil.Letderef _ | Set _ ->
failwith "Unimp: assignment, load, and store"
| Sil.Prune _ | Remove_temps _ | Nullify _ | Abstract _ | Stackop _ | Declare_locals _ ->
astate
end
module Analyzer = AbstractInterpreter.Make
(ProcCfg.Normal)
(Scheduler.ReversePostorder)
(TransferFunctions)
let checker { Callbacks.proc_name; proc_desc; tenv; } =
let formals =
let attrs = Cfg.Procdesc.get_attributes proc_desc in
IList.map
(fun (name, typ) -> AccessPath.base_of_pvar (Pvar.mk name proc_name) typ)
attrs.formals in
let proc_data = ProcData.make proc_desc tenv formals in
ignore (Analyzer.compute_post proc_data)
end

@ -0,0 +1,128 @@
(*
* 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 MockTrace = Trace.Make(struct
module MockTraceElem = struct
type kind = unit
type t = CallSite.t
let call_site t = t
let kind _ = ()
let make _ site = site
let compare = CallSite.compare
let equal = CallSite.equal
let pp = CallSite.pp
let to_callee t _ = t
module Set = PrettyPrintable.MakePPSet(struct
type nonrec t = t
let compare = compare
let pp_element = pp
end)
end
module Source = struct
include MockTraceElem
let get site =
if string_is_prefix "SOURCE" (Procname.to_string (CallSite.pname site))
then [(0, site)]
else []
let is_footprint _ = assert false
let make_footprint _ = assert false
let get_footprint_access_path _ = assert false
let to_return _ _ = assert false
end
module Sink = struct
include MockTraceElem
let get site =
if string_is_prefix "SINK" (Procname.to_string (CallSite.pname site))
then [(0, site)]
else []
end
let should_report _ _ = true
end)
module MockTaintAnalysis = TaintAnalysis.Make(MockTrace)
module TestInterpreter = AnalyzerTester.Make
(ProcCfg.Normal)
(Scheduler.ReversePostorder)
(MockTaintAnalysis.TransferFunctions)
let tests =
let open OUnit2 in
let open AnalyzerTester.StructuredSil in
(* less verbose form of pretty-printing to make writing tests easy *)
let pp_sparse fmt astate =
let pp_call_site fmt call_site =
F.fprintf fmt "%a" Procname.pp (CallSite.pname call_site) in
let pp_sources fmt sources =
if MockTrace.Sources.is_empty sources
then F.fprintf fmt "?"
else
MockTrace.Sources.iter
(fun source -> pp_call_site fmt (MockTrace.Source.call_site source))
sources in
let pp_sinks fmt sinks =
if MockTrace.Sinks.is_empty sinks
then F.fprintf fmt "?"
else
MockTrace.Sinks.iter
(fun sink ->
pp_call_site fmt (MockTrace.Sink.call_site sink))
sinks in
(* just print source -> sink, no line nums or passthroughs *)
let pp_trace fmt trace =
F.fprintf
fmt
"(%a -> %a)"
pp_sources (MockTrace.sources trace)
pp_sinks (MockTrace.sinks trace) in
let pp_item fmt (ap, trace) =
F.fprintf fmt "%a => %a" AccessPath.pp ap pp_trace trace in
(* flatten access tree into list of access paths with associated traces *)
let trace_assocs =
MockTaintAnalysis.TaintDomain.fold
(fun acc ap t ->
if not (MockTrace.is_empty t)
then (ap, t) :: acc
else acc)
astate.MockTaintAnalysis.Domain.access_tree
[] in
PrettyPrintable.pp_collection ~pp_item fmt (IList.rev trace_assocs) in
let assign_to_source ret_str =
let procname = Procname.from_string_c_fun "SOURCE" in
make_call ~procname [ident_of_str ret_str] [] in
let assign_to_non_source ret_str =
let procname = Procname.from_string_c_fun "NON-SOURCE" in
make_call ~procname [ident_of_str ret_str] [] in
let assert_empty = invariant "{ }" in
let test_list = [
"source recorded",
[
assign_to_source "ret_id";
invariant "{ ret_id$0 => (SOURCE -> ?) }";
];
"non-source not recorded",
[
assign_to_non_source "ret_id";
assert_empty;
];
] |> TestInterpreter.create_tests ~pp_opt:pp_sparse [] in
"taint_test_suite">:::test_list

@ -24,6 +24,7 @@ let () =
LivenessTests.tests;
SchedulerTests.tests;
StacktraceTests.tests;
TaintTests.tests;
TraceTests.tests;
] in
let test_suite = "all" >::: tests in

Loading…
Cancel
Save