[HIL] Access expression

Summary: Preparing to extend HIL with Dereference and AddressOf expressions. Next steps: (1) change SIL -> HIL translation to preserve address of and dereference; (2) adapt analyses based on HIL to make use access expressions.

Reviewed By: jeremydubreil

Differential Revision: D6961928

fbshipit-source-id: 51da919
master
Daiva Naudziuniene 7 years ago committed by Facebook Github Bot
parent 2288e66063
commit 896e849bfc

@ -0,0 +1,39 @@
(*
* Copyright (c) 2018 - 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
type t =
| Base of AccessPath.base
| Offset of t * AccessPath.access
(* field/array access *)
| AddressOf of t
(* address of operator & *)
| Dereference of t
(* dereference operator * *)
[@@deriving compare]
(** convert to an AccessPath.t, ignoring AddressOf and Dereference for now *)
let rec to_access_path t =
match t with
| Base base ->
(base, [])
| Offset (ae, acc) ->
AccessPath.append (to_access_path ae) [acc]
| AddressOf ae ->
to_access_path ae
| Dereference ae ->
to_access_path ae
let of_access_path (base, accesses) =
let rec add_access accesses ae =
match accesses with [] -> ae | access :: rest -> add_access rest (Offset (ae, access))
in
add_access accesses (Base base)

@ -0,0 +1,24 @@
(*
* Copyright (c) 2018 - 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
type t =
| Base of AccessPath.base
| Offset of t * AccessPath.access
(* field/array access *)
| AddressOf of t
(* & *)
| Dereference of t
(* * *)
[@@deriving compare]
val to_access_path : t -> AccessPath.t
val of_access_path : AccessPath.t -> t

@ -12,7 +12,7 @@ module F = Format
module L = Logging
type t =
| AccessPath of AccessPath.t
| AccessExpression of AccessExpression.t
| UnaryOperator of Unop.t * t * Typ.t option
| BinaryOperator of Binop.t * t * t
| Exception of t
@ -23,8 +23,8 @@ type t =
[@@deriving compare]
let rec pp fmt = function
| AccessPath access_path ->
AccessPath.pp fmt access_path
| AccessExpression access_expr ->
AccessPath.pp fmt (AccessExpression.to_access_path access_expr)
| UnaryOperator (op, e, _) ->
F.fprintf fmt "%s%a" (Unop.str op) pp e
| BinaryOperator (op, e1, e2) ->
@ -43,8 +43,8 @@ let rec pp fmt = function
let rec get_typ tenv = function
| AccessPath access_path ->
AccessPath.get_typ access_path tenv
| AccessExpression access_expr ->
AccessPath.get_typ (AccessExpression.to_access_path access_expr) tenv
| UnaryOperator (_, _, typ_opt) ->
typ_opt
| BinaryOperator ((Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr), _, _) ->
@ -86,8 +86,8 @@ let rec get_typ tenv = function
let get_access_paths exp0 =
let rec get_access_paths_ exp acc =
match exp with
| AccessPath ap ->
ap :: acc
| AccessExpression ae ->
AccessExpression.to_access_path ae :: acc
| Cast (_, e) | UnaryOperator (_, e, _) | Exception e | Sizeof (_, Some e) ->
get_access_paths_ e acc
| BinaryOperator (_, e1, e2) ->
@ -113,7 +113,7 @@ let of_sil ~include_array_indexes ~f_resolve_id exp typ =
| None ->
AccessPath.of_id id typ
in
AccessPath ap
AccessExpression (AccessExpression.of_access_path ap)
| UnOp (op, e, typ_opt) ->
UnaryOperator (op, of_sil_ e typ, typ_opt)
| BinOp (op, e0, e1) ->
@ -136,7 +136,7 @@ let of_sil ~include_array_indexes ~f_resolve_id exp typ =
| Lfield (root_exp, fld, root_exp_typ) -> (
match AccessPath.of_lhs_exp ~include_array_indexes exp typ ~f_resolve_id with
| Some access_path ->
AccessPath access_path
AccessExpression (AccessExpression.of_access_path access_path)
| None ->
(* unsupported field expression: represent with a dummy variable *)
of_sil_
@ -153,7 +153,7 @@ let of_sil ~include_array_indexes ~f_resolve_id exp typ =
| Lindex (root_exp, index_exp) -> (
match AccessPath.of_lhs_exp ~include_array_indexes exp typ ~f_resolve_id with
| Some access_path ->
AccessPath access_path
AccessExpression (AccessExpression.of_access_path access_path)
| None ->
(* unsupported index expression: represent with a dummy variable *)
of_sil_
@ -163,7 +163,7 @@ let of_sil ~include_array_indexes ~f_resolve_id exp typ =
| Lvar _ ->
match AccessPath.of_lhs_exp ~include_array_indexes exp typ ~f_resolve_id with
| Some access_path ->
AccessPath access_path
AccessExpression (AccessExpression.of_access_path access_path)
| None ->
L.(die InternalError) "Couldn't convert var expression %a to access path" Exp.pp exp
in

@ -11,7 +11,7 @@ open! IStd
module F = Format
type t =
| AccessPath of AccessPath.t (** access path (e.g., x.f.g or x[i]) *)
| AccessExpression of AccessExpression.t (** access path (e.g., x.f.g or x[i]) *)
| UnaryOperator of Unop.t * t * Typ.t option
(** Unary operator with type of the result if known *)
| BinaryOperator of Binop.t * t * t (** Binary operator *)

@ -48,8 +48,8 @@ let of_sil ~include_array_indexes ~f_resolve_id (instr: Sil.instr) =
let analyze_id_assignment lhs_id rhs_exp rhs_typ loc =
let rhs_hil_exp = exp_of_sil rhs_exp rhs_typ in
match rhs_hil_exp with
| AccessPath rhs_access_path ->
Bind (lhs_id, rhs_access_path)
| AccessExpression rhs_access_expr ->
Bind (lhs_id, AccessExpression.to_access_path rhs_access_expr)
| _ ->
Instr (Assign (((lhs_id, rhs_typ), []), rhs_hil_exp, loc))
in
@ -69,8 +69,8 @@ let of_sil ~include_array_indexes ~f_resolve_id (instr: Sil.instr) =
| Store (lhs_exp, typ, rhs_exp, loc) ->
let lhs_access_path =
match exp_of_sil lhs_exp typ with
| AccessPath ap ->
ap
| AccessExpression access_expr ->
AccessExpression.to_access_path access_expr
| BinaryOperator (_, exp0, exp1) -> (
match
(* pointer arithmetic. somewhere in one of the expressions, there should be at least
@ -107,8 +107,8 @@ let of_sil ~include_array_indexes ~f_resolve_id (instr: Sil.instr) =
match exp_of_sil call_exp (Typ.mk Tvoid) with
| Constant Cfun procname | Closure (procname, _) ->
Direct procname
| AccessPath access_path ->
Indirect access_path
| AccessExpression access_expr ->
Indirect (AccessExpression.to_access_path access_expr)
| call_exp ->
L.(die InternalError) "Unexpected call expression %a" HilExp.pp call_exp
in

@ -70,7 +70,10 @@ struct
(fun id access_path astate_acc ->
let lhs_access_path = ((id, Typ.mk Typ.Tvoid), []) in
let dummy_assign =
HilInstr.Assign (lhs_access_path, HilExp.AccessPath access_path, loc)
HilInstr.Assign
( lhs_access_path
, HilExp.AccessExpression (AccessExpression.of_access_path access_path)
, loc )
in
TransferFunctions.exec_instr astate_acc extras node dummy_assign )
id_map actual_state

@ -194,8 +194,11 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
match Var.get_footprint_index var with
| Some footprint_index -> (
match List.nth actuals footprint_index with
| Some HilExp.AccessPath actual_access_path ->
Some (Domain.LocalAccessPath.make actual_access_path caller_pname)
| Some HilExp.AccessExpression actual_access_expr ->
Some
(Domain.LocalAccessPath.make
(AccessExpression.to_access_path actual_access_expr)
caller_pname)
| _ ->
None )
| None ->
@ -218,11 +221,13 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| Call
( (Some return_base as ret_opt)
, Direct (Typ.Procname.Java java_callee_procname as callee_procname)
, ((HilExp.AccessPath receiver_ap) :: _ as actuals)
, ((HilExp.AccessExpression receiver_ae) :: _ as actuals)
, _
, _ ) ->
let summary = Summary.read_summary proc_data.pdesc callee_procname in
let receiver = Domain.LocalAccessPath.make receiver_ap caller_pname in
let receiver =
Domain.LocalAccessPath.make (AccessExpression.to_access_path receiver_ae) caller_pname
in
if ( LithoFramework.is_component_builder callee_procname proc_data.tenv
(* track Builder's in order to check required prop's *)
|| GraphQLGetters.is_function callee_procname summary
@ -247,13 +252,15 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| Call (ret_opt, Direct callee_procname, actuals, _, _) ->
let summary = Summary.read_summary proc_data.pdesc callee_procname in
apply_callee_summary summary caller_pname ret_opt actuals astate
| Assign (lhs_ap, HilExp.AccessPath rhs_ap, _)
| Assign (lhs_ap, HilExp.AccessExpression rhs_ae, _)
-> (
(* creating an alias for the rhs binding; assume all reads will now occur through the
alias. this helps us keep track of chains in cases like tmp = getFoo(); x = tmp;
tmp.getBar() *)
let lhs_access_path = Domain.LocalAccessPath.make lhs_ap caller_pname in
let rhs_access_path = Domain.LocalAccessPath.make rhs_ap caller_pname in
let rhs_access_path =
Domain.LocalAccessPath.make (AccessExpression.to_access_path rhs_ae) caller_pname
in
try
let call_set = Domain.find rhs_access_path astate in
Domain.remove rhs_access_path astate |> Domain.add lhs_access_path call_set

@ -219,7 +219,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
astate
| [arg] when HilExp.is_null_literal arg ->
astate
| (HilExp.AccessPath ap) :: other_args ->
| (HilExp.AccessExpression access_expr) :: other_args ->
let ap = AccessExpression.to_access_path access_expr in
check_nil_in_objc_container proc_data loc other_args (check_ap proc_data loc ap astate)
| _ :: other_args ->
check_nil_in_objc_container proc_data loc other_args astate
@ -245,9 +246,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
when NullCheckedPname.mem callee_pname checked_pnames ->
(* Do not report nullable when the method has already been checked for null *)
remove_nullable_ap (ret_var, []) astate
| Call (_, Direct callee_pname, (HilExp.AccessPath receiver) :: _, _, _)
| Call (_, Direct callee_pname, (HilExp.AccessExpression receiver) :: _, _, _)
when Models.is_check_not_null callee_pname ->
assume_pnames_notnull receiver astate
assume_pnames_notnull (AccessExpression.to_access_path receiver) astate
| Call (_, Direct callee_pname, _, _, _) when is_blacklisted_method callee_pname ->
astate
| Call (Some ret_var, Direct callee_pname, _, _, loc)
@ -255,19 +256,19 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
~attrs_of_pname:Specs.proc_resolve_attributes Annotations.ia_is_nullable ->
let call_site = CallSite.make callee_pname loc in
add_nullable_ap (ret_var, []) (CallSites.singleton call_site) astate
| Call (_, Direct callee_pname, (HilExp.AccessPath receiver) :: _, _, loc)
| Call (_, Direct callee_pname, (HilExp.AccessExpression receiver) :: _, _, loc)
when is_non_objc_instance_method callee_pname ->
check_ap proc_data loc receiver astate
check_ap proc_data loc (AccessExpression.to_access_path receiver) astate
| Call (_, Direct callee_pname, args, _, loc) when is_objc_container_add_method callee_pname ->
check_nil_in_objc_container proc_data loc args astate
| Call
( Some ((_, ret_typ) as ret_var)
, Direct callee_pname
, (HilExp.AccessPath receiver) :: _
, (HilExp.AccessExpression receiver) :: _
, _
, _ )
when Typ.is_pointer ret_typ && is_objc_instance_method callee_pname -> (
match longest_nullable_prefix receiver astate with
match longest_nullable_prefix (AccessExpression.to_access_path receiver) astate with
| None ->
astate
| Some (_, call_sites) ->
@ -285,9 +286,10 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
report_nullable_dereference nullable_ap call_sites proc_data loc )
(longest_nullable_prefix lhs astate) ;
match rhs with
| HilExp.AccessPath ap -> (
| HilExp.AccessExpression access_expr -> (
try
(* Add the lhs to the list of nullable values if the rhs is nullable *)
let ap = AccessExpression.to_access_path access_expr in
add_nullable_ap lhs (find_nullable_ap ap astate) astate
with Not_found ->
(* Remove the lhs from the list of nullable values if the rhs is not nullable *)
@ -295,24 +297,26 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| _ ->
(* Remove the lhs from the list of nullable values if the rhs is not an access path *)
remove_nullable_ap lhs astate )
| Assume (HilExp.AccessPath ap, _, _, _) ->
assume_pnames_notnull ap astate
| Assume (HilExp.AccessExpression access_expr, _, _, _) ->
assume_pnames_notnull (AccessExpression.to_access_path access_expr) astate
| Assume
( ( HilExp.BinaryOperator (Binop.Ne, HilExp.AccessPath ap, exp)
| HilExp.BinaryOperator (Binop.Ne, exp, HilExp.AccessPath ap) )
( ( HilExp.BinaryOperator (Binop.Ne, HilExp.AccessExpression access_expr, exp)
| HilExp.BinaryOperator (Binop.Ne, exp, HilExp.AccessExpression access_expr) )
, _
, _
, _ )
| Assume
( HilExp.UnaryOperator
( Unop.LNot
, ( HilExp.BinaryOperator (Binop.Eq, HilExp.AccessPath ap, exp)
| HilExp.BinaryOperator (Binop.Eq, exp, HilExp.AccessPath ap) )
, ( HilExp.BinaryOperator (Binop.Eq, HilExp.AccessExpression access_expr, exp)
| HilExp.BinaryOperator (Binop.Eq, exp, HilExp.AccessExpression access_expr) )
, _ )
, _
, _
, _ ) ->
if HilExp.is_null_literal exp then assume_pnames_notnull ap astate else astate
if HilExp.is_null_literal exp then
assume_pnames_notnull (AccessExpression.to_access_path access_expr) astate
else astate
| _ ->
astate
end

@ -57,8 +57,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
match exp with
| HilExp.Constant Cint n when IntLit.isnull n ->
Some (UseDefChain.NullDefAssign (loc, lhs))
| HilExp.AccessPath ap -> (
| HilExp.AccessExpression access_expr -> (
try
let ap = AccessExpression.to_access_path access_expr in
match Domain.find ap astate with
| UseDefChain.NullDefCompare _ ->
(* Stop NullDefCompare from propagating here because we want to prevent
@ -73,9 +74,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
let extract_null_compare_expr = function
| HilExp.BinaryOperator ((Eq | Ne), HilExp.AccessPath ap, exp)
| HilExp.BinaryOperator ((Eq | Ne), exp, HilExp.AccessPath ap) ->
Option.some_if (HilExp.is_null_literal exp) ap
| HilExp.BinaryOperator ((Eq | Ne), HilExp.AccessExpression access_expr, exp)
| HilExp.BinaryOperator ((Eq | Ne), exp, HilExp.AccessExpression access_expr) ->
Option.some_if (HilExp.is_null_literal exp) (AccessExpression.to_access_path access_expr)
| _ ->
None

@ -96,10 +96,12 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
List.iteri
~f:(fun idx e ->
match e with
| HilExp.AccessPath ((var, t), al)
when should_report_var pdesc tenv uninit_vars ((var, t), al) && not (Typ.is_pointer t)
&& not (is_struct_field_passed_by_ref call t al idx) ->
report_intra ((var, t), al) loc (snd extras)
| HilExp.AccessExpression access_expr ->
let (var, t), al = AccessExpression.to_access_path access_expr in
if should_report_var pdesc tenv uninit_vars ((var, t), al) && not (Typ.is_pointer t)
&& not (is_struct_field_passed_by_ref call t al idx)
then report_intra ((var, t), al) loc (snd extras)
else ()
| _ ->
() )
actuals
@ -205,7 +207,11 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
else astate.prepost
in
match instr with
| Assign ((((lhs_var, lhs_typ), apl) as lhs_ap), (HilExp.AccessPath (rhs_base, al) as rhs), loc) ->
| Assign
( (((lhs_var, lhs_typ), apl) as lhs_ap)
, (HilExp.AccessExpression access_expr as rhs_expr)
, loc ) ->
let rhs_base, al = AccessExpression.to_access_path access_expr in
let uninit_vars' = D.remove lhs_ap astate.uninit_vars in
let uninit_vars =
if Int.equal (List.length apl) 0 then
@ -213,7 +219,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
remove_all_fields tenv (lhs_var, lhs_typ) uninit_vars'
else uninit_vars'
in
let prepost = update_prepost lhs_ap rhs in
let prepost = update_prepost lhs_ap rhs_expr in
(* check on lhs_typ to avoid false positive when assigning a pointer to another *)
if should_report_var pdesc tenv uninit_vars (rhs_base, al) && not (Typ.is_pointer lhs_typ)
then report_intra (rhs_base, al) loc (snd extras) ;
@ -234,23 +240,25 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
List.foldi
~f:(fun idx acc actual_exp ->
match actual_exp with
| HilExp.AccessPath (((_, {Typ.desc= Tarray _}) as base), al)
when is_blacklisted_function call ->
| HilExp.AccessExpression access_expr -> (
match AccessExpression.to_access_path access_expr with
| ((_, {Typ.desc= Tarray _}) as base), al when is_blacklisted_function call ->
D.remove (base, al) acc
| HilExp.AccessPath (((_, t) as base), al)
when is_struct_field_passed_by_ref call t al idx ->
| ((_, t) as base), al when is_struct_field_passed_by_ref call t al idx ->
(* Access to a field of a struct by reference *)
if Config.uninit_interproc then
remove_initialized_params pdesc call acc idx (base, al) false
else D.remove (base, al) acc
| HilExp.AccessPath ap when Typ.Procname.is_constructor call ->
| ap when Typ.Procname.is_constructor call ->
remove_all_fields tenv (fst ap) (D.remove ap acc)
| HilExp.AccessPath (((_, {Typ.desc= Tptr _}) as base), al) ->
| ((_, {Typ.desc= Tptr _}) as base), al ->
if Config.uninit_interproc then
remove_initialized_params pdesc call acc idx (base, al) true
else
let acc' = D.remove (base, al) acc in
remove_all_fields tenv base acc'
| _ ->
acc )
| HilExp.Closure (_, apl) ->
(* remove the captured variables of a block/lambda *)
List.fold ~f:(fun acc' (base, _) -> D.remove (base, []) acc') ~init:acc apl

@ -33,8 +33,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
let open HilExp in
let open Domain in
match e with
| HilExp.AccessPath ap -> (
try AttributeMapDomain.find ap attribute_map with Not_found -> AttributeSetDomain.empty )
| HilExp.AccessExpression access_expr -> (
try AttributeMapDomain.find (AccessExpression.to_access_path access_expr) attribute_map
with Not_found -> AttributeSetDomain.empty )
| Constant _ ->
AttributeSetDomain.of_list [Attribute.Functional]
| Exception expr (* treat exceptions as transparent wrt attributes *) | Cast (_, expr) ->
@ -53,8 +54,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
let open Domain in
let open HilExp in
match expr with
| AccessPath ap ->
OwnershipDomain.get_owned ap ownership
| AccessExpression access_expr ->
OwnershipDomain.get_owned (AccessExpression.to_access_path access_expr) ownership
| Constant _ ->
OwnershipAbstractValue.owned
| Exception e (* treat exceptions as transparent wrt ownership *) | Cast (_, e) ->
@ -102,7 +103,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
let ret_access_path = (ret, []) in
let get_ownership formal_index acc =
match List.nth actuals formal_index with
| Some HilExp.AccessPath actual_ap ->
| Some HilExp.AccessExpression access_expr ->
let actual_ap = AccessExpression.to_access_path access_expr in
OwnershipDomain.get_owned actual_ap ownership |> OwnershipAbstractValue.join acc
| Some HilExp.Constant _ ->
acc
@ -376,8 +378,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
let open RacerDConfig in
let get_receiver_ap actuals =
match List.hd actuals with
| Some HilExp.AccessPath receiver_ap ->
receiver_ap
| Some HilExp.AccessExpression receiver_expr ->
AccessExpression.to_access_path receiver_expr
| _ ->
L.(die InternalError)
"Call to %a is marked as a container write, but has no receiver" Typ.Procname.pp
@ -428,8 +430,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
if AccessDomain.is_empty accesses then accesses
else
let rec get_access_path = function
| HilExp.AccessPath ap ->
Some ap
| HilExp.AccessExpression access_expr ->
Some (AccessExpression.to_access_path access_expr)
| HilExp.Cast (_, e) | HilExp.Exception e ->
get_access_path e
| _ ->
@ -479,8 +481,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| HilExp.Constant _ ->
(* the actual is a constant, so it's owned in the caller. *)
Conjunction actual_indexes
| HilExp.AccessPath actual_access_path
| HilExp.AccessExpression access_expr
-> (
let actual_access_path = AccessExpression.to_access_path access_expr in
if OwnershipDomain.is_owned actual_access_path caller_astate.ownership then
(* the actual passed to the current callee is owned. drop all the conditional accesses
for that actual, since they're all safe *)
@ -661,15 +664,18 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
in
if is_box callee_pname then
match (ret_opt, actuals) with
| Some ret, (HilExp.AccessPath actual_ap) :: _
when AttributeMapDomain.has_attribute actual_ap Functional
astate.attribute_map ->
| Some ret, (HilExp.AccessExpression actual_access_expr) :: _ ->
let actual_ap = AccessExpression.to_access_path actual_access_expr in
if AttributeMapDomain.has_attribute actual_ap Functional
astate.attribute_map
then
(* TODO: check for constants, which are functional? *)
let attribute_map =
AttributeMapDomain.add_attribute (ret, []) Functional
astate.attribute_map
in
{astate with attribute_map}
else astate
| _ ->
astate
else if should_assume_returns_ownership call_flags actuals then
@ -733,8 +739,10 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
report spurious read/write races *)
rhs_accesses
else
add_access (AccessPath lhs_access_path) loc ~is_write_access:true rhs_accesses
astate.locks astate.threads astate.ownership proc_data
add_access
(AccessExpression (AccessExpression.of_access_path lhs_access_path))
loc ~is_write_access:true rhs_accesses astate.locks astate.threads astate.ownership
proc_data
in
let ownership = propagate_ownership lhs_access_path rhs_exp astate.ownership in
let attribute_map = propagate_attributes lhs_access_path rhs_exp astate.attribute_map in
@ -750,8 +758,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
[var] is set to true. return None if it has free variables that stop us from
evaluating it *)
and eval_bexp var = function
| HilExp.AccessPath ap when AccessPath.equal ap var ->
Some true
| HilExp.AccessExpression access_expr ->
if AccessPath.equal (AccessExpression.to_access_path access_expr) var then Some true
else None
| HilExp.Constant c ->
Some (not (Const.iszero_int_float c))
| HilExp.UnaryOperator (Unop.LNot, e, _) ->

@ -35,15 +35,17 @@ include TaintAnalysis.Make (struct
| None, _ when Typ.Procname.is_constructor pname ->
(* "this" is always the first arg of a constructor; propagate taint there *)
[TaintSpec.Propagate_to_receiver]
| ( None
, (HilExp.AccessPath ((Var.ProgramVar pvar, {desc= Typ.Tptr (_, Typ.Pk_pointer)}), []))
:: _ )
| None, (HilExp.AccessExpression access_expr) :: _ -> (
match AccessExpression.to_access_path access_expr with
| (Var.ProgramVar pvar, {desc= Typ.Tptr (_, Typ.Pk_pointer)}), []
when Pvar.is_frontend_tmp pvar ->
(* no return value, but the frontend has introduced a dummy return variable and will
assign the return value to this variable. So propagate taint to the dummy return
variable *)
let actual_index = List.length actuals - 1 in
[TaintSpec.Propagate_to_actual actual_index]
| _ ->
[TaintSpec.Propagate_to_receiver] )
| None, _ ->
(* no return value; propagate taint from actuals to receiver *)
[TaintSpec.Propagate_to_receiver]

@ -92,7 +92,9 @@ module SourceKind = struct
in
(* accessed global will be passed to us as the only parameter *)
match actuals with
| [(HilExp.AccessPath access_path)] when is_gflag access_path ->
| [(HilExp.AccessExpression access_expr)] ->
let access_path = AccessExpression.to_access_path access_expr in
if is_gflag access_path then
let (global_pvar, _), _ = access_path in
let typ_desc =
match AccessPath.get_typ access_path tenv with
@ -102,6 +104,7 @@ module SourceKind = struct
Typ.void_star.desc
in
Some (CommandLineFlag (global_pvar, typ_desc), None)
else None
| _ ->
None )
| Typ.Procname.C _ -> (

@ -26,8 +26,8 @@ include TaintAnalysis.Make (struct
let handle_unknown_call pname ret_typ_opt actuals tenv =
let get_receiver_typ tenv = function
| HilExp.AccessPath access_path ->
AccessPath.get_typ access_path tenv
| HilExp.AccessExpression access_expr ->
AccessPath.get_typ (AccessExpression.to_access_path access_expr) tenv
| _ ->
None
in

@ -123,7 +123,9 @@ module SourceKind = struct
| Typ.Procname.C _ when Typ.Procname.equal pname BuiltinDecl.__global_access -> (
match (* accessed global will be passed to us as the only parameter *)
actuals with
| [(HilExp.AccessPath ((Var.ProgramVar pvar, _), _))] ->
| [(HilExp.AccessExpression access_expr)] -> (
match AccessExpression.to_access_path access_expr with
| (Var.ProgramVar pvar, _), _ ->
let pvar_string = Pvar.to_string pvar in
(* checking substring instead of prefix because we expect field names like
com.myapp.R$drawable.whatever *)
@ -132,6 +134,8 @@ module SourceKind = struct
else None
| _ ->
None )
| _ ->
None )
| pname when BuiltinDecl.is_declared pname ->
None
| pname ->

@ -74,8 +74,10 @@ module Make (TaintSpecification : TaintSpec.S) = struct
(* get the node associated with [exp] in [access_tree] *)
let rec hil_exp_get_node ?(abstracted= false) (exp: HilExp.t) access_tree proc_data =
match exp with
| AccessPath access_path ->
exp_get_node_ ~abstracted access_path access_tree proc_data
| AccessExpression access_expr ->
exp_get_node_ ~abstracted
(AccessExpression.to_access_path access_expr)
access_tree proc_data
| Cast (_, e) | Exception e | UnaryOperator (_, e, _) ->
hil_exp_get_node ~abstracted e access_tree proc_data
| BinaryOperator (_, e1, e2) -> (
@ -99,8 +101,10 @@ module Make (TaintSpecification : TaintSpec.S) = struct
let add_actual_source source index actuals access_tree proc_data =
match List.nth_exn actuals index with
| HilExp.AccessPath actual_ap_raw ->
let actual_ap = AccessPath.Abs.Abstracted actual_ap_raw in
| HilExp.AccessExpression actual_ae_raw ->
let actual_ap =
AccessPath.Abs.Abstracted (AccessExpression.to_access_path actual_ae_raw)
in
let trace = access_path_get_trace actual_ap access_tree proc_data in
TaintDomain.add_trace actual_ap (TraceDomain.add_source source trace) access_tree
| _ ->
@ -332,11 +336,13 @@ module Make (TaintSpecification : TaintSpec.S) = struct
let actual_trace' = TraceDomain.add_sink sink' actual_trace in
report_trace actual_trace' callee_site proc_data ;
match exp with
| HilExp.AccessPath actual_ap_raw
| HilExp.AccessExpression actual_ae_raw
when not
(TraceDomain.Sources.Footprint.is_empty
(TraceDomain.sources actual_trace').footprint) ->
let actual_ap = AccessPath.Abs.Abstracted actual_ap_raw in
let actual_ap =
AccessPath.Abs.Abstracted (AccessExpression.to_access_path actual_ae_raw)
in
TaintDomain.add_trace actual_ap actual_trace' access_tree_acc
| _ ->
(* no more sources can flow into this sink; no sense in keeping track of it *)
@ -386,8 +392,10 @@ module Make (TaintSpecification : TaintSpec.S) = struct
(projected_ap_opt, Option.value ~default:TaintDomain.empty_node caller_node_opt)
| Var.LogicalVar id when Ident.is_footprint id -> (
match List.nth actuals (Ident.get_stamp id) with
| Some HilExp.AccessPath actual_ap ->
let projected_ap = project ~formal_ap ~actual_ap in
| Some HilExp.AccessExpression actual_ae ->
let projected_ap =
project ~formal_ap ~actual_ap:(AccessExpression.to_access_path actual_ae)
in
let caller_node_opt = access_path_get_node projected_ap access_tree proc_data in
(Some projected_ap, Option.value ~default:TaintDomain.empty_node caller_node_opt)
| Some exp ->
@ -448,7 +456,10 @@ module Make (TaintSpecification : TaintSpec.S) = struct
| AccessPath.ArrayAccess (_, indexes) ->
let dummy_call_site = CallSite.make BuiltinDecl.__array_access loc in
let dummy_actuals =
List.map ~f:(fun index_ap -> HilExp.AccessPath index_ap) indexes
List.map
~f:(fun index_ap ->
HilExp.AccessExpression (AccessExpression.of_access_path index_ap) )
indexes
in
let sinks =
TraceDomain.Sink.get dummy_call_site dummy_actuals proc_data.ProcData.tenv
@ -464,7 +475,11 @@ module Make (TaintSpecification : TaintSpec.S) = struct
let add_sources_for_access_path (((var, _), _) as ap) loc astate =
if Var.is_global var then
let dummy_call_site = CallSite.make BuiltinDecl.__global_access loc in
match TraceDomain.Source.get dummy_call_site [HilExp.AccessPath ap] proc_data.tenv with
match
TraceDomain.Source.get dummy_call_site
[HilExp.AccessExpression (AccessExpression.of_access_path ap)]
proc_data.tenv
with
| Some {TraceDomain.Source.source} ->
let access_path = AccessPath.Abs.Exact ap in
let trace, subtree =
@ -479,7 +494,8 @@ module Make (TaintSpecification : TaintSpec.S) = struct
in
let add_sources_sinks_for_exp exp loc astate =
match exp with
| HilExp.AccessPath access_path ->
| HilExp.AccessExpression access_expr ->
let access_path = AccessExpression.to_access_path access_expr in
add_sinks_for_access_path access_path loc astate
|> add_sources_for_access_path access_path loc
| _ ->
@ -572,15 +588,17 @@ module Make (TaintSpecification : TaintSpec.S) = struct
propagate_to_access_path (AccessPath.Abs.Abstracted (ret_ap, [])) actuals
astate_acc
| ( TaintSpec.Propagate_to_receiver
, (AccessPath receiver_ap) :: (_ :: _ as other_actuals)
, (AccessExpression receiver_ae) :: (_ :: _ as other_actuals)
, _ ) ->
propagate_to_access_path (AccessPath.Abs.Abstracted receiver_ap) other_actuals
astate_acc
propagate_to_access_path
(AccessPath.Abs.Abstracted (AccessExpression.to_access_path receiver_ae))
other_actuals astate_acc
| TaintSpec.Propagate_to_actual actual_index, _, _ -> (
match List.nth actuals actual_index with
| Some HilExp.AccessPath actual_ap ->
propagate_to_access_path (AccessPath.Abs.Abstracted actual_ap) actuals
astate_acc
| Some HilExp.AccessExpression actual_ae ->
propagate_to_access_path
(AccessPath.Abs.Abstracted (AccessExpression.to_access_path actual_ae))
actuals astate_acc
| _ ->
astate_acc )
| _ ->
@ -593,20 +611,27 @@ module Make (TaintSpecification : TaintSpec.S) = struct
| "operator=" when not (Typ.Procname.is_java callee_pname) -> (
match (* treat unknown calls to C++ operator= as assignment *)
actuals with
| [(AccessPath lhs_access_path); rhs_exp] ->
exec_write lhs_access_path rhs_exp access_tree
| [ (AccessPath lhs_access_path)
; rhs_exp
; (HilExp.AccessPath (((Var.ProgramVar pvar, _), []) as dummy_ret_access_path)) ]
when Pvar.is_frontend_tmp pvar ->
| [(AccessExpression lhs_access_expr); rhs_exp] ->
exec_write (AccessExpression.to_access_path lhs_access_expr) rhs_exp access_tree
| [(AccessExpression lhs_access_expr); rhs_exp; (HilExp.AccessExpression access_expr)]
-> (
let dummy_ret_access_path = AccessExpression.to_access_path access_expr in
match dummy_ret_access_path with
| (Var.ProgramVar pvar, _), [] when Pvar.is_frontend_tmp pvar ->
(* the frontend translates operator=(x, y) as operator=(x, y, dummy_ret) when
operator= returns a value type *)
exec_write lhs_access_path rhs_exp access_tree
exec_write
(AccessExpression.to_access_path lhs_access_expr)
rhs_exp access_tree
|> exec_write dummy_ret_access_path rhs_exp
| _ ->
L.internal_error "Unexpected call to operator= %a in %a" HilInstr.pp instr
Typ.Procname.pp callee_pname ;
access_tree )
| _ ->
L.internal_error "Unexpected call to operator= %a in %a" HilInstr.pp instr
Typ.Procname.pp callee_pname ;
access_tree )
| _ ->
let model =
TaintSpecification.handle_unknown_call callee_pname (Option.map ~f:snd ret_opt)
@ -623,11 +648,14 @@ module Make (TaintSpecification : TaintSpec.S) = struct
assigning to it. understand this pattern by pretending it's the return value *)
List.last actuals
with
| Some HilExp.AccessPath (((Var.ProgramVar pvar, _) as ret_base), [])
when Pvar.is_frontend_tmp pvar ->
| Some HilExp.AccessExpression access_expr -> (
match AccessExpression.to_access_path access_expr with
| ((Var.ProgramVar pvar, _) as ret_base), [] when Pvar.is_frontend_tmp pvar ->
Some ret_base
| _ ->
None )
| _ ->
None )
| _ ->
ret_opt
in

Loading…
Cancel
Save