|
|
|
(*
|
|
|
|
* 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
|
|
|
|
|
|
|
|
(** Create a taint analysis from a trace domain *)
|
|
|
|
module Make (TraceDomain : Trace.S) = struct
|
|
|
|
|
|
|
|
module TaintDomain = AccessTree.Make (TraceDomain)
|
|
|
|
module IdMapDomain = IdAccessPathMapDomain
|
|
|
|
|
|
|
|
module Domain = struct
|
|
|
|
type astate =
|
|
|
|
{
|
|
|
|
access_tree : TaintDomain.astate; (* mapping of access paths to trace sets *)
|
|
|
|
id_map : IdMapDomain.astate; (* mapping of id's to access paths for normalization *)
|
|
|
|
}
|
|
|
|
|
|
|
|
let initial =
|
|
|
|
let access_tree = TaintDomain.initial in
|
|
|
|
let id_map = IdMapDomain.initial in
|
|
|
|
{ access_tree; id_map; }
|
|
|
|
|
|
|
|
let (<=) ~lhs ~rhs =
|
|
|
|
if lhs == rhs
|
|
|
|
then true
|
|
|
|
else
|
|
|
|
TaintDomain.(<=) ~lhs:lhs.access_tree ~rhs:rhs.access_tree &&
|
|
|
|
IdMapDomain.(<=) ~lhs:lhs.id_map ~rhs:rhs.id_map
|
|
|
|
|
|
|
|
let join astate1 astate2 =
|
|
|
|
if astate1 == astate2
|
|
|
|
then astate1
|
|
|
|
else
|
|
|
|
let access_tree = TaintDomain.join astate1.access_tree astate2.access_tree in
|
|
|
|
let id_map = IdMapDomain.join astate1.id_map astate2.id_map in
|
|
|
|
{ access_tree; id_map; }
|
|
|
|
|
|
|
|
let widen ~prev ~next ~num_iters =
|
|
|
|
if prev == next
|
|
|
|
then prev
|
|
|
|
else
|
|
|
|
let access_tree =
|
|
|
|
TaintDomain.widen ~prev:prev.access_tree ~next:next.access_tree ~num_iters in
|
|
|
|
let id_map = IdMapDomain.widen ~prev:prev.id_map ~next:next.id_map ~num_iters in
|
|
|
|
{ access_tree; id_map; }
|
|
|
|
|
|
|
|
let pp fmt { access_tree; id_map; } =
|
|
|
|
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
|