[thread-safety] refactoring to make it easier to add container reads

Summary: Adding new ContainerRead access kind + utilities for creating it.

Reviewed By: da319

Differential Revision: D5493322

fbshipit-source-id: 4ce6704
master
Sam Blackshear 7 years ago committed by Facebook Github Bot
parent c2207f5b06
commit df3590c407

@ -42,6 +42,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
type thread_model = Threaded | Unknown | ThreadedIfTrue type thread_model = Threaded | Unknown | ThreadedIfTrue
type container_access_model = ContainerRead | ContainerWrite
let is_thread_utils_type java_pname = let is_thread_utils_type java_pname =
let pn = Typ.Procname.java_get_class_name java_pname in let pn = Typ.Procname.java_get_class_name java_pname in
String.is_suffix ~suffix:"ThreadUtils" pn || String.is_suffix ~suffix:"ThreadUtil" pn String.is_suffix ~suffix:"ThreadUtils" pn || String.is_suffix ~suffix:"ThreadUtil" pn
@ -132,6 +134,36 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| _ | _
-> Unknown -> Unknown
let get_container_access pn tenv =
match pn with
| Typ.Procname.Java java_pname
-> let typename = Typ.Name.Java.from_string (Typ.Procname.java_get_class_name java_pname) in
let get_container_access_ typename _ =
match (Typ.Name.name typename, Typ.Procname.java_get_method java_pname) with
| ( ("android.util.SparseArray" | "android.support.v4.util.SparseArrayCompat")
, ( "append" | "clear" | "delete" | "put" | "remove" | "removeAt" | "removeAtRange"
| "setValueAt" ) )
-> Some ContainerWrite
| ( "android.support.v4.util.SimpleArrayMap"
, ("clear" | "ensureCapacity" | "put" | "putAll" | "remove" | "removeAt" | "setValueAt")
)
-> Some ContainerWrite
| "android.support.v4.util.Pools$SimplePool", ("acquire" | "release")
-> Some ContainerWrite
| "java.util.List", ("add" | "addAll" | "clear" | "remove" | "set")
-> Some ContainerWrite
| "java.util.Map", ("clear" | "put" | "putAll" | "remove")
-> Some ContainerWrite
| "java.util.Map", "get" when false
-> (* temporary measure to silence compiler warning *)
Some ContainerRead
| _
-> None
in
PatternMatch.supertype_find_map_opt tenv get_container_access_ typename
| _
-> None
let add_conditional_ownership_attribute access_path formal_map attribute_map attributes = let add_conditional_ownership_attribute access_path formal_map attribute_map attributes =
match FormalMap.get_formal_index (fst access_path) formal_map with match FormalMap.get_formal_index (fst access_path) formal_map with
| Some formal_index when not (is_owned access_path attribute_map) | Some formal_index when not (is_owned access_path attribute_map)
@ -392,33 +424,6 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
is_allocation pname || is_owned_in_library pname is_allocation pname || is_owned_in_library pname
|| PatternMatch.override_exists is_owned_in_library tenv pname || PatternMatch.override_exists is_owned_in_library tenv pname
let is_container_write pn tenv =
match pn with
| Typ.Procname.Java java_pname
-> let typename = Typ.Name.Java.from_string (Typ.Procname.java_get_class_name java_pname) in
let is_container_write_ typename _ =
match (Typ.Name.name typename, Typ.Procname.java_get_method java_pname) with
| ( ("android.util.SparseArray" | "android.support.v4.util.SparseArrayCompat")
, ( "append" | "clear" | "delete" | "put" | "remove" | "removeAt" | "removeAtRange"
| "setValueAt" ) )
-> true
| ( "android.support.v4.util.SimpleArrayMap"
, ("clear" | "ensureCapacity" | "put" | "putAll" | "remove" | "removeAt" | "setValueAt")
)
-> true
| "android.support.v4.util.Pools$SimplePool", ("acquire" | "release")
-> true
| "java.util.List", ("add" | "addAll" | "clear" | "remove" | "set")
-> true
| "java.util.Map", ("clear" | "put" | "putAll" | "remove")
-> true
| _
-> false
in
PatternMatch.supertype_exists tenv is_container_write_ typename
| _
-> false
let is_threadsafe_collection pn tenv = let is_threadsafe_collection pn tenv =
match pn with match pn with
| Typ.Procname.Java java_pname | Typ.Procname.Java java_pname
@ -479,8 +484,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
Some (true, false, false, callee_accesses, AttributeSetDomain.empty, escapee_formals) Some (true, false, false, callee_accesses, AttributeSetDomain.empty, escapee_formals)
let get_summary caller_pdesc callee_pname actuals callee_loc tenv = let get_summary caller_pdesc callee_pname actuals callee_loc tenv =
if is_container_write callee_pname tenv then match get_container_access callee_pname tenv with
let receiver_ap = | Some ContainerWrite
-> let receiver_ap =
match List.hd actuals with match List.hd actuals with
| Some HilExp.AccessPath receiver_ap | Some HilExp.AccessPath receiver_ap
-> receiver_ap -> receiver_ap
@ -489,7 +495,10 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
Typ.Procname.pp callee_pname Typ.Procname.pp callee_pname
in in
make_container_write callee_pname actuals receiver_ap callee_loc tenv make_container_write callee_pname actuals receiver_ap callee_loc tenv
else Summary.read_summary caller_pdesc callee_pname | Some ContainerRead
-> failwith "Container reads not yet supported"
| None
-> Summary.read_summary caller_pdesc callee_pname
(* return true if the given procname boxes a primitive type into a reference type *) (* return true if the given procname boxes a primitive type into a reference type *)
let is_box = function let is_box = function
@ -1064,7 +1073,7 @@ let pp_access fmt sink =
match ThreadSafetyDomain.PathDomain.Sink.kind sink with match ThreadSafetyDomain.PathDomain.Sink.kind sink with
| Read access_path | Write access_path | Read access_path | Write access_path
-> F.fprintf fmt "%a" (MF.wrap_monospaced AccessPath.pp_access_list) (snd access_path) -> F.fprintf fmt "%a" (MF.wrap_monospaced AccessPath.pp_access_list) (snd access_path)
| ContainerWrite (access_path, access_pname) | ContainerRead (access_path, access_pname) | ContainerWrite (access_path, access_pname)
-> pp_container_access fmt (access_path, access_pname) -> pp_container_access fmt (access_path, access_pname)
| InterfaceCall _ as access | InterfaceCall _ as access
-> F.fprintf fmt "%a" ThreadSafetyDomain.Access.pp access -> F.fprintf fmt "%a" ThreadSafetyDomain.Access.pp access
@ -1076,6 +1085,10 @@ let desc_of_sink sink =
-> if Typ.Procname.equal sink_pname Typ.Procname.empty_block then -> if Typ.Procname.equal sink_pname Typ.Procname.empty_block then
F.asprintf "access to %a" pp_access sink F.asprintf "access to %a" pp_access sink
else F.asprintf "call to %a" Typ.Procname.pp sink_pname else F.asprintf "call to %a" Typ.Procname.pp sink_pname
| ContainerRead (access_path, access_pname)
-> if Typ.Procname.equal sink_pname access_pname then
F.asprintf "Read of %a" pp_container_access (access_path, access_pname)
else F.asprintf "call to %a" Typ.Procname.pp sink_pname
| ContainerWrite (access_path, access_pname) | ContainerWrite (access_path, access_pname)
-> if Typ.Procname.equal sink_pname access_pname then -> if Typ.Procname.equal sink_pname access_pname then
F.asprintf "Write to %a" pp_container_access (access_path, access_pname) F.asprintf "Write to %a" pp_container_access (access_path, access_pname)
@ -1255,7 +1268,7 @@ let report_unsafe_accesses aggregated_access_map =
match TraceElem.kind access with match TraceElem.kind access with
| Access.Write _ | Access.ContainerWrite _ | Access.Write _ | Access.ContainerWrite _
-> Typ.Procname.Set.mem pname reported_writes -> Typ.Procname.Set.mem pname reported_writes
| Access.Read _ | Access.Read _ | Access.ContainerRead _
-> Typ.Procname.Set.mem pname reported_reads -> Typ.Procname.Set.mem pname reported_reads
| Access.InterfaceCall _ | Access.InterfaceCall _
-> false -> false
@ -1266,7 +1279,7 @@ let report_unsafe_accesses aggregated_access_map =
| Access.Write _ | Access.ContainerWrite _ | Access.Write _ | Access.ContainerWrite _
-> let reported_writes = Typ.Procname.Set.add pname reported.reported_writes in -> let reported_writes = Typ.Procname.Set.add pname reported.reported_writes in
{reported with reported_writes; reported_sites} {reported with reported_writes; reported_sites}
| Access.Read _ | Access.Read _ | Access.ContainerRead _
-> let reported_reads = Typ.Procname.Set.add pname reported.reported_reads in -> let reported_reads = Typ.Procname.Set.add pname reported.reported_reads in
{reported with reported_reads; reported_sites} {reported with reported_reads; reported_sites}
| Access.InterfaceCall _ | Access.InterfaceCall _
@ -1298,7 +1311,7 @@ let report_unsafe_accesses aggregated_access_map =
| (Access.Write _ | ContainerWrite _), AccessPrecondition.Protected _ | (Access.Write _ | ContainerWrite _), AccessPrecondition.Protected _
-> (* protected write, do nothing *) -> (* protected write, do nothing *)
reported_acc reported_acc
| Access.Read _, AccessPrecondition.Unprotected _ | (Access.Read _ | ContainerRead _), AccessPrecondition.Unprotected _
-> (* unprotected read. report all writes as conflicts for java *) -> (* unprotected read. report all writes as conflicts for java *)
(* for c++ filter out unprotected writes *) (* for c++ filter out unprotected writes *)
let is_cpp_protected_write pre = let is_cpp_protected_write pre =
@ -1322,7 +1335,7 @@ let report_unsafe_accesses aggregated_access_map =
~conflicts:(List.map ~f:(fun (access, _, _, _, _) -> access) all_writes) ~conflicts:(List.map ~f:(fun (access, _, _, _, _) -> access) all_writes)
access ; access ;
update_reported access pname reported_acc ) update_reported access pname reported_acc )
| Access.Read _, AccessPrecondition.Protected excl | (Access.Read _ | ContainerRead _), AccessPrecondition.Protected excl
-> (* protected read. -> (* protected read.
report unprotected writes and opposite protected writes as conflicts report unprotected writes and opposite protected writes as conflicts
Thread and Lock are opposites of one another, and Thread and Lock are opposites of one another, and

@ -14,6 +14,7 @@ module Access = struct
type t = type t =
| Read of AccessPath.Raw.t | Read of AccessPath.Raw.t
| Write of AccessPath.Raw.t | Write of AccessPath.Raw.t
| ContainerRead of AccessPath.Raw.t * Typ.Procname.t
| ContainerWrite of AccessPath.Raw.t * Typ.Procname.t | ContainerWrite of AccessPath.Raw.t * Typ.Procname.t
| InterfaceCall of Typ.Procname.t | InterfaceCall of Typ.Procname.t
[@@deriving compare] [@@deriving compare]
@ -22,7 +23,10 @@ module Access = struct
if is_write then Write access_path else Read access_path if is_write then Write access_path else Read access_path
let get_access_path = function let get_access_path = function
| Read access_path | Write access_path | ContainerWrite (access_path, _) | Read access_path
| Write access_path
| ContainerWrite (access_path, _)
| ContainerRead (access_path, _)
-> Some access_path -> Some access_path
| InterfaceCall _ | InterfaceCall _
-> None -> None
@ -34,6 +38,9 @@ module Access = struct
-> F.fprintf fmt "Read of %a" AccessPath.Raw.pp access_path -> F.fprintf fmt "Read of %a" AccessPath.Raw.pp access_path
| Write access_path | Write access_path
-> F.fprintf fmt "Write to %a" AccessPath.Raw.pp access_path -> F.fprintf fmt "Write to %a" AccessPath.Raw.pp access_path
| ContainerRead (access_path, pname)
-> F.fprintf fmt "Read of container %a via %a" AccessPath.Raw.pp access_path Typ.Procname.pp
pname
| ContainerWrite (access_path, pname) | ContainerWrite (access_path, pname)
-> F.fprintf fmt "Write to container %a via %a" AccessPath.Raw.pp access_path Typ.Procname.pp -> F.fprintf fmt "Write to container %a via %a" AccessPath.Raw.pp access_path Typ.Procname.pp
pname pname
@ -47,10 +54,18 @@ module TraceElem = struct
type t = {site: CallSite.t; kind: Kind.t} [@@deriving compare] type t = {site: CallSite.t; kind: Kind.t} [@@deriving compare]
let is_write {kind} = let is_write {kind} =
match kind with InterfaceCall _ | Read _ -> false | ContainerWrite _ | Write _ -> true match kind with
| InterfaceCall _ | Read _ | ContainerRead _
-> false
| ContainerWrite _ | Write _
-> true
let is_container_write {kind} = let is_container_write {kind} =
match kind with InterfaceCall _ | Read _ | Write _ -> false | ContainerWrite _ -> true match kind with
| InterfaceCall _ | Read _ | Write _ | ContainerRead _
-> false
| ContainerWrite _
-> true
let call_site {site} = site let call_site {site} = site

@ -14,6 +14,7 @@ module Access : sig
type t = type t =
| Read of AccessPath.Raw.t (** Field or array read *) | Read of AccessPath.Raw.t (** Field or array read *)
| Write of AccessPath.Raw.t (** Field or array write *) | Write of AccessPath.Raw.t (** Field or array write *)
| ContainerRead of AccessPath.Raw.t * Typ.Procname.t (** Read of container object *)
| ContainerWrite of AccessPath.Raw.t * Typ.Procname.t (** Write to container object *) | ContainerWrite of AccessPath.Raw.t * Typ.Procname.t (** Write to container object *)
| InterfaceCall of Typ.Procname.t | InterfaceCall of Typ.Procname.t
(** Call to method of interface not annotated with @ThreadSafe *) (** Call to method of interface not annotated with @ThreadSafe *)

Loading…
Cancel
Save