[HIL] Implicit dereference in access expression

Reviewed By: sblackshear

Differential Revision: D7153744

fbshipit-source-id: f439e02
master
Daiva Naudziuniene 7 years ago committed by Facebook Github Bot
parent a0149872ad
commit 4157ba820a

@ -8,32 +8,146 @@
*) *)
open! IStd open! IStd
module F = Format
type t = type t =
| Base of AccessPath.base | Base of AccessPath.base
| Offset of t * AccessPath.access | FieldOffset of t * Typ.Fieldname.t
(* field/array access *) | ArrayOffset of t * Typ.t * t list
| AddressOf of t | AddressOf of t
(* address of operator & *)
| Dereference of t | Dereference of t
(* dereference operator * *)
[@@deriving compare] [@@deriving compare]
(** convert to an AccessPath.t, ignoring AddressOf and Dereference for now *) (** convert to an AccessPath.t, ignoring AddressOf and Dereference for now *)
let rec to_access_path t = let rec to_access_path t =
match t with let rec to_access_path_ t =
match t with
| Base base ->
(base, [])
| FieldOffset (ae, fld) ->
let base, accesses = to_access_path_ ae in
(base, AccessPath.FieldAccess fld :: accesses)
| ArrayOffset (ae, typ, index_aes) ->
let access_paths = to_access_paths index_aes in
let base, accesses = to_access_path_ ae in
(base, AccessPath.ArrayAccess (typ, access_paths) :: accesses)
| AddressOf ae ->
to_access_path_ ae
| Dereference ae ->
to_access_path_ ae
in
let base, accesses = to_access_path_ t in
(base, List.rev accesses)
and to_access_paths ts = List.map ~f:to_access_path ts
let rec get_base = function
| Base base -> | Base base ->
(base, []) base
| Offset (ae, acc) -> | FieldOffset (ae, _) | ArrayOffset (ae, _, _) | AddressOf ae | Dereference ae ->
AccessPath.append (to_access_path ae) [acc] get_base ae
let lookup_field_type_annot tenv base_typ field_name =
let lookup = Tenv.lookup tenv in
Typ.Struct.get_field_type_and_annotation ~lookup field_name base_typ
let rec get_typ t tenv : Typ.t option =
match t with
| Base (_, typ) ->
Some typ
| FieldOffset (ae, fld)
-> (
let base_typ_opt = get_typ ae tenv in
match base_typ_opt with
| Some base_typ ->
Option.map (lookup_field_type_annot tenv base_typ fld) ~f:fst
| None ->
None )
| ArrayOffset (_, typ, _) ->
Some typ
| AddressOf ae -> | AddressOf ae ->
to_access_path ae let base_typ_opt = get_typ ae tenv in
Option.map base_typ_opt ~f:(fun base_typ -> Typ.mk (Tptr (base_typ, Pk_pointer)))
| Dereference ae -> | Dereference ae ->
to_access_path ae let base_typ_opt = get_typ ae tenv in
match base_typ_opt with Some {Typ.desc= Tptr (typ, _)} -> Some typ | _ -> None
let of_access_path (base, accesses) = let rec pp fmt = function
let rec add_access accesses ae = | Base (pvar, _) ->
match accesses with [] -> ae | access :: rest -> add_access rest (Offset (ae, access)) Var.pp fmt pvar
| FieldOffset (ae, fld) ->
F.fprintf fmt "%a.%a" pp ae Typ.Fieldname.pp fld
| ArrayOffset (ae, _, []) ->
F.fprintf fmt "%a[_]" pp ae
| ArrayOffset (ae, _, index_aes) ->
F.fprintf fmt "%a[%a]" pp ae (PrettyPrintable.pp_collection ~pp_item:pp) index_aes
| AddressOf ae ->
F.fprintf fmt "&(%a)" pp ae
| Dereference ae ->
F.fprintf fmt "*(%a)" pp ae
let equal = [%compare.equal : t]
let base_of_id id typ = (Var.of_id id, typ)
let base_of_pvar pvar typ = (Var.of_pvar pvar, typ)
let of_pvar pvar typ = Base (base_of_pvar pvar typ)
let of_id id typ = Base (base_of_id id typ)
(* Adapted from AccessPath.of_exp. *)
let of_exp ~include_array_indexes exp0 typ0 ~(f_resolve_id: Var.t -> t option) =
let rec of_exp_ exp typ (add_accesses: t -> t) acc : t list =
match exp with
| Exp.Var id -> (
match f_resolve_id (Var.of_id id) with
| Some access_expr ->
add_accesses access_expr :: acc
| None ->
add_accesses (of_id id typ) :: acc )
| Exp.Lvar pvar when Pvar.is_ssa_frontend_tmp pvar -> (
match f_resolve_id (Var.of_pvar pvar) with
| Some access_expr ->
add_accesses access_expr :: acc
| None ->
add_accesses (of_pvar pvar typ) :: acc )
| Exp.Lvar pvar ->
add_accesses (of_pvar pvar typ) :: acc
| Exp.Lfield (root_exp, fld, root_exp_typ) ->
let add_field_access_expr access_expr = add_accesses (FieldOffset (access_expr, fld)) in
of_exp_ root_exp root_exp_typ add_field_access_expr acc
| Exp.Lindex (root_exp, index_exp) ->
let index_access_exprs =
if include_array_indexes then of_exp_ index_exp typ Fn.id [] else []
in
let add_array_access_expr access_expr =
add_accesses (ArrayOffset (access_expr, typ, index_access_exprs))
in
let array_typ = Typ.mk_array typ in
of_exp_ root_exp array_typ add_array_access_expr acc
| Exp.Cast (cast_typ, cast_exp) ->
of_exp_ cast_exp cast_typ Fn.id acc
| Exp.UnOp (_, unop_exp, _) ->
of_exp_ unop_exp typ Fn.id acc
| Exp.Exn exn_exp ->
of_exp_ exn_exp typ Fn.id acc
| Exp.BinOp (_, exp1, exp2) ->
of_exp_ exp1 typ Fn.id acc |> of_exp_ exp2 typ Fn.id
| Exp.Const _ | Closure _ | Sizeof _ ->
acc
in in
add_access accesses (Base base) of_exp_ exp0 typ0 Fn.id []
let of_lhs_exp ~include_array_indexes lhs_exp typ ~(f_resolve_id: Var.t -> t option) =
match of_exp ~include_array_indexes lhs_exp typ ~f_resolve_id with
| [lhs_ae] ->
Some lhs_ae
| _ ->
None

@ -11,14 +11,31 @@ open! IStd
type t = type t =
| Base of AccessPath.base | Base of AccessPath.base
| Offset of t * AccessPath.access | FieldOffset of t * Typ.Fieldname.t
(* field/array access *) (* field access *)
| ArrayOffset of t * Typ.t * t list
(* array access *)
| AddressOf of t | AddressOf of t
(* & *) (* address of operator & *)
| Dereference of t | Dereference of t
(* * *) (* dereference operator * *)
[@@deriving compare] [@@deriving compare]
val to_access_path : t -> AccessPath.t val to_access_path : t -> AccessPath.t
val of_access_path : AccessPath.t -> t val to_access_paths : t list -> AccessPath.t list
val of_id : Ident.t -> Typ.t -> t
(** create an access expression from an ident *)
val get_base : t -> AccessPath.base
val get_typ : t -> Tenv.t -> Typ.t option
val pp : Format.formatter -> t -> unit
val equal : t -> t -> bool
val of_lhs_exp :
include_array_indexes:bool -> Exp.t -> Typ.t -> f_resolve_id:(Var.t -> t option) -> t option
(** convert [lhs_exp] to an access expression, resolving identifiers using [f_resolve_id] *)

@ -24,7 +24,7 @@ type t =
let rec pp fmt = function let rec pp fmt = function
| AccessExpression access_expr -> | AccessExpression access_expr ->
AccessPath.pp fmt (AccessExpression.to_access_path access_expr) AccessExpression.pp fmt access_expr
| UnaryOperator (op, e, _) -> | UnaryOperator (op, e, _) ->
F.fprintf fmt "%s%a" (Unop.str op) pp e F.fprintf fmt "%s%a" (Unop.str op) pp e
| BinaryOperator (op, e1, e2) -> | BinaryOperator (op, e1, e2) ->
@ -53,7 +53,7 @@ let rec pp fmt = function
let rec get_typ tenv = function let rec get_typ tenv = function
| AccessExpression access_expr -> | AccessExpression access_expr ->
AccessPath.get_typ (AccessExpression.to_access_path access_expr) tenv AccessExpression.get_typ access_expr tenv
| UnaryOperator (_, _, typ_opt) -> | UnaryOperator (_, _, typ_opt) ->
typ_opt typ_opt
| BinaryOperator ((Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr), _, _) -> | BinaryOperator ((Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr), _, _) ->
@ -92,39 +92,39 @@ let rec get_typ tenv = function
Some (Typ.mk (Typ.Tint Typ.IUInt)) Some (Typ.mk (Typ.Tint Typ.IUInt))
let get_access_paths exp0 = let get_access_exprs exp0 =
let rec get_access_paths_ exp acc = let rec get_access_exprs_ exp acc =
match exp with match exp with
| AccessExpression ae -> | AccessExpression ae ->
AccessExpression.to_access_path ae :: acc ae :: acc
| Cast (_, e) | UnaryOperator (_, e, _) | Exception e | Sizeof (_, Some e) -> | Cast (_, e) | UnaryOperator (_, e, _) | Exception e | Sizeof (_, Some e) ->
get_access_paths_ e acc get_access_exprs_ e acc
| BinaryOperator (_, e1, e2) -> | BinaryOperator (_, e1, e2) ->
get_access_paths_ e1 acc |> get_access_paths_ e2 get_access_exprs_ e1 acc |> get_access_exprs_ e2
| Closure (_, captured) -> | Closure (_, captured) ->
List.fold captured ~f:(fun acc (_, e) -> get_access_paths_ e acc) ~init:acc List.fold captured ~f:(fun acc (_, e) -> get_access_exprs_ e acc) ~init:acc
| Constant _ | Sizeof _ -> | Constant _ | Sizeof _ ->
acc acc
in in
get_access_paths_ exp0 [] get_access_exprs_ exp0 []
(* convert an SIL expression into an HIL expression. the [f_resolve_id] function should map an SSA (* convert an SIL expression into an HIL expression. the [f_resolve_id] function should map an SSA
temporary variable to the access path it represents. evaluating the HIL expression should temporary variable to the access path it represents. evaluating the HIL expression should
produce the same result as evaluating the SIL expression and replacing the temporary variables produce the same result as evaluating the SIL expression and replacing the temporary variables
using [f_resolve_id] *) using [f_resolve_id] *)
let of_sil ~include_array_indexes ~f_resolve_id exp typ = let of_sil ~include_array_indexes ~f_resolve_id ~add_deref exp typ =
let rec of_sil_ (exp: Exp.t) typ = let rec of_sil_ (exp: Exp.t) typ =
match exp with match exp with
| Var id -> | Var id ->
let ap = let ae =
match f_resolve_id (Var.of_id id) with match f_resolve_id (Var.of_id id) with
| Some access_path -> | Some access_expr ->
access_path if add_deref then AccessExpression.Dereference access_expr else access_expr
| None -> | None ->
AccessPath.of_id id typ AccessExpression.of_id id typ
in in
AccessExpression (AccessExpression.of_access_path ap) AccessExpression ae
| UnOp (op, e, typ_opt) -> | UnOp (op, e, typ_opt) ->
UnaryOperator (op, of_sil_ e typ, typ_opt) UnaryOperator (op, of_sil_ e typ, typ_opt)
| BinOp (op, e0, e1) -> | BinOp (op, e0, e1) ->
@ -156,9 +156,9 @@ let of_sil ~include_array_indexes ~f_resolve_id exp typ =
in in
Closure (closure.name, environment) Closure (closure.name, environment)
| Lfield (root_exp, fld, root_exp_typ) -> ( | Lfield (root_exp, fld, root_exp_typ) -> (
match AccessPath.of_lhs_exp ~include_array_indexes exp typ ~f_resolve_id with match AccessExpression.of_lhs_exp ~include_array_indexes exp typ ~f_resolve_id with
| Some access_path -> | Some access_expr ->
AccessExpression (AccessExpression.of_access_path access_path) AccessExpression access_expr
| None -> | None ->
(* unsupported field expression: represent with a dummy variable *) (* unsupported field expression: represent with a dummy variable *)
of_sil_ of_sil_
@ -173,9 +173,9 @@ let of_sil ~include_array_indexes ~f_resolve_id exp typ =
literal, e.g. using `const_cast<char*>` *) literal, e.g. using `const_cast<char*>` *)
of_sil_ (Exp.Lindex (Var (Ident.create_normal (Ident.string_to_name s) 0), index_exp)) typ of_sil_ (Exp.Lindex (Var (Ident.create_normal (Ident.string_to_name s) 0), index_exp)) typ
| Lindex (root_exp, index_exp) -> ( | Lindex (root_exp, index_exp) -> (
match AccessPath.of_lhs_exp ~include_array_indexes exp typ ~f_resolve_id with match AccessExpression.of_lhs_exp ~include_array_indexes exp typ ~f_resolve_id with
| Some access_path -> | Some access_expr ->
AccessExpression (AccessExpression.of_access_path access_path) AccessExpression access_expr
| None -> | None ->
(* unsupported index expression: represent with a dummy variable *) (* unsupported index expression: represent with a dummy variable *)
of_sil_ of_sil_
@ -183,9 +183,9 @@ let of_sil ~include_array_indexes ~f_resolve_id exp typ =
( Var (Ident.create_normal (Ident.string_to_name (Exp.to_string root_exp)) 0) ( Var (Ident.create_normal (Ident.string_to_name (Exp.to_string root_exp)) 0)
, index_exp )) typ ) , index_exp )) typ )
| Lvar _ -> | Lvar _ ->
match AccessPath.of_lhs_exp ~include_array_indexes exp typ ~f_resolve_id with match AccessExpression.of_lhs_exp ~include_array_indexes exp typ ~f_resolve_id with
| Some access_path -> | Some access_expr ->
AccessExpression (AccessExpression.of_access_path access_path) AccessExpression access_expr
| None -> | None ->
L.(die InternalError) "Couldn't convert var expression %a to access path" Exp.pp exp L.(die InternalError) "Couldn't convert var expression %a to access path" Exp.pp exp
in in

@ -30,10 +30,11 @@ val get_typ : Tenv.t -> t -> Typ.t option
(** Get the type of the expression. Warning: not fully implemented *) (** Get the type of the expression. Warning: not fully implemented *)
val of_sil : val of_sil :
include_array_indexes:bool -> f_resolve_id:(Var.t -> AccessPath.t option) -> Exp.t -> Typ.t -> t include_array_indexes:bool -> f_resolve_id:(Var.t -> AccessExpression.t option) -> add_deref:bool
-> Exp.t -> Typ.t -> t
(** Convert SIL expression to HIL expression *) (** Convert SIL expression to HIL expression *)
val get_access_paths : t -> AccessPath.t list val get_access_exprs : t -> AccessExpression.t list
(** Get all the access paths used in the given HIL expression, including duplicates if a path is (** Get all the access paths used in the given HIL expression, including duplicates if a path is
used more than once. *) used more than once. *)

@ -11,13 +11,13 @@ open! IStd
module F = Format module F = Format
module L = Logging module L = Logging
type call = Direct of Typ.Procname.t | Indirect of AccessPath.t [@@deriving compare] type call = Direct of Typ.Procname.t | Indirect of AccessExpression.t [@@deriving compare]
let pp_call fmt = function let pp_call fmt = function
| Direct pname -> | Direct pname ->
Typ.Procname.pp fmt pname Typ.Procname.pp fmt pname
| Indirect access_path -> | Indirect access_expr ->
F.fprintf fmt "*%a" AccessPath.pp access_path F.fprintf fmt "*%a" AccessExpression.pp access_expr
type t = type t =
@ -28,9 +28,7 @@ type t =
let pp fmt = function let pp fmt = function
| Assign (access_expr, exp, loc) -> | Assign (access_expr, exp, loc) ->
F.fprintf fmt "%a := %a [%a]" AccessPath.pp F.fprintf fmt "%a := %a [%a]" AccessExpression.pp access_expr HilExp.pp exp Location.pp loc
(AccessExpression.to_access_path access_expr)
HilExp.pp exp Location.pp loc
| Assume (exp, _, _, loc) -> | Assume (exp, _, _, loc) ->
F.fprintf fmt "assume %a [%a]" HilExp.pp exp Location.pp loc F.fprintf fmt "assume %a [%a]" HilExp.pp exp Location.pp loc
| Call (ret_opt, call, actuals, _, loc) -> | Call (ret_opt, call, actuals, _, loc) ->
@ -39,25 +37,31 @@ let pp fmt = function
F.fprintf fmt "%a%a(%a) [%a]" pp_ret ret_opt pp_call call pp_actuals actuals Location.pp loc 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.t | Unbind of Var.t list | Ignore type translation =
| Instr of t
| Bind of Var.t * AccessExpression.t
| Unbind of Var.t list
| Ignore
(* convert an SIL instruction into an HIL instruction. the [f_resolve_id] function should map an SSA (* 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 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 produce the same result as evaluating the SIL instruction and replacing the temporary variables
using [f_resolve_id]. *) using [f_resolve_id]. *)
let of_sil ~include_array_indexes ~f_resolve_id (instr: Sil.instr) = let of_sil ~include_array_indexes ~f_resolve_id (instr: Sil.instr) =
let exp_of_sil = HilExp.of_sil ~include_array_indexes ~f_resolve_id in let exp_of_sil ?(add_deref= false) =
let analyze_id_assignment lhs_id rhs_exp rhs_typ loc = HilExp.of_sil ~include_array_indexes ~f_resolve_id ~add_deref
let rhs_hil_exp = exp_of_sil rhs_exp rhs_typ in in
let analyze_id_assignment ?(add_deref= false) lhs_id rhs_exp rhs_typ loc =
let rhs_hil_exp = exp_of_sil ~add_deref rhs_exp rhs_typ in
match rhs_hil_exp with match rhs_hil_exp with
| AccessExpression rhs_access_expr -> | AccessExpression rhs_access_expr ->
Bind (lhs_id, AccessExpression.to_access_path rhs_access_expr) Bind (lhs_id, rhs_access_expr)
| _ -> | _ ->
Instr (Assign (AccessExpression.Base (lhs_id, rhs_typ), rhs_hil_exp, loc)) Instr (Assign (AccessExpression.Base (lhs_id, rhs_typ), rhs_hil_exp, loc))
in in
match instr with match instr with
| Load (lhs_id, rhs_exp, rhs_typ, loc) -> | Load (lhs_id, rhs_exp, rhs_typ, loc) ->
analyze_id_assignment (Var.of_id lhs_id) rhs_exp rhs_typ loc analyze_id_assignment ~add_deref:true (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 -> | 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 analyze_id_assignment (Var.of_pvar lhs_pvar) rhs_exp lhs_typ loc
| Call | Call
@ -70,7 +74,7 @@ let of_sil ~include_array_indexes ~f_resolve_id (instr: Sil.instr) =
analyze_id_assignment (Var.of_id ret_id) target_exp cast_typ loc analyze_id_assignment (Var.of_id ret_id) target_exp cast_typ loc
| Store (lhs_exp, typ, rhs_exp, loc) -> | Store (lhs_exp, typ, rhs_exp, loc) ->
let lhs_access_expr = let lhs_access_expr =
match exp_of_sil lhs_exp typ with match exp_of_sil ~add_deref:true lhs_exp typ with
| AccessExpression access_expr -> | AccessExpression access_expr ->
access_expr access_expr
| BinaryOperator (_, exp0, exp1) -> ( | BinaryOperator (_, exp0, exp1) -> (
@ -79,14 +83,14 @@ let of_sil ~include_array_indexes ~f_resolve_id (instr: Sil.instr) =
one pointer type represented as an access path. just use that access path and forget 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 about the arithmetic. if you need to model this more precisely, you should be using
SIL instead *) SIL instead *)
HilExp.get_access_paths exp0 HilExp.get_access_exprs exp0
with with
| ap :: _ -> | ap :: _ ->
AccessExpression.of_access_path ap ap
| [] -> | [] ->
match HilExp.get_access_paths exp1 with match HilExp.get_access_exprs exp1 with
| ap :: _ -> | ap :: _ ->
AccessExpression.of_access_path ap ap
| [] -> | [] ->
L.(die InternalError) L.(die InternalError)
"Invalid pointer arithmetic expression %a used as LHS at %a" Exp.pp lhs_exp "Invalid pointer arithmetic expression %a used as LHS at %a" Exp.pp lhs_exp
@ -110,7 +114,7 @@ let of_sil ~include_array_indexes ~f_resolve_id (instr: Sil.instr) =
| Constant Cfun procname | Closure (procname, _) -> | Constant Cfun procname | Closure (procname, _) ->
Direct procname Direct procname
| AccessExpression access_expr -> | AccessExpression access_expr ->
Indirect (AccessExpression.to_access_path access_expr) Indirect access_expr
| call_exp -> | call_exp ->
L.(die InternalError) "Unexpected call expression %a" HilExp.pp call_exp L.(die InternalError) "Unexpected call expression %a" HilExp.pp call_exp
in in

@ -11,7 +11,7 @@ open! IStd
module F = Format module F = Format
(** type of a procedure call; either direct or via function pointer *) (** type of a procedure call; either direct or via function pointer *)
type call = Direct of Typ.Procname.t | Indirect of AccessPath.t [@@deriving compare] type call = Direct of Typ.Procname.t | Indirect of AccessExpression.t [@@deriving compare]
type t = type t =
| Assign of AccessExpression.t * HilExp.t * Location.t | Assign of AccessExpression.t * HilExp.t * Location.t
@ -27,11 +27,11 @@ val pp : F.formatter -> t -> unit
(** Result of translating an SIL instruction *) (** Result of translating an SIL instruction *)
type translation = type translation =
| Instr of t (** HIL instruction to execute *) | Instr of t (** HIL instruction to execute *)
| Bind of Var.t * AccessPath.t (** add binding to identifier map *) | Bind of Var.t * AccessExpression.t (** add binding to identifier map *)
| Unbind of Var.t list (** remove binding from identifier map *) | Unbind of Var.t list (** remove binding from identifier map *)
| Ignore (** no-op *) | Ignore (** no-op *)
val of_sil : val of_sil :
include_array_indexes:bool -> f_resolve_id:(Var.t -> AccessPath.t option) -> Sil.instr include_array_indexes:bool -> f_resolve_id:(Var.t -> AccessExpression.t option) -> Sil.instr
-> translation -> translation
(** Convert an SIL instruction to an HIL instruction *) (** Convert an SIL instruction to an HIL instruction *)

@ -67,13 +67,10 @@ struct
"dump" all of the temporaries out of the id map, then execute the unlock instruction. *) "dump" all of the temporaries out of the id map, then execute the unlock instruction. *)
let actual_state' = let actual_state' =
IdAccessPathMapDomain.fold IdAccessPathMapDomain.fold
(fun id access_path astate_acc -> (fun id access_expr astate_acc ->
let lhs_access_path = AccessExpression.Base (id, Typ.mk Typ.Tvoid) in let lhs_access_path = AccessExpression.Base (id, Typ.mk Typ.Tvoid) in
let dummy_assign = let dummy_assign =
HilInstr.Assign HilInstr.Assign (lhs_access_path, HilExp.AccessExpression access_expr, loc)
( lhs_access_path
, HilExp.AccessExpression (AccessExpression.of_access_path access_path)
, loc )
in in
TransferFunctions.exec_instr astate_acc extras node dummy_assign ) TransferFunctions.exec_instr astate_acc extras node dummy_assign )
id_map actual_state id_map actual_state

@ -11,20 +11,20 @@ open! IStd
module IdMap = Var.Map module IdMap = Var.Map
module L = Logging module L = Logging
type astate = AccessPath.t IdMap.t type astate = AccessExpression.t IdMap.t
include IdMap include IdMap
let pp fmt astate = IdMap.pp ~pp_value:AccessPath.pp fmt astate let pp fmt astate = IdMap.pp ~pp_value:AccessExpression.pp fmt astate
let check_invariant ap1 ap2 = function let check_invariant ap1 ap2 = function
| Var.ProgramVar pvar when Pvar.is_ssa_frontend_tmp pvar -> | Var.ProgramVar pvar when Pvar.is_ssa_frontend_tmp pvar ->
(* Sawja reuses temporary variables which sometimes breaks this invariant *) (* Sawja reuses temporary variables which sometimes breaks this invariant *)
() ()
| id -> | id ->
if not (AccessPath.equal ap1 ap2) then if not (AccessExpression.equal ap1 ap2) then
L.(die InternalError) L.(die InternalError)
"Id %a maps to both %a and %a" Var.pp id AccessPath.pp ap1 AccessPath.pp ap2 "Id %a maps to both %a and %a" Var.pp id AccessExpression.pp ap1 AccessExpression.pp ap2
let ( <= ) ~lhs ~rhs = let ( <= ) ~lhs ~rhs =

@ -13,7 +13,7 @@ open! IStd
module IdMap = Var.Map module IdMap = Var.Map
type astate = AccessPath.t IdMap.t type astate = AccessExpression.t IdMap.t
include module type of IdMap include module type of IdMap

@ -124,8 +124,9 @@ module Domain = struct
let exp_add_reads exp loc summary astate = let exp_add_reads exp loc summary astate =
List.fold List.fold
~f:(fun acc access_path -> access_path_add_read access_path loc summary acc) ~f:(fun acc access_expr ->
(HilExp.get_access_paths exp) ~init:astate access_path_add_read (AccessExpression.to_access_path access_expr) loc summary acc )
(HilExp.get_access_exprs exp) ~init:astate
let actuals_add_reads actuals loc summary astate = let actuals_add_reads actuals loc summary astate =
@ -144,8 +145,10 @@ module Domain = struct
let borrow_exp lhs_var rhs_exp astate = let borrow_exp lhs_var rhs_exp astate =
let rhs_vars = let rhs_vars =
List.fold (HilExp.get_access_paths rhs_exp) List.fold (HilExp.get_access_exprs rhs_exp)
~f:(fun acc ((var, _), _) -> VarSet.add var acc) ~f:(fun acc access_expr ->
let (var, _), _ = AccessExpression.to_access_path access_expr in
VarSet.add var acc )
~init:VarSet.empty ~init:VarSet.empty
in in
borrow_vars lhs_var rhs_vars astate borrow_vars lhs_var rhs_vars astate

@ -164,9 +164,10 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
add_field_access pre add_field_access pre
in in
List.fold List.fold
~f:(fun acc (base, accesses) -> ~f:(fun acc access_expr ->
let base, accesses = AccessExpression.to_access_path access_expr in
if is_static_access (fst base) then acc else add_field_accesses (base, []) acc accesses ) if is_static_access (fst base) then acc else add_field_accesses (base, []) acc accesses )
~init:accesses (HilExp.get_access_paths exp) ~init:accesses (HilExp.get_access_exprs exp)
let is_synchronized_container callee_pname ((_, (base_typ: Typ.t)), accesses) tenv = let is_synchronized_container callee_pname ((_, (base_typ: Typ.t)), accesses) tenv =
@ -584,7 +585,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
add_access rhs_exp loc ~is_write_access:false astate.accesses astate.locks astate.threads add_access rhs_exp loc ~is_write_access:false astate.accesses astate.locks astate.threads
astate.ownership proc_data astate.ownership proc_data
in in
let rhs_access_paths = HilExp.get_access_paths rhs_exp in let rhs_access_paths =
AccessExpression.to_access_paths (HilExp.get_access_exprs rhs_exp)
in
let is_functional = let is_functional =
not (List.is_empty rhs_access_paths) not (List.is_empty rhs_access_paths)
&& List.for_all && List.for_all
@ -607,10 +610,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
report spurious read/write races *) report spurious read/write races *)
rhs_accesses rhs_accesses
else else
add_access add_access (AccessExpression lhs_access_expr) loc ~is_write_access:true rhs_accesses
(AccessExpression (AccessExpression.of_access_path lhs_access_path)) astate.locks astate.threads astate.ownership proc_data
loc ~is_write_access:true rhs_accesses astate.locks astate.threads astate.ownership
proc_data
in in
let ownership = propagate_ownership lhs_access_path rhs_exp astate.ownership 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 let attribute_map = propagate_attributes lhs_access_path rhs_exp astate.attribute_map in
@ -674,9 +675,10 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
astate.threads astate.ownership proc_data astate.threads astate.ownership proc_data
in in
let astate' = let astate' =
match HilExp.get_access_paths assume_exp with match HilExp.get_access_exprs assume_exp with
| [access_path] | [access_expr]
-> ( -> (
let access_path = AccessExpression.to_access_path access_expr in
let choices = AttributeMapDomain.get_choices access_path astate.attribute_map in let choices = AttributeMapDomain.get_choices access_path astate.attribute_map in
match eval_bexp access_path assume_exp with match eval_bexp access_path assume_exp with
| Some bool_value -> | Some bool_value ->

@ -466,7 +466,7 @@ module StabilityDomain = struct
let add_wobbly_path_exp exp paths = let add_wobbly_path_exp exp paths =
let access_paths = HilExp.get_access_paths exp in let access_paths = AccessExpression.to_access_paths (HilExp.get_access_exprs exp) in
List.fold ~f:(fun acc p -> add_path p acc) access_paths ~init:paths List.fold ~f:(fun acc p -> add_path p acc) access_paths ~init:paths
@ -529,9 +529,9 @@ module StabilityDomain = struct
| Some formal_index -> ( | Some formal_index -> (
match List.nth actuals formal_index with match List.nth actuals formal_index with
| Some actual_exp -> ( | Some actual_exp -> (
match HilExp.get_access_paths actual_exp with match HilExp.get_access_exprs actual_exp with
| [actual] -> | [actual] ->
AccessPath.append actual accesses AccessPath.append (AccessExpression.to_access_path actual) accesses
| _ -> | _ ->
path ) path )
| None -> | None ->

@ -449,39 +449,46 @@ module Make (TaintSpecification : TaintSpec.S) = struct
(* not all sinks are function calls; we might want to treat an array or field access as a (* not all sinks are function calls; we might want to treat an array or field access as a
sink too. do this by pretending an access is a call to a dummy function and using the sink too. do this by pretending an access is a call to a dummy function and using the
existing machinery for adding function call sinks *) existing machinery for adding function call sinks *)
let add_sinks_for_access_path (_, accesses) loc astate = let add_sinks_for_access_path access_expr loc astate =
let add_sinks_for_access astate_acc = function let rec add_sinks_for_access astate_acc = function
| AccessPath.FieldAccess _ | AccessPath.ArrayAccess (_, []) -> | AccessExpression.Base _ ->
astate_acc astate_acc
| AccessPath.ArrayAccess (_, indexes) -> | AccessExpression.FieldOffset (ae, _)
| ArrayOffset (ae, _, [])
| Dereference ae
| AddressOf ae ->
add_sinks_for_access astate_acc ae
| AccessExpression.ArrayOffset (ae, _, indexes) ->
let dummy_call_site = CallSite.make BuiltinDecl.__array_access loc in let dummy_call_site = CallSite.make BuiltinDecl.__array_access loc in
let dummy_actuals = let dummy_actuals =
List.map List.map ~f:(fun index_ae -> HilExp.AccessExpression index_ae) indexes
~f:(fun index_ap ->
HilExp.AccessExpression (AccessExpression.of_access_path index_ap) )
indexes
in in
let sinks = let sinks =
TraceDomain.Sink.get dummy_call_site dummy_actuals proc_data.ProcData.tenv TraceDomain.Sink.get dummy_call_site dummy_actuals proc_data.ProcData.tenv
in in
match sinks with let astate_acc_result =
| None -> match sinks with
astate_acc | None ->
| Some sink -> astate_acc
add_sink sink dummy_actuals astate proc_data dummy_call_site | Some sink ->
add_sink sink dummy_actuals astate proc_data dummy_call_site
in
add_sinks_for_access astate_acc_result ae
in in
List.fold ~f:add_sinks_for_access ~init:astate accesses add_sinks_for_access astate access_expr
in in
let add_sources_for_access_path (((var, _), _) as ap) loc astate = let add_sources_for_access_path access_expr loc astate =
let var, _ = AccessExpression.get_base access_expr in
if Var.is_global var then if Var.is_global var then
let dummy_call_site = CallSite.make BuiltinDecl.__global_access loc in let dummy_call_site = CallSite.make BuiltinDecl.__global_access loc in
match match
TraceDomain.Source.get dummy_call_site TraceDomain.Source.get dummy_call_site [HilExp.AccessExpression access_expr]
[HilExp.AccessExpression (AccessExpression.of_access_path ap)]
proc_data.tenv proc_data.tenv
with with
| Some {TraceDomain.Source.source} -> | Some {TraceDomain.Source.source} ->
let access_path = AccessPath.Abs.Exact ap in let access_path =
AccessPath.Abs.Exact (AccessExpression.to_access_path access_expr)
in
let trace, subtree = let trace, subtree =
Option.value ~default:TaintDomain.empty_node Option.value ~default:TaintDomain.empty_node
(TaintDomain.get_node access_path astate) (TaintDomain.get_node access_path astate)
@ -495,16 +502,16 @@ module Make (TaintSpecification : TaintSpec.S) = struct
let add_sources_sinks_for_exp exp loc astate = let add_sources_sinks_for_exp exp loc astate =
match exp with match exp with
| HilExp.AccessExpression access_expr -> | HilExp.AccessExpression access_expr ->
let access_path = AccessExpression.to_access_path access_expr in add_sinks_for_access_path access_expr loc astate
add_sinks_for_access_path access_path loc astate |> add_sources_for_access_path access_expr loc
|> add_sources_for_access_path access_path loc
| _ -> | _ ->
astate astate
in in
let exec_write lhs_access_path rhs_exp astate = let exec_write lhs_access_expr rhs_exp astate =
let rhs_node = let rhs_node =
Option.value (hil_exp_get_node rhs_exp astate proc_data) ~default:TaintDomain.empty_node Option.value (hil_exp_get_node rhs_exp astate proc_data) ~default:TaintDomain.empty_node
in in
let lhs_access_path = AccessExpression.to_access_path lhs_access_expr in
TaintDomain.add_node (AccessPath.Abs.Exact lhs_access_path) rhs_node astate TaintDomain.add_node (AccessPath.Abs.Exact lhs_access_path) rhs_node astate
in in
match instr with match instr with
@ -522,9 +529,8 @@ module Make (TaintSpecification : TaintSpec.S) = struct
`return null` in a void function *) `return null` in a void function *)
astate astate
| Assign (lhs_access_expr, rhs_exp, loc) -> | Assign (lhs_access_expr, rhs_exp, loc) ->
let lhs_access_path = AccessExpression.to_access_path lhs_access_expr in
add_sources_sinks_for_exp rhs_exp loc astate add_sources_sinks_for_exp rhs_exp loc astate
|> add_sinks_for_access_path lhs_access_path loc |> exec_write lhs_access_path rhs_exp |> add_sinks_for_access_path lhs_access_expr loc |> exec_write lhs_access_expr rhs_exp
| Assume (assume_exp, _, _, loc) -> | Assume (assume_exp, _, _, loc) ->
add_sources_sinks_for_exp assume_exp loc astate add_sources_sinks_for_exp assume_exp loc astate
| Call (ret_opt, Direct called_pname, actuals, _, callee_loc) -> | Call (ret_opt, Direct called_pname, actuals, _, callee_loc) ->
@ -614,18 +620,16 @@ module Make (TaintSpecification : TaintSpec.S) = struct
match (* treat unknown calls to C++ operator= as assignment *) match (* treat unknown calls to C++ operator= as assignment *)
actuals with actuals with
| [(AccessExpression lhs_access_expr); rhs_exp] -> | [(AccessExpression lhs_access_expr); rhs_exp] ->
exec_write (AccessExpression.to_access_path lhs_access_expr) rhs_exp access_tree exec_write lhs_access_expr rhs_exp access_tree
| [(AccessExpression lhs_access_expr); rhs_exp; (HilExp.AccessExpression access_expr)] | [(AccessExpression lhs_access_expr); rhs_exp; (HilExp.AccessExpression access_expr)]
-> ( -> (
let dummy_ret_access_path = AccessExpression.to_access_path access_expr in let dummy_ret_access_expr = access_expr in
match dummy_ret_access_path with match dummy_ret_access_expr with
| (Var.ProgramVar pvar, _), [] when Pvar.is_frontend_tmp pvar -> | AccessExpression.Base (Var.ProgramVar pvar, _) when Pvar.is_frontend_tmp pvar ->
(* the frontend translates operator=(x, y) as operator=(x, y, dummy_ret) when (* the frontend translates operator=(x, y) as operator=(x, y, dummy_ret) when
operator= returns a value type *) operator= returns a value type *)
exec_write exec_write lhs_access_expr rhs_exp access_tree
(AccessExpression.to_access_path lhs_access_expr) |> exec_write dummy_ret_access_expr rhs_exp
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 L.internal_error "Unexpected call to operator= %a in %a" HilInstr.pp instr
Typ.Procname.pp callee_pname ; Typ.Procname.pp callee_pname ;

@ -0,0 +1,71 @@
/*
* 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.
*/
#include <mutex>
namespace dereferencing {
struct B {
int c;
};
struct A {
B b;
};
struct X {
int w;
int u;
X* x1;
X** x2;
A a;
};
class Basic {
public:
Basic() {}
void pointer_deref_race(int* v1) { (*v1)++; } // HIL: *(&v1) := *(&v1) + 1
void pointer_arith_ok(int* v2) { v2++; } // HIL: &v2 := &v2 + 1
void value_ok(int v3) { v3++; } // HIL: &v3 := &v3 + 1
void field_race(int& f) { f++; } // HIL: *(&f) := *(&f) + 1
void mixed_deref_race(X& xparam) {
xparam.x1->w++; // HIL: &xparam.x1.w := &xparam.x1.w + 1
(*xparam.x1).u++; // HIL: &xparam.x1.u := &xparam.x1.u + 1
(**xparam.x2).a.b.c++; // HIL:*(&xparam.x2).a.b.c := *(&xparam.x2).a.b.c + 1
}
void call1() {
pointer_deref_race(&p); // race - FalseNegative
pointer_arith_ok(&q); // no race
value_ok(h); // no race
field_race(g); // race - FalseNegative
mixed_deref_race(x); // race
}
int test_lock() {
mutex_.lock();
call1();
}
int test_unlock() { call1(); }
private:
int g;
int h;
int p;
int q;
X x;
std::mutex mutex_;
};
} // namespace dereferencing

@ -3,6 +3,12 @@ codetoanalyze/cpp/racerd/basics.cpp, basics::Basic_get4, 0, LOCK_CONSISTENCY_VIO
codetoanalyze/cpp/racerd/basics.cpp, basics::Basic_get5, 0, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,call to basics::Basic_get_private_suspiciously_read,access to `&this.suspiciously_read`,<Write trace>,access to `&this.suspiciously_read`] codetoanalyze/cpp/racerd/basics.cpp, basics::Basic_get5, 0, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,call to basics::Basic_get_private_suspiciously_read,access to `&this.suspiciously_read`,<Write trace>,access to `&this.suspiciously_read`]
codetoanalyze/cpp/racerd/basics.cpp, basics::Basic_test_double_lock_bad, 0, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,access to `&this.single_lock_suspiciously_read`,<Write trace>,access to `&this.single_lock_suspiciously_read`] codetoanalyze/cpp/racerd/basics.cpp, basics::Basic_test_double_lock_bad, 0, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,access to `&this.single_lock_suspiciously_read`,<Write trace>,access to `&this.single_lock_suspiciously_read`]
codetoanalyze/cpp/racerd/constructor_ownership.cpp, constructors::TSL_not_locked_race, 0, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,call to constructors::BSS_toJson_race,call to constructors::dynamic_operator=,access to `&this.type_`,<Write trace>,call to constructors::BSS_toJson_race,call to constructors::dynamic_operator=,access to `&this.type_`] codetoanalyze/cpp/racerd/constructor_ownership.cpp, constructors::TSL_not_locked_race, 0, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,call to constructors::BSS_toJson_race,call to constructors::dynamic_operator=,access to `&this.type_`,<Write trace>,call to constructors::BSS_toJson_race,call to constructors::dynamic_operator=,access to `&this.type_`]
codetoanalyze/cpp/racerd/dereferencing.cpp, dereferencing::Basic_call1, 5, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,call to dereferencing::Basic_mixed_deref_race,access to `&xparam.x1.u`,<Write trace>,call to dereferencing::Basic_call1,call to dereferencing::Basic_mixed_deref_race,access to `&xparam.x1.u`]
codetoanalyze/cpp/racerd/dereferencing.cpp, dereferencing::Basic_call1, 5, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,call to dereferencing::Basic_mixed_deref_race,access to `&xparam.x2.a.b.c`,<Write trace>,call to dereferencing::Basic_call1,call to dereferencing::Basic_mixed_deref_race,access to `&xparam.x2.a.b.c`]
codetoanalyze/cpp/racerd/dereferencing.cpp, dereferencing::Basic_call1, 5, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,call to dereferencing::Basic_mixed_deref_race,access to `&xparam.x1.w`,<Write trace>,call to dereferencing::Basic_call1,call to dereferencing::Basic_mixed_deref_race,access to `&xparam.x1.w`]
codetoanalyze/cpp/racerd/dereferencing.cpp, dereferencing::Basic_test_unlock, 0, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,call to dereferencing::Basic_call1,call to dereferencing::Basic_mixed_deref_race,access to `&xparam.x1.w`,<Write trace>,call to dereferencing::Basic_call1,call to dereferencing::Basic_mixed_deref_race,access to `&xparam.x1.w`]
codetoanalyze/cpp/racerd/dereferencing.cpp, dereferencing::Basic_test_unlock, 0, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,call to dereferencing::Basic_call1,call to dereferencing::Basic_mixed_deref_race,access to `&xparam.x2.a.b.c`,<Write trace>,call to dereferencing::Basic_call1,call to dereferencing::Basic_mixed_deref_race,access to `&xparam.x2.a.b.c`]
codetoanalyze/cpp/racerd/dereferencing.cpp, dereferencing::Basic_test_unlock, 0, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,call to dereferencing::Basic_call1,call to dereferencing::Basic_mixed_deref_race,access to `&xparam.x1.u`,<Write trace>,call to dereferencing::Basic_call1,call to dereferencing::Basic_mixed_deref_race,access to `&xparam.x1.u`]
codetoanalyze/cpp/racerd/locals_ownership.cpp, locals::Ownership_test2_bad, 5, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,access to `&x.f`,<Write trace>,access to `&x.f`] codetoanalyze/cpp/racerd/locals_ownership.cpp, locals::Ownership_test2_bad, 5, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,access to `&x.f`,<Write trace>,access to `&x.f`]
codetoanalyze/cpp/racerd/locals_ownership.cpp, locals::Ownership_test3_bad, 5, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,access to `&x.f`,<Write trace>,access to `&x.f`] codetoanalyze/cpp/racerd/locals_ownership.cpp, locals::Ownership_test3_bad, 5, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,access to `&x.f`,<Write trace>,access to `&x.f`]
codetoanalyze/cpp/racerd/lock_guard.cpp, basics::LockGuard_get2, 3, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,access to `&this.suspiciously_written`,<Write trace>,access to `&this.suspiciously_written`] codetoanalyze/cpp/racerd/lock_guard.cpp, basics::LockGuard_get2, 3, LOCK_CONSISTENCY_VIOLATION, [<Read trace>,access to `&this.suspiciously_written`,<Write trace>,access to `&this.suspiciously_written`]

Loading…
Cancel
Save