[racerd] use concurrent context for reporting in C++/ObjC

Summary:
To emulate the `ThreadSafe` contract in C++/ObjC, reporting was gated behind a check that ensured a C++/ObjC class has a `std::mutex` member (plus other filters).  This is reasonable, but it has some drawbacks
- other locks may be used, and therefore must be added to the member check;
- locking mechanisms that use the object itself as a monitor cannot be modelled (`synchronized` in ObjC)

RacerD already has `ThreadsDomain` which models our guess on whether a method is expected to run in a concurrent context, and which in C++/ObjC boils down to whether the method non-transitively acquires a lock.  This should be a good enough indicator that the class should be checked regardless of whether the locks are member fields.  This diff gates the C++/ObjC check on that abstract property.

Reviewed By: dulmarod

Differential Revision: D19558355

fbshipit-source-id: 229d7ff82
master
Nikos Gorogiannis 5 years ago committed by Facebook Github Bot
parent 5510223850
commit 777eb33870

@ -55,8 +55,6 @@ let get_thread_assert_effect = function
module Clang : sig module Clang : sig
val get_lock_effect : Procname.t -> HilExp.t list -> lock_effect val get_lock_effect : Procname.t -> HilExp.t list -> lock_effect
val lock_types_matcher : QualifiedCppName.Match.quals_matcher
val is_recursive_lock_type : QualifiedCppName.t -> bool val is_recursive_lock_type : QualifiedCppName.t -> bool
end = struct end = struct
type lock_model = type lock_model =
@ -133,11 +131,6 @@ end = struct
, mk_matcher ["std::lock"] ) , mk_matcher ["std::lock"] )
let lock_types_matcher =
let class_names = List.map lock_models ~f:(fun mdl -> mdl.classname) in
QualifiedCppName.Match.of_fuzzy_qual_names class_names
(** C++ guard classes used for scope-based lock management. NB we pretend all classes below (** C++ guard classes used for scope-based lock management. NB we pretend all classes below
implement the mutex interface even though only [shared_lock] and [unique_lock] do, for implement the mutex interface even though only [shared_lock] and [unique_lock] do, for
simplicity. The comments summarise which methods are implemented. *) simplicity. The comments summarise which methods are implemented. *)
@ -399,8 +392,6 @@ let runs_on_ui_thread ~attrs_of_pname tenv pname =
is_modeled_ui_method tenv pname || annotated_as_uithread_equivalent ~attrs_of_pname tenv pname is_modeled_ui_method tenv pname || annotated_as_uithread_equivalent ~attrs_of_pname tenv pname
let cpp_lock_types_matcher = Clang.lock_types_matcher
let is_recursive_lock_type = function let is_recursive_lock_type = function
| Typ.CppClass (qname, _) -> | Typ.CppClass (qname, _) ->
Clang.is_recursive_lock_type qname Clang.is_recursive_lock_type qname

@ -36,8 +36,6 @@ val get_thread_assert_effect : Procname.t -> thread
val get_current_class_and_annotated_superclasses : val get_current_class_and_annotated_superclasses :
(Annot.Item.t -> bool) -> Tenv.t -> Procname.t -> (Typ.name * Typ.name list) option (Annot.Item.t -> bool) -> Tenv.t -> Procname.t -> (Typ.name * Typ.name list) option
val cpp_lock_types_matcher : QualifiedCppName.Match.quals_matcher
val is_recursive_lock_type : Typ.name -> bool val is_recursive_lock_type : Typ.name -> bool
(** Type documenting why a method is considered as annotated with a certain annotation *) (** Type documenting why a method is considered as annotated with a certain annotation *)

@ -850,21 +850,12 @@ let should_report_on_proc tenv procdesc =
|| Procdesc.get_access procdesc <> PredSymb.Private || Procdesc.get_access procdesc <> PredSymb.Private
&& (not (Procname.Java.is_autogen_method java_pname)) && (not (Procname.Java.is_autogen_method java_pname))
&& not (Annotations.pdesc_return_annot_ends_with procdesc Annotations.visibleForTesting) && not (Annotations.pdesc_return_annot_ends_with procdesc Annotations.visibleForTesting)
| ObjC_Cpp {kind; class_name} -> | ObjC_Cpp {kind= CPPMethod _ | CPPConstructor _ | CPPDestructor _} ->
( match kind with Procdesc.get_access procdesc <> PredSymb.Private
| CPPMethod _ | CPPConstructor _ | CPPDestructor _ -> | ObjC_Cpp {kind= ObjCClassMethod | ObjCInstanceMethod | ObjCInternalMethod; class_name} ->
Procdesc.get_access procdesc <> PredSymb.Private Tenv.lookup tenv class_name
| ObjCClassMethod | ObjCInstanceMethod | ObjCInternalMethod -> |> Option.exists ~f:(fun {Struct.exported_objc_methods} ->
Tenv.lookup tenv class_name List.mem ~equal:Procname.equal exported_objc_methods proc_name )
|> Option.exists ~f:(fun {Struct.exported_objc_methods} ->
List.mem ~equal:Procname.equal exported_objc_methods proc_name ) )
&&
let matcher = ConcurrencyModels.cpp_lock_types_matcher in
Option.exists (Tenv.lookup tenv class_name) ~f:(fun class_str ->
(* check if the class contains a lock member *)
List.exists class_str.Struct.fields ~f:(fun (_, ft, _) ->
Option.exists (Typ.name ft) ~f:(fun name ->
QualifiedCppName.Match.match_qualifiers matcher (Typ.Name.qual_name name) ) ) )
| _ -> | _ ->
false false
@ -1106,6 +1097,28 @@ let make_results_table exe_env summaries =
Payloads.racerd summary.payloads |> Option.fold ~init:acc ~f:(aggregate_post tenv procname) ) Payloads.racerd summary.payloads |> Option.fold ~init:acc ~f:(aggregate_post tenv procname) )
let class_has_concurrent_method class_summaries =
let open RacerDDomain in
let method_has_concurrent_context (summary : Summary.t) =
Payloads.racerd summary.payloads
|> Option.exists ~f:(fun (payload : summary) ->
match (payload.threads : ThreadsDomain.t) with NoThread -> false | _ -> true )
in
List.exists class_summaries ~f:method_has_concurrent_context
let should_report_on_class (classname : Typ.Name.t) class_summaries =
match classname with
| JavaClass _ ->
true
| CppClass _ | ObjcClass _ | ObjcProtocol _ | CStruct _ ->
class_has_concurrent_method class_summaries
| CUnion _ ->
false
let filter_reportable_classes class_map = Typ.Name.Map.filter should_report_on_class class_map
(* aggregate all of the procedures in the file env by their declaring (* aggregate all of the procedures in the file env by their declaring
class. this lets us analyze each class individually *) class. this lets us analyze each class individually *)
let aggregate_by_class exe_env procedures = let aggregate_by_class exe_env procedures =
@ -1123,6 +1136,7 @@ let aggregate_by_class exe_env procedures =
| None -> Some [summary] | Some summaries -> Some (summary :: summaries) ) | None -> Some [summary] | Some summaries -> Some (summary :: summaries) )
acc ) ) acc ) )
|> Option.value ~default:acc ) |> Option.value ~default:acc )
|> filter_reportable_classes
(* Gathers results by analyzing all the methods in a file, then (* Gathers results by analyzing all the methods in a file, then

@ -24,3 +24,4 @@ codetoanalyze/cpp/racerd/unique_lock.cpp, basics::UniqueLock::get4, 55, LOCK_CON
codetoanalyze/cpp/racerd/unique_lock.cpp, basics::UniqueLock::get4, 56, LOCK_CONSISTENCY_VIOLATION, no_bucket, WARNING, [<Read trace>,access to `this->suspiciously_read2`,<Write trace>,access to `this->suspiciously_read2`] codetoanalyze/cpp/racerd/unique_lock.cpp, basics::UniqueLock::get4, 56, LOCK_CONSISTENCY_VIOLATION, no_bucket, WARNING, [<Read trace>,access to `this->suspiciously_read2`,<Write trace>,access to `this->suspiciously_read2`]
codetoanalyze/cpp/racerd/unique_lock.cpp, basics::UniqueLock::get5, 64, LOCK_CONSISTENCY_VIOLATION, no_bucket, WARNING, [<Read trace>,access to `this->suspiciously_read1`,<Write trace>,access to `this->suspiciously_read1`] codetoanalyze/cpp/racerd/unique_lock.cpp, basics::UniqueLock::get5, 64, LOCK_CONSISTENCY_VIOLATION, no_bucket, WARNING, [<Read trace>,access to `this->suspiciously_read1`,<Write trace>,access to `this->suspiciously_read1`]
codetoanalyze/cpp/racerd/unique_lock.cpp, basics::UniqueLock::get6, 73, LOCK_CONSISTENCY_VIOLATION, no_bucket, WARNING, [<Read trace>,access to `this->suspiciously_read1`,<Write trace>,access to `this->suspiciously_read1`] codetoanalyze/cpp/racerd/unique_lock.cpp, basics::UniqueLock::get6, 73, LOCK_CONSISTENCY_VIOLATION, no_bucket, WARNING, [<Read trace>,access to `this->suspiciously_read1`,<Write trace>,access to `this->suspiciously_read1`]
codetoanalyze/cpp/racerd/without_mutex.cpp, without_mutex::WithoutMutex::get, 15, LOCK_CONSISTENCY_VIOLATION, no_bucket, WARNING, [<Read trace>,access to `this->field_1`,<Write trace>,access to `this->field_1`]

@ -0,0 +1,25 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <mutex>
namespace without_mutex {
class WithoutMutex {
public:
WithoutMutex() {}
int get() { return field_1; }
int set(std::mutex& mutex, int data) {
std::lock_guard<std::mutex> lock(mutex);
field_1 = data;
}
private:
int field_1;
};
} // namespace without_mutex
Loading…
Cancel
Save