|
|
|
(*
|
|
|
|
* Copyright (c) 2017 - 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! IStd
|
|
|
|
module F = Format
|
|
|
|
module L = Logging
|
|
|
|
|
|
|
|
type call = Direct of Typ.Procname.t | Indirect of AccessPath.Raw.t [@@deriving compare]
|
|
|
|
|
|
|
|
let pp_call fmt = function
|
|
|
|
| Direct pname
|
|
|
|
-> Typ.Procname.pp fmt pname
|
|
|
|
| Indirect access_path
|
|
|
|
-> F.fprintf fmt "*%a" AccessPath.Raw.pp access_path
|
|
|
|
|
|
|
|
type t =
|
|
|
|
| Assign of AccessPath.Raw.t * HilExp.t * Location.t
|
|
|
|
| Assume of HilExp.t * [`Then | `Else] * Sil.if_kind * Location.t
|
|
|
|
| Call of AccessPath.base option * call * HilExp.t list * CallFlags.t * Location.t
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
let pp fmt = function
|
|
|
|
| Assign (access_path, exp, loc)
|
|
|
|
-> F.fprintf fmt "%a := %a [%a]" AccessPath.Raw.pp access_path HilExp.pp exp Location.pp loc
|
|
|
|
| Assume (exp, _, _, loc)
|
|
|
|
-> F.fprintf fmt "assume %a [%a]" HilExp.pp exp Location.pp loc
|
|
|
|
| Call (ret_opt, call, actuals, _, loc)
|
|
|
|
-> let pp_ret fmt = Option.iter ~f:(F.fprintf fmt "%a := " AccessPath.pp_base) in
|
|
|
|
let pp_actuals fmt = PrettyPrintable.pp_collection ~pp_item:HilExp.pp fmt in
|
|
|
|
F.fprintf fmt "%a%a(%a) [%a]" pp_ret ret_opt pp_call call pp_actuals actuals Location.pp loc
|
|
|
|
|
|
|
|
type translation = Instr of t | Bind of Var.t * AccessPath.Raw.t | Unbind of Var.t list | Ignore
|
|
|
|
|
|
|
|
(* convert an SIL instruction into an HIL instruction. the [f_resolve_id] function should map an SSA
|
|
|
|
temporary variable to the access path it represents. evaluating the HIL instruction should
|
|
|
|
produce the same result as evaluating the SIL instruction and replacing the temporary variables
|
|
|
|
using [f_resolve_id]. *)
|
|
|
|
let of_sil ~f_resolve_id (instr: Sil.instr) =
|
|
|
|
let analyze_id_assignment lhs_id rhs_exp rhs_typ loc =
|
|
|
|
let rhs_hil_exp = HilExp.of_sil ~f_resolve_id rhs_exp rhs_typ in
|
|
|
|
match HilExp.get_access_paths rhs_hil_exp with
|
|
|
|
| [rhs_access_path]
|
|
|
|
-> Bind (lhs_id, rhs_access_path)
|
|
|
|
| _
|
|
|
|
-> Instr (Assign (((lhs_id, rhs_typ), []), rhs_hil_exp, loc))
|
|
|
|
in
|
|
|
|
match instr with
|
|
|
|
| Load (lhs_id, rhs_exp, rhs_typ, loc)
|
|
|
|
-> analyze_id_assignment (Var.of_id lhs_id) rhs_exp rhs_typ loc
|
|
|
|
| Store (Lvar lhs_pvar, lhs_typ, rhs_exp, loc) when Pvar.is_ssa_frontend_tmp lhs_pvar
|
|
|
|
-> analyze_id_assignment (Var.of_pvar lhs_pvar) rhs_exp lhs_typ loc
|
|
|
|
| Call
|
|
|
|
( Some (ret_id, _)
|
|
|
|
, Const Cfun callee_pname
|
|
|
|
, (target_exp, _) :: (Sizeof {typ= cast_typ}, _) :: _
|
|
|
|
, loc
|
|
|
|
, _ )
|
|
|
|
when Typ.Procname.equal callee_pname BuiltinDecl.__cast
|
|
|
|
-> analyze_id_assignment (Var.of_id ret_id) target_exp cast_typ loc
|
|
|
|
| Store (lhs_exp, typ, rhs_exp, loc)
|
|
|
|
-> let lhs_access_path =
|
|
|
|
match HilExp.of_sil ~f_resolve_id lhs_exp typ with
|
|
|
|
| AccessPath ap
|
|
|
|
-> ap
|
|
|
|
| BinaryOperator (_, exp0, exp1) -> (
|
|
|
|
match
|
|
|
|
(* pointer arithmetic. somewhere in one of the expressions, there should be at least
|
|
|
|
one pointer type represented as an access path. just use that access path and forget
|
|
|
|
about the arithmetic. if you need to model this more precisely, you should be using
|
|
|
|
SIL instead *)
|
|
|
|
HilExp.get_access_paths exp0
|
|
|
|
with
|
|
|
|
| ap :: _
|
|
|
|
-> ap
|
|
|
|
| [] ->
|
|
|
|
match HilExp.get_access_paths exp1 with
|
|
|
|
| ap :: _
|
|
|
|
-> ap
|
|
|
|
| []
|
|
|
|
-> invalid_argf "Invalid pointer arithmetic expression %a used as LHS" Exp.pp lhs_exp
|
|
|
|
)
|
|
|
|
| _
|
|
|
|
-> invalid_argf "Non-assignable LHS expression %a" Exp.pp lhs_exp
|
|
|
|
in
|
|
|
|
Instr (Assign (lhs_access_path, HilExp.of_sil ~f_resolve_id rhs_exp typ, loc))
|
|
|
|
| Call (ret_opt, call_exp, formals, loc, call_flags)
|
|
|
|
-> let hil_ret = Option.map ~f:(fun (ret_id, ret_typ) -> (Var.of_id ret_id, ret_typ)) ret_opt in
|
|
|
|
let hil_call =
|
|
|
|
match HilExp.of_sil ~f_resolve_id call_exp (Typ.mk Tvoid) with
|
|
|
|
| Constant Cfun procname
|
|
|
|
-> Direct procname
|
|
|
|
| AccessPath access_path
|
|
|
|
-> Indirect access_path
|
|
|
|
| call_exp
|
|
|
|
-> invalid_argf "Unexpected call expression %a" HilExp.pp call_exp
|
|
|
|
in
|
|
|
|
let formals = List.map ~f:(fun (exp, typ) -> HilExp.of_sil ~f_resolve_id exp typ) formals in
|
|
|
|
Instr (Call (hil_ret, hil_call, formals, call_flags, loc))
|
|
|
|
| Prune (exp, loc, true_branch, if_kind)
|
|
|
|
-> let hil_exp = HilExp.of_sil ~f_resolve_id exp (Typ.mk (Tint IBool)) in
|
|
|
|
let branch = if true_branch then `Then else `Else in
|
|
|
|
Instr (Assume (hil_exp, branch, if_kind, loc))
|
|
|
|
| Nullify (pvar, _)
|
|
|
|
-> Unbind [Var.of_pvar pvar]
|
|
|
|
| Remove_temps (ids, _)
|
|
|
|
-> Unbind (List.map ~f:Var.of_id ids)
|
|
|
|
(* ignoring for now; will translate as builtin function call if needed *)
|
|
|
|
| Abstract _
|
|
|
|
| Declare_locals _
|
|
|
|
-> (* these don't seem useful for most analyses. can translate them later if we want to *)
|
|
|
|
Ignore
|