diff --git a/infer/src/IR/AccessExpression.ml b/infer/src/IR/AccessExpression.ml new file mode 100644 index 000000000..8c6a6668e --- /dev/null +++ b/infer/src/IR/AccessExpression.ml @@ -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) diff --git a/infer/src/IR/AccessExpression.mli b/infer/src/IR/AccessExpression.mli new file mode 100644 index 000000000..70ccf33c0 --- /dev/null +++ b/infer/src/IR/AccessExpression.mli @@ -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 diff --git a/infer/src/IR/HilExp.ml b/infer/src/IR/HilExp.ml index 950c0d378..047a9ce80 100644 --- a/infer/src/IR/HilExp.ml +++ b/infer/src/IR/HilExp.ml @@ -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 diff --git a/infer/src/IR/HilExp.mli b/infer/src/IR/HilExp.mli index 5fd7f376f..e7150727d 100644 --- a/infer/src/IR/HilExp.mli +++ b/infer/src/IR/HilExp.mli @@ -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 *) diff --git a/infer/src/IR/HilInstr.ml b/infer/src/IR/HilInstr.ml index 82e5146c6..2ffe286a5 100644 --- a/infer/src/IR/HilInstr.ml +++ b/infer/src/IR/HilInstr.ml @@ -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 diff --git a/infer/src/absint/LowerHil.ml b/infer/src/absint/LowerHil.ml index 0d9296713..5f1d02125 100644 --- a/infer/src/absint/LowerHil.ml +++ b/infer/src/absint/LowerHil.ml @@ -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 diff --git a/infer/src/checkers/Litho.ml b/infer/src/checkers/Litho.ml index de86587a1..5698f0507 100644 --- a/infer/src/checkers/Litho.ml +++ b/infer/src/checkers/Litho.ml @@ -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 diff --git a/infer/src/checkers/NullabilityCheck.ml b/infer/src/checkers/NullabilityCheck.ml index 0ee287a57..15c61d637 100644 --- a/infer/src/checkers/NullabilityCheck.ml +++ b/infer/src/checkers/NullabilityCheck.ml @@ -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 diff --git a/infer/src/checkers/NullabilitySuggest.ml b/infer/src/checkers/NullabilitySuggest.ml index fccb24eae..7403c2057 100644 --- a/infer/src/checkers/NullabilitySuggest.ml +++ b/infer/src/checkers/NullabilitySuggest.ml @@ -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 diff --git a/infer/src/checkers/uninit.ml b/infer/src/checkers/uninit.ml index b9fd47ba8..c1d5ed980 100644 --- a/infer/src/checkers/uninit.ml +++ b/infer/src/checkers/uninit.ml @@ -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 -> - D.remove (base, al) acc - | HilExp.AccessPath (((_, 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 -> - remove_all_fields tenv (fst ap) (D.remove ap acc) - | HilExp.AccessPath (((_, {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' + | 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 + | ((_, 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 + | ap when Typ.Procname.is_constructor call -> + remove_all_fields tenv (fst ap) (D.remove ap acc) + | ((_, {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 diff --git a/infer/src/concurrency/RacerD.ml b/infer/src/concurrency/RacerD.ml index 7ce9ea50e..d8989aabb 100644 --- a/infer/src/concurrency/RacerD.ml +++ b/infer/src/concurrency/RacerD.ml @@ -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 -> - (* TODO: check for constants, which are functional? *) - let attribute_map = - AttributeMapDomain.add_attribute (ret, []) Functional - astate.attribute_map - in - {astate with 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, _) -> diff --git a/infer/src/quandary/ClangTaintAnalysis.ml b/infer/src/quandary/ClangTaintAnalysis.ml index bfb71a278..cbc7877e2 100644 --- a/infer/src/quandary/ClangTaintAnalysis.ml +++ b/infer/src/quandary/ClangTaintAnalysis.ml @@ -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)}), [])) - :: _ ) - when Pvar.is_frontend_tmp pvar -> - (* no return value, but the frontend has introduced a dummy return variable and will + | 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] + 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] diff --git a/infer/src/quandary/ClangTrace.ml b/infer/src/quandary/ClangTrace.ml index dee57017b..770b37daa 100644 --- a/infer/src/quandary/ClangTrace.ml +++ b/infer/src/quandary/ClangTrace.ml @@ -92,16 +92,19 @@ 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 -> - let (global_pvar, _), _ = access_path in - let typ_desc = - match AccessPath.get_typ access_path tenv with - | Some {Typ.desc} -> - desc - | None -> - Typ.void_star.desc - in - Some (CommandLineFlag (global_pvar, typ_desc), None) + | [(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 + | Some {Typ.desc} -> + desc + | None -> + Typ.void_star.desc + in + Some (CommandLineFlag (global_pvar, typ_desc), None) + else None | _ -> None ) | Typ.Procname.C _ -> ( diff --git a/infer/src/quandary/JavaTaintAnalysis.ml b/infer/src/quandary/JavaTaintAnalysis.ml index 066f25375..c2bbb2fe8 100644 --- a/infer/src/quandary/JavaTaintAnalysis.ml +++ b/infer/src/quandary/JavaTaintAnalysis.ml @@ -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 diff --git a/infer/src/quandary/JavaTrace.ml b/infer/src/quandary/JavaTrace.ml index 1f183afb4..04d5da83d 100644 --- a/infer/src/quandary/JavaTrace.ml +++ b/infer/src/quandary/JavaTrace.ml @@ -123,13 +123,17 @@ 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, _), _))] -> - let pvar_string = Pvar.to_string pvar in - (* checking substring instead of prefix because we expect field names like - com.myapp.R$drawable.whatever *) - if String.is_substring ~substring:AndroidFramework.drawable_prefix pvar_string then - Some (DrawableResource pvar, None) - else None + | [(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 *) + if String.is_substring ~substring:AndroidFramework.drawable_prefix pvar_string then + Some (DrawableResource pvar, None) + else None + | _ -> + None ) | _ -> None ) | pname when BuiltinDecl.is_declared pname -> diff --git a/infer/src/quandary/TaintAnalysis.ml b/infer/src/quandary/TaintAnalysis.ml index b099a75ae..2d091165c 100644 --- a/infer/src/quandary/TaintAnalysis.ml +++ b/infer/src/quandary/TaintAnalysis.ml @@ -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,16 +611,23 @@ 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 -> - (* the frontend translates operator=(x, y) as operator=(x, y, dummy_ret) when + | [(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 dummy_ret_access_path rhs_exp + 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 ; @@ -623,9 +648,12 @@ 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 ret_base + | 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 ) | _ ->