Reviewed By: jvillard Differential Revision: D20692190 fbshipit-source-id: e1ac5c454master
parent
a148812ac8
commit
8c82072cb0
@ -0,0 +1,171 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
module L = Logging
|
||||
|
||||
module VDom = AbstractDomain.Flat (JavaClassName)
|
||||
(** value domain, with the following concretization function [gamma]:
|
||||
|
||||
{[
|
||||
gamma(VDom.top) = { any value }
|
||||
gamma(VDom.v A) = { any ref of exact class A }
|
||||
gamma(VDom.bot) = emptyset
|
||||
]} *)
|
||||
|
||||
module CFG = ProcCfg.Normal
|
||||
module Domain = AbstractDomain.Map (Var) (VDom)
|
||||
|
||||
let get_var (astate : Domain.t) (v : Var.t) : VDom.t =
|
||||
match Domain.find_opt v astate with Some ab -> ab | None -> VDom.bottom
|
||||
|
||||
|
||||
let rec eval_expr (astate : Domain.t) (expr : Exp.t) : VDom.t =
|
||||
match expr with
|
||||
| Var id ->
|
||||
get_var astate (Var.of_id id)
|
||||
| UnOp _ | BinOp _ | Exn _ | Closure _ ->
|
||||
VDom.top
|
||||
| Const _ ->
|
||||
VDom.top (* could be more precise for Cstr and Cclass *)
|
||||
| Cast (_, e) ->
|
||||
eval_expr astate e (* could be more precise for final class *)
|
||||
| Lvar v ->
|
||||
get_var astate (Var.of_pvar v)
|
||||
| Lfield _ ->
|
||||
VDom.top (* could be more precise for final class *)
|
||||
| Lindex _ ->
|
||||
VDom.top
|
||||
| Sizeof _ ->
|
||||
VDom.top
|
||||
|
||||
|
||||
let eval_fun pname args =
|
||||
(* can be extended later if we decide to handle more builtins *)
|
||||
if Procname.equal pname BuiltinDecl.__new then
|
||||
match args with
|
||||
| (_, typ) :: _ when Typ.is_pointer typ -> (
|
||||
match Typ.name (Typ.strip_ptr typ) with Some (Typ.JavaClass cn) -> VDom.v cn | _ -> VDom.top )
|
||||
| _ ->
|
||||
VDom.top
|
||||
else VDom.top
|
||||
|
||||
|
||||
let eval_instr (astate : Domain.t) (instr : Sil.instr) : Domain.t =
|
||||
match instr with
|
||||
| Load {id} when Ident.is_none id ->
|
||||
astate
|
||||
| Load {id; e} ->
|
||||
let aval = eval_expr astate e in
|
||||
Domain.add (Var.of_id id) aval astate
|
||||
| Call ((id, _), Const (Const.Cfun pname), args, _, _) ->
|
||||
let aval = eval_fun pname args in
|
||||
Domain.add (Var.of_id id) aval astate
|
||||
| Call ((id, _), _, _, _, _) ->
|
||||
Domain.add (Var.of_id id) VDom.top astate
|
||||
| Store {e1= Lvar pvar; e2} ->
|
||||
let aval = eval_expr astate e2 in
|
||||
Domain.add (Var.of_pvar pvar) aval astate
|
||||
| Store _ | Prune _ | Metadata _ ->
|
||||
astate
|
||||
|
||||
|
||||
module TransferFunctions = struct
|
||||
module CFG = CFG
|
||||
module Domain = Domain
|
||||
|
||||
type analysis_data = unit
|
||||
|
||||
let exec_instr astate _ _node instr = eval_instr astate instr
|
||||
|
||||
let pp_session_name node fmt =
|
||||
Format.fprintf fmt "devirtualizer analysis %a" CFG.Node.pp_id (CFG.Node.id node)
|
||||
end
|
||||
|
||||
module Analyzer = AbstractInterpreter.MakeRPO (TransferFunctions)
|
||||
|
||||
let analyze_at_node (map : Analyzer.invariant_map) node : Domain.t =
|
||||
match Analyzer.InvariantMap.find_opt (Procdesc.Node.get_id node) map with
|
||||
| Some abstate ->
|
||||
abstate.pre
|
||||
| None ->
|
||||
Domain.bottom
|
||||
|
||||
|
||||
(* inspired from biabduction/Symexec.ml, function resolve_method *)
|
||||
let resolve_method tenv class_name proc_name =
|
||||
let method_exists pname methods = List.exists ~f:(Procname.equal pname) methods in
|
||||
let rec resolve class_name =
|
||||
let resolved_proc_name = Procname.replace_class proc_name class_name in
|
||||
match Tenv.lookup tenv class_name with
|
||||
| Some {methods; supers} when Typ.Name.is_class class_name -> (
|
||||
if method_exists resolved_proc_name methods then Some resolved_proc_name
|
||||
else match supers with super_classname :: _ -> resolve super_classname | _ -> None )
|
||||
| _ ->
|
||||
None
|
||||
in
|
||||
resolve class_name
|
||||
|
||||
|
||||
let process summary tenv =
|
||||
let pdesc = Summary.get_proc_desc summary in
|
||||
let node_cfg = CFG.from_pdesc pdesc in
|
||||
let all_params = Procdesc.get_pvar_formals pdesc in
|
||||
let initial =
|
||||
(* all params -> top, bottom otherwise *)
|
||||
(* we could use param type (if final) to get more precision *)
|
||||
List.fold_left ~init:Domain.empty
|
||||
~f:(fun acc (pvar, _) -> Domain.add (Var.of_pvar pvar) VDom.top acc)
|
||||
all_params
|
||||
in
|
||||
let map = Analyzer.exec_cfg node_cfg () ~initial in
|
||||
let is_virtual_call call_flags =
|
||||
call_flags.CallFlags.cf_virtual || call_flags.CallFlags.cf_interface
|
||||
in
|
||||
let replace_instr node (astate : Domain.t) (instr : Sil.instr) : Sil.instr =
|
||||
let kind = `ExecNode in
|
||||
let pp_name fmt = Format.pp_print_string fmt "devirtualizer" in
|
||||
NodePrinter.with_session (CFG.Node.underlying_node node) ~kind ~pp_name ~f:(fun () ->
|
||||
match instr with
|
||||
| Call
|
||||
( ret_id_typ
|
||||
, Const (Const.Cfun callee_pname)
|
||||
, ((this_expr, _) :: _ as actual_params)
|
||||
, loc
|
||||
, call_flags )
|
||||
when is_virtual_call call_flags -> (
|
||||
L.d_printfln "virtual call %a " Procname.pp callee_pname ;
|
||||
let aval = eval_expr astate this_expr in
|
||||
match VDom.get aval with
|
||||
| Some dyn_typ -> (
|
||||
match resolve_method tenv (Typ.JavaClass dyn_typ) callee_pname with
|
||||
| None ->
|
||||
L.d_printfln "(unexpected: no resolved method found)" ;
|
||||
instr
|
||||
| Some resolved_callee_pname ->
|
||||
let resolved_call_flags =
|
||||
{call_flags with cf_virtual= false; cf_interface= false}
|
||||
in
|
||||
L.d_printfln "replaced by nonvirtual <%a>\n" Procname.pp resolved_callee_pname ;
|
||||
Sil.Call
|
||||
( ret_id_typ
|
||||
, Const (Const.Cfun resolved_callee_pname)
|
||||
, actual_params
|
||||
, loc
|
||||
, resolved_call_flags ) )
|
||||
| _ ->
|
||||
Logging.debug Capture Verbose "unchanged\n" ;
|
||||
instr )
|
||||
| _ ->
|
||||
instr )
|
||||
in
|
||||
let update_context = eval_instr in
|
||||
let context_at_node node = analyze_at_node map node in
|
||||
let _has_changed : bool =
|
||||
Procdesc.replace_instrs_using_context pdesc ~f:replace_instr ~update_context ~context_at_node
|
||||
in
|
||||
()
|
@ -0,0 +1,13 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
(** {1 Simple devirtualization pre-analysis using a flow-sensitive tracking of dynamic classes} *)
|
||||
|
||||
open! IStd
|
||||
|
||||
val process : Summary.t -> Tenv.t -> unit
|
||||
(** Run the devirtualization pass by replacing some virtual calls by resolved calls *)
|
Loading…
Reference in new issue