diff --git a/infer/src/IR/Tenv.ml b/infer/src/IR/Tenv.ml index 5ade4f195..7bddb3008 100644 --- a/infer/src/IR/Tenv.ml +++ b/infer/src/IR/Tenv.ml @@ -164,15 +164,3 @@ let store_global tenv = frontend and backend run in the same process *) global_tenv := Some tenv ; store_to_filename tenv global_tenv_path - - -exception Found of Typ.Name.t - -let language_is tenv lang = - match TypenameHash.iter (fun n -> raise (Found n)) tenv with - | () -> - false - | exception Found (JavaClass _) -> - Language.equal lang Java - | exception Found _ -> - Language.equal lang Clang diff --git a/infer/src/IR/Tenv.mli b/infer/src/IR/Tenv.mli index edff4b6a2..cf5dc025c 100644 --- a/infer/src/IR/Tenv.mli +++ b/infer/src/IR/Tenv.mli @@ -47,9 +47,6 @@ val add_field : t -> Typ.Name.t -> Typ.Struct.field -> unit val pp : Format.formatter -> t -> unit [@@warning "-32"] (** print a type environment *) -val language_is : t -> Language.t -> bool -(** Test the language from which the types in the tenv were translated *) - type per_file = Global | FileLocal of t val pp_per_file : Format.formatter -> per_file -> unit diff --git a/infer/src/IR/Var.ml b/infer/src/IR/Var.ml index 90f427fce..fc6196aac 100644 --- a/infer/src/IR/Var.ml +++ b/infer/src/IR/Var.ml @@ -15,11 +15,13 @@ type t = LogicalVar of Ident.t | ProgramVar of Pvar.t [@@deriving compare] let equal = [%compare.equal: t] let compare_modulo_this x y = - match (x, y) with - | ProgramVar i, ProgramVar j -> - Pvar.compare_modulo_this i j - | _, _ -> - compare x y + if phys_equal x y then 0 + else + match (x, y) with + | ProgramVar i, ProgramVar j -> + Pvar.compare_modulo_this i j + | _, _ -> + compare x y let of_id id = LogicalVar id diff --git a/infer/src/concurrency/RacerD.ml b/infer/src/concurrency/RacerD.ml index ed8716c9d..ea77f0ebc 100644 --- a/infer/src/concurrency/RacerD.ml +++ b/infer/src/concurrency/RacerD.ml @@ -726,8 +726,6 @@ let analyze_procedure {Callbacks.proc_desc; tenv; summary} = else Payload.update_summary empty_post summary -module AccessListMap = Caml.Map.Make (RacerDDomain.Access) - type conflict = RacerDDomain.TraceElem.t type report_kind = @@ -1016,6 +1014,81 @@ let empty_reported = {reported_sites; reported_reads; reported_writes; reported_unannotated_calls} +(* decide if we should throw away a path before doing safety analysis + for now, just check for whether the access is within a switch-map + that is auto-generated by Java. *) +let should_filter_access path_opt = + let check_access = function + | AccessPath.ArrayAccess _ -> + false + | AccessPath.FieldAccess fld -> + String.is_substring ~substring:"$SwitchMap" (Typ.Fieldname.to_string fld) + in + Option.value_map path_opt ~default:false ~f:(fun (_, path) -> List.exists path ~f:check_access) + + +(** + Map containing reported accesses, which groups them in lists, by abstract location. + The equivalence relation used for grouping them is equality of access paths. + This is slightly complicated because local variables contain the pname of the function declaring + them. Here we want a purely name-based comparison, and in particular that [this == this] + regardless the method declaring it. Hence the redefined comparison functions. +*) +module ReportMap : sig + type t + + val empty : t + + val add : reported_access -> t -> t + + val fold : (reported_access list -> 'a -> 'a) -> t -> 'a -> 'a +end = struct + module PathModuloThis : Caml.Map.OrderedType with type t = AccessPath.t = struct + type t = AccessPath.t + + type var_ = Var.t + + let compare_var_ = Var.compare_modulo_this + + let compare = [%compare: (var_ * Typ.t) * AccessPath.access list] + end + + module Key = struct + type t = + | Location of PathModuloThis.t + | Container of PathModuloThis.t + | Call of Typ.Procname.t + [@@deriving compare] + + let of_access (access : RacerDDomain.Access.t) = + match access with + | Read ap | Write ap -> + Location ap + | ContainerRead (ap, _) | ContainerWrite (ap, _) -> + Container ap + | InterfaceCall pn -> + Call pn + end + + module M = Caml.Map.Make (Key) + + type t = reported_access list M.t + + let empty = M.empty + + let add (rep : reported_access) map = + let access = RacerDDomain.TraceElem.kind rep.snapshot.access in + if RacerDDomain.Access.get_access_path access |> should_filter_access then map + else + let k = Key.of_access access in + M.update k (function None -> Some [rep] | Some reps -> Some (rep :: reps)) map + + + let fold f map a = + let f _ v acc = f v acc in + M.fold f map a +end + (** Report accesses that may race with each other. Principles for race reporting. @@ -1044,7 +1117,7 @@ let empty_reported = currently not distinguishing different locks, and are treating "known to be confined to a thread" as if "known to be confined to UI thread". *) -let report_unsafe_accesses (aggregated_access_map : reported_access list AccessListMap.t) = +let report_unsafe_accesses (aggregated_access_map : ReportMap.t) = let open RacerDDomain in let open RacerDModels in let is_duplicate_report access pname @@ -1078,7 +1151,7 @@ let report_unsafe_accesses (aggregated_access_map : reported_access list AccessL {reported with reported_unannotated_calls; reported_sites} else reported in - let report_unsafe_access {snapshot; threads; tenv; procdesc; wobbly_paths} accesses reported_acc + let report_unsafe_access accesses reported_acc {snapshot; threads; tenv; procdesc; wobbly_paths} = let pname = Procdesc.get_proc_name procdesc in if is_duplicate_report snapshot.access pname reported_acc then reported_acc @@ -1129,32 +1202,25 @@ let report_unsafe_accesses (aggregated_access_map : reported_access list AccessL (* Do not report unprotected writes when an access can't run in parallel with itself, or for ObjC_Cpp *) reported_acc ) - | (Access.Read _ | ContainerRead _) when AccessSnapshot.is_unprotected snapshot -> + | (Access.Read _ | ContainerRead _) when AccessSnapshot.is_unprotected snapshot -> ( (* unprotected read. report all writes as conflicts for java. for c++ filter out unprotected writes *) - let is_cpp_protected_write snapshot = - Typ.Procname.is_java pname || not (AccessSnapshot.is_unprotected snapshot) - in - let is_conflict (snapshot : AccessSnapshot.t) other_thread = + let is_conflict {snapshot; threads= other_threads} = TraceElem.is_write snapshot.access && if Typ.Procname.is_java pname then - ThreadsDomain.is_any threads || ThreadsDomain.is_any other_thread - else is_cpp_protected_write snapshot - in - let all_writes = - List.filter - ~f:(fun {snapshot; threads= other_threads} -> is_conflict snapshot other_threads) - accesses + ThreadsDomain.is_any threads || ThreadsDomain.is_any other_threads + else not (AccessSnapshot.is_unprotected snapshot) in - if not (List.is_empty all_writes) then ( - let conflict = List.hd_exn all_writes in - report_thread_safety_violation tenv procdesc - ~make_description:(make_read_write_race_description ~read_is_sync:false conflict) - ~report_kind:(ReadWriteRace conflict.snapshot.access) snapshot.access threads - wobbly_paths ; - update_reported snapshot.access pname reported_acc ) - else reported_acc + match List.find ~f:is_conflict accesses with + | None -> + reported_acc + | Some conflict -> + report_thread_safety_violation tenv procdesc + ~make_description:(make_read_write_race_description ~read_is_sync:false conflict) + ~report_kind:(ReadWriteRace conflict.snapshot.access) snapshot.access threads + wobbly_paths ; + update_reported snapshot.access pname reported_acc ) | Access.Read _ | ContainerRead _ -> (* protected read. report unprotected writes and opposite protected writes as conflicts *) let can_conflict (snapshot1 : AccessSnapshot.t) (snapshot2 : AccessSnapshot.t) = @@ -1181,220 +1247,62 @@ let report_unsafe_accesses (aggregated_access_map : reported_access list AccessL update_reported snapshot.access pname reported_acc ) else reported_acc in - AccessListMap.fold - (fun _ (grouped_accesses : reported_access list) reported_acc -> - (* reset the reported reads and writes for each memory location *) - let reported = - { reported_acc with - reported_writes= Typ.Procname.Set.empty; reported_reads= Typ.Procname.Set.empty } - in - let class_has_mutex_member objc_cpp tenv = - let class_name = Typ.Procname.ObjC_Cpp.get_class_type_name objc_cpp in - let matcher = QualifiedCppName.Match.of_fuzzy_qual_names ["std::mutex"] in - Option.exists (Tenv.lookup tenv class_name) ~f:(fun class_str -> - (* check if the class contains a member of type std::mutex *) - List.exists class_str.Typ.Struct.fields ~f:(fun (_, ft, _) -> - Option.exists (Typ.name ft) ~f:(fun name -> - QualifiedCppName.Match.match_qualifiers matcher (Typ.Name.qual_name name) ) ) - ) - in - let should_report pdesc tenv = - match Procdesc.get_proc_name pdesc with - | Java _ -> - (* report if - - the method/class of the access is thread-safe - (or an override or superclass is), or - - any access is in a field marked thread-safe (or an override) *) - List.exists - ~f:(fun ({threads} : reported_access) -> ThreadsDomain.is_any threads) - grouped_accesses - && should_report_on_proc pdesc tenv - | ObjC_Cpp objc_cpp -> - (* do not report if a procedure is private *) - Procdesc.get_access pdesc <> PredSymb.Private - && (* report if the class has a mutex member *) - class_has_mutex_member objc_cpp tenv - | _ -> - false - in - let reportable_accesses = - List.filter ~f:(fun {tenv; procdesc} -> should_report procdesc tenv) grouped_accesses - in - List.fold - ~f:(fun acc access -> report_unsafe_access access reportable_accesses acc) - reportable_accesses ~init:reported ) - aggregated_access_map empty_reported - |> ignore - - -module type QuotientedAccessListMap = sig - type t - - val empty : t - - val add : RacerDDomain.Access.t -> reported_access -> t -> t - - val quotient : t -> reported_access list AccessListMap.t -end - -module SyntacticQuotientedAccessListMap : QuotientedAccessListMap = struct - module M = Caml.Map.Make (struct - type t = RacerDDomain.Access.t - - type var_ = Var.t - - let compare_var_ (u : Var.t) (v : Var.t) = - if phys_equal u v then 0 - else - match (u, v) with - | LogicalVar i, LogicalVar j -> - Ident.compare i j - | ProgramVar x, ProgramVar y -> - Pvar.compare_modulo_this x y - | _ -> - Pervasives.compare u v - - - let compare (x : t) (y : t) = - match (x, y) with - | (Read ap1 | Write ap1), (Read ap2 | Write ap2) - | ( (ContainerRead (ap1, _) | ContainerWrite (ap1, _)) - , (ContainerRead (ap2, _) | ContainerWrite (ap2, _)) ) -> - [%compare: (var_ * Typ.t) * AccessPath.access list] ap1 ap2 - | (InterfaceCall _ | Read _ | Write _ | ContainerRead _ | ContainerWrite _), _ -> - RacerDDomain.Access.compare x y - end) - - type t = reported_access list M.t - - let empty = M.empty - - let add k d m = - let ds = try M.find k m with Caml.Not_found -> [] in - M.add k (d :: ds) m - - - let quotient m = M.fold AccessListMap.add m AccessListMap.empty -end - -module MayAliasQuotientedAccessListMap : QuotientedAccessListMap = struct - type t = reported_access list AccessListMap.t - - let empty = AccessListMap.empty - - let add = AccessListMap.add - - let add k d m = - let ds = try AccessListMap.find k m with Caml.Not_found -> [] in - add k (d :: ds) m - - - let syntactic_equal_access_path tenv p1 p2 = - (* unsound, but effective: report that the containers alias if their access paths are - syntactically identical *) - match (fst p1, fst p2) with - | (Var.ProgramVar pvar1, typ1), (Var.ProgramVar pvar2, typ2) - when Pvar.is_this pvar1 && Pvar.is_this pvar2 - && ( Typ.equal typ1 typ2 - || Prover.Subtyping_check.check_subtype tenv typ1 typ2 - || Prover.Subtyping_check.check_subtype tenv typ2 typ1 ) -> - (* the `this` used in C.foo and C.bar will compare unequal if we're not careful `this` is - represented as a local pvar, and a local pvar contains its parent procedure name. Count - the `this`'s as equal if their types are compatible *) - AccessPath.equal_access_list (snd p1) (snd p2) - | _ -> - AccessPath.equal p1 p2 - - - (* equivalence relation computing whether two access paths may refer - to the same heap location. *) - let may_alias tenv p1 p2 = - let open AccessPath in - phys_equal p1 p2 - || - match (List.last_exn (snd p1), List.last_exn (snd p2)) with - | FieldAccess _, ArrayAccess _ | ArrayAccess _, FieldAccess _ -> - false - | _, _ -> - syntactic_equal_access_path tenv p1 p2 - - - (* take a results table and quotient it by the may_alias relation *) - let quotient acc_map = - let rec aux acc m = - if AccessListMap.is_empty m then acc - else - let k, vals = AccessListMap.min_binding m in - let tenv = - (List.find_exn vals ~f:(fun {snapshot} -> - RacerDDomain.Access.equal (RacerDDomain.TraceElem.kind snapshot.access) k )) - .tenv - in - (* assumption: the tenv for k is sufficient for k' too *) - let k_part, non_k_part = - AccessListMap.partition - (fun k' _ -> - match (k, k') with - | (Read ap1 | Write ap1), (Read ap2 | Write ap2) -> - may_alias tenv ap1 ap2 - | ( (ContainerRead (ap1, _) | ContainerWrite (ap1, _)) - , (ContainerRead (ap2, _) | ContainerWrite (ap2, _)) ) -> - syntactic_equal_access_path tenv ap1 ap2 - | _ -> - RacerDDomain.Access.equal k k' ) - m - in - if AccessListMap.is_empty k_part then L.(die InternalError) "may_alias is not reflexive!" ; - let k_accesses = AccessListMap.fold (fun _ v acc' -> List.append v acc') k_part [] in - let new_acc = AccessListMap.add k k_accesses acc in - aux new_acc non_k_part + let report_accesses_on_location (grouped_accesses : reported_access list) reported_acc = + (* reset the reported reads and writes for each memory location *) + let reported = + { reported_acc with + reported_writes= Typ.Procname.Set.empty; reported_reads= Typ.Procname.Set.empty } in - aux AccessListMap.empty acc_map -end - -(* decide if we should throw away a path before doing safety analysis - for now, just check for whether the access is within a switch-map - that is auto-generated by Java. *) -let should_filter_access access = - match RacerDDomain.Access.get_access_path access with - | Some (_, path) -> - let check_access_step = function - | AccessPath.ArrayAccess _ -> - false - | AccessPath.FieldAccess fld -> - String.is_substring ~substring:"$SwitchMap" (Typ.Fieldname.to_string fld) - in - List.exists path ~f:check_access_step - | None -> - false + let class_has_mutex_member objc_cpp tenv = + let class_name = Typ.Procname.ObjC_Cpp.get_class_type_name objc_cpp in + let matcher = QualifiedCppName.Match.of_fuzzy_qual_names ["std::mutex"] in + Option.exists (Tenv.lookup tenv class_name) ~f:(fun class_str -> + (* check if the class contains a member of type std::mutex *) + List.exists class_str.Typ.Struct.fields ~f:(fun (_, ft, _) -> + Option.exists (Typ.name ft) ~f:(fun name -> + QualifiedCppName.Match.match_qualifiers matcher (Typ.Name.qual_name name) ) ) ) + in + let should_report {tenv; procdesc} = + match Procdesc.get_proc_name procdesc with + | Java _ -> + List.exists + ~f:(fun ({threads} : reported_access) -> ThreadsDomain.is_any threads) + grouped_accesses + && should_report_on_proc procdesc tenv + | ObjC_Cpp objc_cpp -> + (* do not report if a procedure is private *) + Procdesc.get_access procdesc <> PredSymb.Private + && (* report if the class has a mutex member *) + class_has_mutex_member objc_cpp tenv + | _ -> + false + in + let reportable_accesses = List.filter ~f:should_report grouped_accesses in + List.fold ~f:(report_unsafe_access reportable_accesses) reportable_accesses ~init:reported + in + ReportMap.fold report_accesses_on_location aggregated_access_map empty_reported |> ignore (* create a map from [abstraction of a memory loc] -> accesses that may touch that memory loc. for now, our abstraction is an access path like x.f.g whose concretization is the set of memory cells that x.f.g may point to during execution *) -let make_results_table (module AccessListMap : QuotientedAccessListMap) file_env = +let make_results_table file_env = let open RacerDDomain in - let aggregate_post {threads; accesses; wobbly_paths} tenv procdesc acc = + let aggregate_post tenv procdesc acc {threads; accesses; wobbly_paths} = AccessDomain.fold (fun snapshot acc -> - let access_kind = TraceElem.kind snapshot.access in - if should_filter_access access_kind then acc - else - let reported_access : reported_access = - {threads; snapshot; tenv; procdesc; wobbly_paths} - in - AccessListMap.add access_kind reported_access acc ) + let reported_access : reported_access = + {threads; snapshot; tenv; procdesc; wobbly_paths} + in + ReportMap.add reported_access acc ) accesses acc in let aggregate_posts acc (tenv, proc_desc) = - match Payload.read proc_desc (Procdesc.get_proc_name proc_desc) with - | Some summary -> - aggregate_post summary tenv proc_desc acc - | None -> - acc + Payload.read proc_desc (Procdesc.get_proc_name proc_desc) + |> Option.fold ~init:acc ~f:(aggregate_post tenv proc_desc) in - List.fold ~f:aggregate_posts file_env ~init:AccessListMap.empty |> AccessListMap.quotient + List.fold ~f:aggregate_posts file_env ~init:ReportMap.empty (* aggregate all of the procedures in the file env by their declaring @@ -1410,10 +1318,8 @@ let aggregate_by_class file_env = | _ -> "unknown" in - let bucket = - try String.Map.find_exn acc classname with Not_found_s _ | Caml.Not_found -> [] - in - String.Map.set ~key:classname ~data:(proc :: bucket) acc ) + String.Map.update acc classname ~f:(function None -> [proc] | Some bucket -> proc :: bucket) + ) ~init:String.Map.empty @@ -1422,12 +1328,6 @@ let aggregate_by_class file_env = safety *) let file_analysis {Callbacks.procedures; source_file} = String.Map.iter - ~f:(fun class_env -> - let tenv = fst (List.hd_exn class_env) in - report_unsafe_accesses - (make_results_table - ( if Tenv.language_is tenv Clang then (module SyntacticQuotientedAccessListMap) - else (module MayAliasQuotientedAccessListMap) ) - class_env) ) + ~f:(fun class_env -> report_unsafe_accesses (make_results_table class_env)) (aggregate_by_class procedures) ; IssueLog.store Config.racerd_issues_dir_name source_file diff --git a/infer/src/concurrency/RacerDDomain.ml b/infer/src/concurrency/RacerDDomain.ml index 843afaf20..141978922 100644 --- a/infer/src/concurrency/RacerDDomain.ml +++ b/infer/src/concurrency/RacerDDomain.ml @@ -28,8 +28,6 @@ module Access = struct | InterfaceCall of Typ.Procname.t [@@deriving compare] - let equal = [%compare.equal: t] - let suffix_matches (_, accesses1) (_, accesses2) = match (List.rev accesses1, List.rev accesses2) with | access1 :: _, access2 :: _ -> diff --git a/infer/src/concurrency/RacerDDomain.mli b/infer/src/concurrency/RacerDDomain.mli index 77099c3f4..f5ca4f334 100644 --- a/infer/src/concurrency/RacerDDomain.mli +++ b/infer/src/concurrency/RacerDDomain.mli @@ -24,8 +24,6 @@ module Access : sig between the formals and actuals *) val get_access_path : t -> AccessPath.t option - - val equal : t -> t -> bool end module TraceElem : sig diff --git a/infer/tests/codetoanalyze/java/racerd/Containers.java b/infer/tests/codetoanalyze/java/racerd/Containers.java index a66274087..08c3393a3 100644 --- a/infer/tests/codetoanalyze/java/racerd/Containers.java +++ b/infer/tests/codetoanalyze/java/racerd/Containers.java @@ -88,8 +88,14 @@ class Containers { mListNobodyWrites.size(); } + List mListSyncWrites; + + synchronized void listSyncAddBad(String s) { + mListSyncWrites.add(s); + } + boolean listReadBad(String s) { - return mList.contains(s); + return mListSyncWrites.contains(s); } void accessSafeListOk(CopyOnWriteArrayList list, int index) { diff --git a/infer/tests/codetoanalyze/java/racerd/issues.exp b/infer/tests/codetoanalyze/java/racerd/issues.exp index b0d870bca..aa21519ba 100644 --- a/infer/tests/codetoanalyze/java/racerd/issues.exp +++ b/infer/tests/codetoanalyze/java/racerd/issues.exp @@ -26,26 +26,26 @@ codetoanalyze/java/racerd/Constructors.java, Constructors.singleton1Bad():Constr codetoanalyze/java/racerd/Constructors.java, Constructors.singleton2Bad():Constructors, 63, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [,access to `Constructors.Constructors.sSingleton2`,,access to `Constructors.Constructors.sSingleton2`] codetoanalyze/java/racerd/Constructors.java, Constructors.singleton2Bad():Constructors, 64, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [access to `Constructors.Constructors.sSingleton2`] codetoanalyze/java/racerd/Constructors.java, Constructors.singleton2Bad():Constructors, 66, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [,access to `Constructors.Constructors.sSingleton2`,,access to `Constructors.Constructors.sSingleton2`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToSimpleArrayMapBad(android.support.v4.util.SimpleArrayMap):void, 273, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `map` via call to `put`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToSparseArrayBad(android.util.SparseArray):void, 263, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `sparseArray` via call to `put`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToSparseArrayCompatBad(android.support.v4.util.SparseArrayCompat):void, 254, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `sparseArray` via call to `put`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.containerWrapperUnownedWriteBad(java.lang.Object):void, 161, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [call to Object ContainerWrapper.write(Object),call to Object ContainerWrapper._write(Object),Write to container `this.codetoanalyze.java.checkers.ContainerWrapper.children` via call to `add`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToSimpleArrayMapBad(android.support.v4.util.SimpleArrayMap):void, 279, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `map` via call to `put`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToSparseArrayBad(android.util.SparseArray):void, 269, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `sparseArray` via call to `put`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.addToSparseArrayCompatBad(android.support.v4.util.SparseArrayCompat):void, 260, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `sparseArray` via call to `put`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.containerWrapperUnownedWriteBad(java.lang.Object):void, 167, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [call to Object ContainerWrapper.write(Object),call to Object ContainerWrapper._write(Object),Write to container `this.codetoanalyze.java.checkers.ContainerWrapper.children` via call to `add`] codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listAddAllBad(java.util.Collection):void, 55, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mList` via call to `addAll`] codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listAddBad1(java.lang.String):void, 47, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mList` via call to `add`] codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listAddBad2(int,java.lang.String):void, 51, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mList` via call to `add`] codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listClearBad():void, 59, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mList` via call to `clear`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listReadBad(java.lang.String):boolean, 92, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [,Read of container `this.codetoanalyze.java.checkers.Containers.mList` via call to `contains`,,Write to container `this.codetoanalyze.java.checkers.Containers.mList` via call to `set`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listReadBad(java.lang.String):boolean, 98, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [,Read of container `this.codetoanalyze.java.checkers.Containers.mListSyncWrites` via call to `contains`,,Write to container `this.codetoanalyze.java.checkers.Containers.mListSyncWrites` via call to `add`] codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listRemoveBad1(int):void, 63, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mList` via call to `remove`] codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listRemoveBad2(java.lang.String):void, 67, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mList` via call to `remove`] codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listSetBad(int,java.lang.String):void, 75, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mList` via call to `set`] codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.listSubclassWriteBad(java.util.ArrayList,int):void, 79, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `list` via call to `remove`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapClearBad():void, 109, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mMap` via call to `clear`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapPutAllBad(java.util.Map):void, 113, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mMap` via call to `putAll`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapPutBad(java.lang.String,java.lang.String):void, 101, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mMap` via call to `put`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapRemoveBad(java.lang.String):void, 105, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mMap` via call to `remove`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapSubclassWriteBad(java.util.HashMap,java.lang.String):void, 131, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `m` via call to `remove`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.poolBad():void, 292, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.simplePool` via call to `release`] -codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.readSimpleArrayMap():int, 278, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [,Read of container `this.codetoanalyze.java.checkers.Containers.si_map` via call to `get`,,Write to container `this.codetoanalyze.java.checkers.Containers.si_map` via call to `put`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapClearBad():void, 115, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mMap` via call to `clear`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapPutAllBad(java.util.Map):void, 119, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mMap` via call to `putAll`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapPutBad(java.lang.String,java.lang.String):void, 107, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mMap` via call to `put`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapRemoveBad(java.lang.String):void, 111, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.mMap` via call to `remove`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.mapSubclassWriteBad(java.util.HashMap,java.lang.String):void, 137, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `m` via call to `remove`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.poolBad():void, 298, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [Write to container `this.codetoanalyze.java.checkers.Containers.simplePool` via call to `release`] +codetoanalyze/java/racerd/Containers.java, codetoanalyze.java.checkers.Containers.readSimpleArrayMap():int, 284, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [,Read of container `this.codetoanalyze.java.checkers.Containers.si_map` via call to `get`,,Write to container `this.codetoanalyze.java.checkers.Containers.si_map` via call to `put`] codetoanalyze/java/racerd/DeepOwnership.java, DeepOwnership.globalNotOwnedBad():void, 16, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [access to `DeepOwnership.DeepOwnership.global.DeepOwnership.next`] codetoanalyze/java/racerd/DeepOwnership.java, DeepOwnership.reassignBaseToGlobalBad():void, 22, THREAD_SAFETY_VIOLATION, no_bucket, ERROR, [access to `x.DeepOwnership.next`] codetoanalyze/java/racerd/Dispatch.java, codetoanalyze.java.checkers.Dispatch.callUnannotatedInterfaceBad(codetoanalyze.java.checkers.UnannotatedInterface):void, 49, INTERFACE_NOT_THREAD_SAFE, no_bucket, ERROR, [Call to un-annotated interface method void UnannotatedInterface.foo()]