From 2701073b3e78c616a9495dd2a6f956a1e9ec6b5b Mon Sep 17 00:00:00 2001 From: Nikos Gorogiannis Date: Mon, 10 Dec 2018 06:32:06 -0800 Subject: [PATCH] [starvation] C++ deadlock FPs due to recursive and unknown mutexes Reviewed By: jvillard Differential Revision: D13376553 fbshipit-source-id: 79ad1a63a --- infer/src/concurrency/ConcurrencyModels.ml | 101 ++++++++++++------ infer/src/concurrency/ConcurrencyModels.mli | 14 ++- infer/src/concurrency/RacerD.ml | 3 +- infer/src/concurrency/RacerDModels.ml | 4 +- infer/src/concurrency/starvation.ml | 15 ++- infer/src/concurrency/starvationDomain.ml | 74 ++++++++----- infer/src/concurrency/starvationDomain.mli | 9 +- .../codetoanalyze/cpp/starvation/basics.cpp | 14 +++ .../codetoanalyze/cpp/starvation/issues.exp | 4 +- .../cpp/starvation/recursive.cpp | 14 +-- 10 files changed, 156 insertions(+), 96 deletions(-) diff --git a/infer/src/concurrency/ConcurrencyModels.ml b/infer/src/concurrency/ConcurrencyModels.ml index bfb8dc2a2..16ff8991a 100644 --- a/infer/src/concurrency/ConcurrencyModels.ml +++ b/infer/src/concurrency/ConcurrencyModels.ml @@ -55,36 +55,61 @@ module Clang : sig val get_lock_effect : Typ.Procname.t -> HilExp.t list -> lock_effect val lock_types_matcher : QualifiedCppName.Match.quals_matcher + + val is_recursive_lock_type : QualifiedCppName.t -> bool end = struct - type lock_model = {cls: string; lck: string list; tlk: string list; unl: string list} + type lock_model = + { classname: string + ; lock: string list + ; trylock: string list + ; unlock: string list + ; recursive: bool } let lock_models = - let def = {cls= ""; lck= ["lock"]; tlk= ["try_lock"]; unl= ["unlock"]} in + let def = + {classname= ""; lock= ["lock"]; trylock= ["try_lock"]; unlock= ["unlock"]; recursive= false} + in let shd = - { cls= "std::shared_mutex" - ; lck= "lock_shared" :: def.lck - ; tlk= "try_lock_shared" :: def.tlk - ; unl= "unlock_shared" :: def.unl } + { def with + lock= "lock_shared" :: def.lock + ; trylock= "try_lock_shared" :: def.trylock + ; unlock= "unlock_shared" :: def.unlock } in let rwm = - { cls= "apache::thrift::concurrency::ReadWriteMutex" - ; lck= ["acquireRead"; "acquireWrite"] - ; tlk= ["attemptRead"; "attemptWrite"] - ; unl= ["release"] } + { def with + lock= ["acquireRead"; "acquireWrite"] + ; trylock= ["attemptRead"; "attemptWrite"] + ; unlock= ["release"] } in - [ {def with cls= "apache::thrift::concurrency::Monitor"; tlk= "timedlock" :: def.tlk} - ; {def with cls= "apache::thrift::concurrency::Mutex"; tlk= "timedlock" :: def.tlk} - ; {rwm with cls= "apache::thrift::concurrency::NoStarveReadWriteMutex"} - ; rwm - ; {shd with cls= "boost::shared_mutex"} - ; {def with cls= "boost::mutex"} - ; {def with cls= "folly::MicroSpinLock"} - ; {shd with cls= "folly::RWSpinLock"} - ; {shd with cls= "folly::SharedMutex"} - ; {shd with cls= "folly::SharedMutexImpl"} - ; {def with cls= "folly::SpinLock"} - ; {def with cls= "std::mutex"} - ; shd ] + [ { def with + classname= "apache::thrift::concurrency::Monitor"; trylock= "timedlock" :: def.trylock } + ; { def with + classname= "apache::thrift::concurrency::Mutex"; trylock= "timedlock" :: def.trylock } + ; {rwm with classname= "apache::thrift::concurrency::NoStarveReadWriteMutex"} + ; {rwm with classname= "apache::thrift::concurrency::ReadWriteMutex"} + ; {shd with classname= "boost::shared_mutex"} + ; {def with classname= "boost::mutex"} + ; {def with classname= "folly::MicroSpinLock"} + ; {shd with classname= "folly::RWSpinLock"} + ; {shd with classname= "folly::SharedMutex"} + ; {shd with classname= "folly::SharedMutexImpl"} + ; {def with classname= "folly::SpinLock"} + ; {def with classname= "std::mutex"} + ; {def with classname= "std::recursive_mutex"; recursive= true} + ; {def with classname= "std::recursive_timed_mutex"; recursive= true} + ; {shd with classname= "std::shared_mutex"} + ; {def with classname= "std::timed_mutex"} ] + + + let is_recursive_lock_type qname = + let qname_str = QualifiedCppName.to_qual_string qname in + match List.find lock_models ~f:(fun mdl -> String.equal qname_str mdl.classname) with + | None -> + L.debug Analysis Medium "is_recursive_lock_type: Could not find lock type %a@." + QualifiedCppName.pp qname ; + true + | Some mdl -> + mdl.recursive let mk_matcher methods = @@ -98,18 +123,18 @@ end = struct let mk_model_matcher ~f = let lock_methods = List.concat_map lock_models ~f:(fun mdl -> - List.map (f mdl) ~f:(fun mtd -> mdl.cls ^ "::" ^ mtd) ) + List.map (f mdl) ~f:(fun mtd -> mdl.classname ^ "::" ^ mtd) ) in mk_matcher lock_methods in - ( mk_model_matcher ~f:(fun mdl -> mdl.lck) - , mk_model_matcher ~f:(fun mdl -> mdl.unl) - , mk_model_matcher ~f:(fun mdl -> mdl.tlk) + ( mk_model_matcher ~f:(fun mdl -> mdl.lock) + , mk_model_matcher ~f:(fun mdl -> mdl.unlock) + , mk_model_matcher ~f:(fun mdl -> mdl.trylock) , mk_matcher ["std::lock"] ) let lock_types_matcher = - let class_names = List.map lock_models ~f:(fun mdl -> mdl.cls) in + let class_names = List.map lock_models ~f:(fun mdl -> mdl.classname) in QualifiedCppName.Match.of_fuzzy_qual_names class_names @@ -285,11 +310,9 @@ let get_current_class_and_annotated_superclasses is_annot tenv pname = None -let find_annotated_or_overriden_annotated_method is_annot pname tenv = +let find_annotated_or_overriden_annotated_method ~attrs_of_pname is_annot pname tenv = PatternMatch.override_find - (fun pn -> - Annotations.pname_has_return_annot pn ~attrs_of_pname:Summary.proc_resolve_attributes - is_annot ) + (fun pn -> Annotations.pname_has_return_annot pn ~attrs_of_pname is_annot) tenv pname @@ -351,11 +374,12 @@ let runs_on_ui_thread = in let is_annot annot = List.exists annotation_matchers ~f:(fun m -> m annot) in let mono_pname = MF.wrap_monospaced Typ.Procname.pp in - fun tenv proc_desc -> + fun ~attrs_of_pname tenv proc_desc -> let pname = Procdesc.get_proc_name proc_desc in if Annotations.pdesc_has_return_annot proc_desc Annotations.ia_is_worker_thread - || find_annotated_or_overriden_annotated_method Annotations.ia_is_worker_thread pname tenv + || find_annotated_or_overriden_annotated_method ~attrs_of_pname + Annotations.ia_is_worker_thread pname tenv |> Option.is_some || get_current_class_and_annotated_superclasses Annotations.ia_is_worker_thread tenv pname |> Option.value_map ~default:false ~f:(function _, [] -> false | _ -> true) @@ -367,7 +391,7 @@ let runs_on_ui_thread = (F.asprintf "%a is annotated %s" mono_pname pname (MF.monospaced_to_string Annotations.ui_thread)) else - match find_annotated_or_overriden_annotated_method is_annot pname tenv with + match find_annotated_or_overriden_annotated_method ~attrs_of_pname is_annot pname tenv with | Some override_pname -> Some (F.asprintf "class %a overrides %a, which is annotated %s" mono_pname pname mono_pname @@ -390,3 +414,10 @@ let runs_on_ui_thread = let cpp_lock_types_matcher = Clang.lock_types_matcher + +let is_recursive_lock_type = function + | Typ.CppClass (qname, _) -> + Clang.is_recursive_lock_type qname + | _ -> + (* non-C++ lock types are always considered recursive *) + true diff --git a/infer/src/concurrency/ConcurrencyModels.mli b/infer/src/concurrency/ConcurrencyModels.mli index 847fb3cf1..14918bb40 100644 --- a/infer/src/concurrency/ConcurrencyModels.mli +++ b/infer/src/concurrency/ConcurrencyModels.mli @@ -33,7 +33,11 @@ val get_lock_effect : Typ.Procname.t -> HilExp.t list -> lock_effect val get_thread : Typ.Procname.t -> thread (** describe how this procedure behaves with respect to thread access *) -val runs_on_ui_thread : Tenv.t -> Procdesc.t -> string option +val runs_on_ui_thread : + attrs_of_pname:(Typ.Procname.t -> ProcAttributes.t option) + -> Tenv.t + -> Procdesc.t + -> string option (** We don't want to warn on methods that run on the UI thread because they should always be single-threaded. Assume that methods annotated with @UiThread, @OnEvent, @OnBind, @OnMount, @OnUnbind, @OnUnmount always run on the UI thread. Also assume that any superclass @@ -44,6 +48,12 @@ val get_current_class_and_annotated_superclasses : (Annot.Item.t -> bool) -> Tenv.t -> Typ.Procname.t -> (Typ.name * Typ.name list) option val find_annotated_or_overriden_annotated_method : - (Annot.Item.t -> bool) -> BuiltinDecl.t -> Tenv.t -> BuiltinDecl.t sexp_option + attrs_of_pname:(Typ.Procname.t -> ProcAttributes.t option) + -> (Annot.Item.t -> bool) + -> Typ.Procname.t + -> Tenv.t + -> Typ.Procname.t sexp_option val cpp_lock_types_matcher : QualifiedCppName.Match.quals_matcher + +val is_recursive_lock_type : Typ.name -> bool diff --git a/infer/src/concurrency/RacerD.ml b/infer/src/concurrency/RacerD.ml index a92f08cd0..c3a778a99 100644 --- a/infer/src/concurrency/RacerD.ml +++ b/infer/src/concurrency/RacerD.ml @@ -617,7 +617,8 @@ let analyze_procedure {Callbacks.proc_desc; tenv; summary} = let initial = let threads = if - runs_on_ui_thread tenv proc_desc |> Option.is_some + runs_on_ui_thread ~attrs_of_pname:Summary.proc_resolve_attributes tenv proc_desc + |> Option.is_some || is_thread_confined_method tenv proc_desc then ThreadsDomain.AnyThreadButSelf else if Procdesc.is_java_synchronized proc_desc || is_marked_thread_safe proc_desc tenv diff --git a/infer/src/concurrency/RacerDModels.ml b/infer/src/concurrency/RacerDModels.ml index 870b22608..7dd662ca6 100644 --- a/infer/src/concurrency/RacerDModels.ml +++ b/infer/src/concurrency/RacerDModels.ml @@ -374,7 +374,9 @@ let is_thread_safe_class pname tenv = let is_thread_safe_method pname tenv = - find_annotated_or_overriden_annotated_method is_thread_safe pname tenv |> Option.is_some + find_annotated_or_overriden_annotated_method ~attrs_of_pname:Summary.proc_resolve_attributes + is_thread_safe pname tenv + |> Option.is_some let is_marked_thread_safe pdesc tenv = diff --git a/infer/src/concurrency/starvation.ml b/infer/src/concurrency/starvation.ml index 1f0a6191e..41f9b14d9 100644 --- a/infer/src/concurrency/starvation.ml +++ b/infer/src/concurrency/starvation.ml @@ -80,13 +80,12 @@ module TransferFunctions (CFG : ProcCfg.S) = struct in let is_java = Procdesc.get_proc_name pdesc |> Typ.Procname.is_java in let do_lock locks loc astate = - List.filter_map ~f:get_lock_path locks |> Domain.acquire ~recursive_locks:is_java astate loc + List.filter_map ~f:get_lock_path locks |> Domain.acquire tenv astate loc in let do_unlock locks astate = List.filter_map ~f:get_lock_path locks |> Domain.release astate in let do_call callee loc astate = Payload.read pdesc callee - |> Option.value_map ~default:astate - ~f:(Domain.integrate_summary ~recursive_locks:is_java astate callee loc) + |> Option.value_map ~default:astate ~f:(Domain.integrate_summary tenv astate callee loc) in match instr with | Assign _ | Assume _ | Call (_, Indirect _, _, _, _) | ExitScope _ -> @@ -98,11 +97,11 @@ module TransferFunctions (CFG : ProcCfg.S) = struct | Lock locks -> do_lock locks loc astate | GuardLock guard -> - Domain.lock_guard ~recursive_locks:is_java astate guard loc + Domain.lock_guard tenv astate guard loc | GuardConstruct {guard; lock; acquire_now} -> ( match get_lock_path lock with | Some lock_path -> - Domain.add_guard astate guard lock_path ~acquire_now ~recursive_locks:false loc + Domain.add_guard tenv astate guard lock_path ~acquire_now loc | None -> log_parse_error "Couldn't parse lock in guard constructor" callee actuals ; astate ) @@ -146,7 +145,6 @@ let analyze_procedure {Callbacks.proc_desc; tenv; summary} = let formals = FormalMap.make proc_desc in let proc_data = ProcData.make proc_desc tenv formals in let loc = Procdesc.get_loc proc_desc in - let recursive_locks = Procdesc.get_proc_name proc_desc |> Typ.Procname.is_java in let initial = if not (Procdesc.is_java_synchronized proc_desc) then StarvationDomain.empty else @@ -159,10 +157,11 @@ let analyze_procedure {Callbacks.proc_desc; tenv; summary} = | _ -> FormalMap.get_formal_base 0 formals |> Option.map ~f:(fun base -> (base, [])) in - StarvationDomain.acquire ~recursive_locks StarvationDomain.empty loc (Option.to_list lock) + StarvationDomain.acquire tenv StarvationDomain.empty loc (Option.to_list lock) in let initial = - ConcurrencyModels.runs_on_ui_thread tenv proc_desc + ConcurrencyModels.runs_on_ui_thread ~attrs_of_pname:Summary.proc_resolve_attributes tenv + proc_desc |> Option.value_map ~default:initial ~f:(StarvationDomain.set_on_ui_thread initial loc) in let filter_blocks = diff --git a/infer/src/concurrency/starvationDomain.ml b/infer/src/concurrency/starvationDomain.ml index 279a2b7de..5b63a9fea 100644 --- a/infer/src/concurrency/starvationDomain.ml +++ b/infer/src/concurrency/starvationDomain.ml @@ -6,6 +6,7 @@ *) open! IStd module F = Format +module L = Logging module MF = MarkupFormatter let pname_pp = MF.wrap_monospaced Typ.Procname.pp @@ -147,7 +148,7 @@ module LockState = struct false - let acquire lock_id lock_event map = + let acquire map lock_id lock_event = let current_value = try find lock_id map with Caml.Not_found -> LockStack.top in let new_value = LockStack.push lock_event current_value in add lock_id new_value map @@ -253,10 +254,34 @@ let ( <= ) ~lhs ~rhs = && UIThreadDomain.( <= ) ~lhs:lhs.ui ~rhs:rhs.ui +let is_recursive_lock event tenv = + let is_class_and_recursive_lock = function + | {Typ.desc= Tptr ({desc= Tstruct name}, _)} | {desc= Tstruct name} -> + ConcurrencyModels.is_recursive_lock_type name + | typ -> + L.die L.InternalError "Asked if non-struct type %a is a recursive lock type.@." + (Typ.pp_full Pp.text) typ + in + match event with + | {Event.elem= LockAcquire lock_path} -> + AccessPath.get_typ lock_path tenv |> Option.exists ~f:is_class_and_recursive_lock + | _ -> + false + + +(** skip adding an order pair [(_, event)] if + - we have no tenv, or, + - [event] is not a lock event, or, + - we do not hold the lock, or, + - the lock is not recursive. *) +let should_skip tenv_opt event lock_state = + Option.exists tenv_opt ~f:(fun tenv -> + LockState.is_taken event lock_state && is_recursive_lock event tenv ) + + (* for every lock b held locally, add a pair (b, event) *) -let add_order_pairs ~recursive_locks lock_state event acc = - (* add no pairs whatsoever if we already hold that lock *) - if recursive_locks && LockState.is_taken event lock_state then acc +let add_order_pairs tenv_opt lock_state event acc = + if should_skip tenv_opt event lock_state then acc else let add_first_and_eventually acc f = match f.Event.elem with @@ -269,32 +294,30 @@ let add_order_pairs ~recursive_locks lock_state event acc = LockState.fold_over_events add_first_and_eventually lock_state acc -let acquire ~recursive_locks ({lock_state; events; order} as astate) loc locks = +let acquire tenv ({lock_state; events; order} as astate) loc locks = let new_events = List.map locks ~f:(fun lock -> Event.make_acquire lock loc) in { astate with events= List.fold new_events ~init:events ~f:(fun acc e -> EventDomain.add e acc) ; order= List.fold new_events ~init:order ~f:(fun acc e -> - OrderDomain.union acc (add_order_pairs ~recursive_locks lock_state e order) ) - ; lock_state= - List.fold2_exn locks new_events ~init:lock_state ~f:(fun acc lock e -> - LockState.acquire lock e acc ) } + OrderDomain.union acc (add_order_pairs (Some tenv) lock_state e order) ) + ; lock_state= List.fold2_exn locks new_events ~init:lock_state ~f:LockState.acquire } -let make_call_with_event new_event astate = +let make_call_with_event tenv_opt new_event astate = { astate with events= EventDomain.add new_event astate.events - ; order= add_order_pairs ~recursive_locks:false astate.lock_state new_event astate.order } + ; order= add_order_pairs tenv_opt astate.lock_state new_event astate.order } let blocking_call callee sev loc astate = let new_event = Event.make_blocking_call callee sev loc in - make_call_with_event new_event astate + make_call_with_event None new_event astate let strict_mode_call callee loc astate = let new_event = Event.make_strict_mode_call callee loc in - make_call_with_event new_event astate + make_call_with_event None new_event astate let release ({lock_state} as astate) locks = @@ -302,26 +325,19 @@ let release ({lock_state} as astate) locks = lock_state= List.fold locks ~init:lock_state ~f:(fun acc l -> LockState.release l acc) } -let integrate_summary ~recursive_locks ({lock_state; events; order; ui} as astate) callee_pname loc +let integrate_summary tenv ({lock_state; events; order; ui} as astate) callee_pname loc callee_summary = let callsite = CallSite.make callee_pname loc in let callee_order = OrderDomain.with_callsite callee_summary.order callsite in let callee_ui = UIThreadDomain.with_callsite callee_summary.ui callsite in + let should_keep event = should_skip (Some tenv) event lock_state |> not in let filtered_order = - if recursive_locks then - OrderDomain.filter - (fun {elem= {eventually}} -> LockState.is_taken eventually lock_state |> not) - callee_order - else callee_order + OrderDomain.filter (fun {elem= {eventually}} -> should_keep eventually) callee_order in let callee_events = EventDomain.with_callsite callee_summary.events callsite in - let filtered_events = - if recursive_locks then - EventDomain.filter (fun e -> LockState.is_taken e lock_state |> not) callee_events - else callee_events - in + let filtered_events = EventDomain.filter should_keep callee_events in let order' = - EventDomain.fold (add_order_pairs ~recursive_locks lock_state) filtered_events filtered_order + EventDomain.fold (add_order_pairs (Some tenv) lock_state) filtered_events filtered_order in { astate with events= EventDomain.join events filtered_events @@ -337,9 +353,9 @@ let set_on_ui_thread ({ui} as astate) loc explain = {astate with ui} -let add_guard astate guard lock ~acquire_now ~recursive_locks loc = +let add_guard tenv astate guard lock ~acquire_now loc = let astate = {astate with guard_map= GuardToLockMap.add_guard ~guard ~lock astate.guard_map} in - if acquire_now then acquire ~recursive_locks astate loc [lock] else astate + if acquire_now then acquire tenv astate loc [lock] else astate let remove_guard astate guard = @@ -356,10 +372,10 @@ let unlock_guard astate guard = FlatLock.get lock_opt |> Option.to_list |> release astate ) -let lock_guard ~recursive_locks astate guard loc = +let lock_guard tenv astate guard loc = GuardToLockMap.find_opt guard astate.guard_map |> Option.value_map ~default:astate ~f:(fun lock_opt -> - FlatLock.get lock_opt |> Option.to_list |> acquire astate ~recursive_locks loc ) + FlatLock.get lock_opt |> Option.to_list |> acquire tenv astate loc ) type summary = t diff --git a/infer/src/concurrency/starvationDomain.mli b/infer/src/concurrency/starvationDomain.mli index 976c63b2d..77e28cff2 100644 --- a/infer/src/concurrency/starvationDomain.mli +++ b/infer/src/concurrency/starvationDomain.mli @@ -80,7 +80,7 @@ type t = include AbstractDomain.WithBottom with type t := t -val acquire : recursive_locks:bool -> t -> Location.t -> Lock.t list -> t +val acquire : Tenv.t -> t -> Location.t -> Lock.t list -> t (** simultaneously acquire a number of locks, no-op if list is empty *) val release : t -> Lock.t list -> t @@ -94,11 +94,10 @@ val set_on_ui_thread : t -> Location.t -> string -> t (** set the property "runs on UI thread" to true by attaching the given explanation string as to why this method is thought to do so *) -val add_guard : - t -> HilExp.t -> Lock.t -> acquire_now:bool -> recursive_locks:bool -> Location.t -> t +val add_guard : Tenv.t -> t -> HilExp.t -> Lock.t -> acquire_now:bool -> Location.t -> t (** Install a mapping from the guard expression to the lock provided, and optionally lock it. *) -val lock_guard : recursive_locks:bool -> t -> HilExp.t -> Location.t -> t +val lock_guard : Tenv.t -> t -> HilExp.t -> Location.t -> t (** Acquire the lock the guard was constructed with. *) val remove_guard : t -> HilExp.t -> t @@ -111,4 +110,4 @@ type summary = t val pp_summary : F.formatter -> summary -> unit -val integrate_summary : recursive_locks:bool -> t -> Typ.Procname.t -> Location.t -> summary -> t +val integrate_summary : Tenv.t -> t -> Typ.Procname.t -> Location.t -> summary -> t diff --git a/infer/tests/codetoanalyze/cpp/starvation/basics.cpp b/infer/tests/codetoanalyze/cpp/starvation/basics.cpp index 21fb82db3..7f1fdafb9 100644 --- a/infer/tests/codetoanalyze/cpp/starvation/basics.cpp +++ b/infer/tests/codetoanalyze/cpp/starvation/basics.cpp @@ -136,4 +136,18 @@ class SelfDeadlock { std::mutex mutex_; }; +class PathSensitive { + public: + void FP_ok() { + std::lock_guard l(mutex_); + bool flag = false; + + if (flag) { + std::lock_guard l(mutex_); + } + } + + private: + std::mutex mutex_; +}; } // namespace basics diff --git a/infer/tests/codetoanalyze/cpp/starvation/issues.exp b/infer/tests/codetoanalyze/cpp/starvation/issues.exp index 63cc252b3..0a8b8ade3 100644 --- a/infer/tests/codetoanalyze/cpp/starvation/issues.exp +++ b/infer/tests/codetoanalyze/cpp/starvation/issues.exp @@ -1,9 +1,7 @@ codetoanalyze/cpp/starvation/basics.cpp, basics::Basic_thread1_bad, 18, DEADLOCK, no_bucket, ERROR, [[Trace 1] `basics::Basic_thread1_bad`,locks `this.mutex_1` in class `basics::Basic*`,locks `this.mutex_2` in class `basics::Basic*`,[Trace 2] `basics::Basic_thread2_bad`,locks `this.mutex_2` in class `basics::Basic*`,locks `this.mutex_1` in class `basics::Basic*`] +codetoanalyze/cpp/starvation/basics.cpp, basics::PathSensitive_FP_ok, 142, DEADLOCK, no_bucket, ERROR, [In method`basics::PathSensitive_FP_ok`,locks `this.mutex_` in class `basics::PathSensitive*`,locks `this.mutex_` in class `basics::PathSensitive*`] codetoanalyze/cpp/starvation/basics.cpp, basics::SelfDeadlock_complicated_bad, 131, DEADLOCK, no_bucket, ERROR, [In method`basics::SelfDeadlock_complicated_bad`,locks `this.mutex_` in class `basics::SelfDeadlock*`,locks `this.mutex_` in class `basics::SelfDeadlock*`] codetoanalyze/cpp/starvation/basics.cpp, basics::SelfDeadlock_interproc1_bad, 114, DEADLOCK, no_bucket, ERROR, [In method`basics::SelfDeadlock_interproc1_bad`,locks `this.mutex_` in class `basics::SelfDeadlock*`,Method call: `basics::SelfDeadlock_interproc2_bad`,locks `this.mutex_` in class `basics::SelfDeadlock*`] codetoanalyze/cpp/starvation/basics.cpp, basics::SelfDeadlock_thread_bad, 105, DEADLOCK, no_bucket, ERROR, [In method`basics::SelfDeadlock_thread_bad`,locks `this.mutex_` in class `basics::SelfDeadlock*`,locks `this.mutex_` in class `basics::SelfDeadlock*`] codetoanalyze/cpp/starvation/basics.cpp, basics::WithGuard_thread1_bad, 44, DEADLOCK, no_bucket, ERROR, [[Trace 1] `basics::WithGuard_thread1_bad`,locks `this.mutex_1` in class `basics::WithGuard*`,locks `this.mutex_2` in class `basics::WithGuard*`,[Trace 2] `basics::WithGuard_thread2_bad`,locks `this.mutex_2` in class `basics::WithGuard*`,locks `this.mutex_1` in class `basics::WithGuard*`] -codetoanalyze/cpp/starvation/recursive.cpp, Recursive_FP_multi_ok, 26, DEADLOCK, no_bucket, ERROR, [In method`Recursive_FP_multi_ok`,locks `this.recursive_mutex_` in class `Recursive*`,locks `this.recursive_mutex_` in class `Recursive*`] -codetoanalyze/cpp/starvation/recursive.cpp, Recursive_FP_path_sensitive_ok, 36, DEADLOCK, no_bucket, ERROR, [In method`Recursive_FP_path_sensitive_ok`,locks `this.mutex_` in class `Recursive*`,locks `this.mutex_` in class `Recursive*`] -codetoanalyze/cpp/starvation/recursive.cpp, Recursive_FP_unknown_ok, 31, DEADLOCK, no_bucket, ERROR, [In method`Recursive_FP_unknown_ok`,locks `this.umutex_` in class `Recursive*`,locks `this.umutex_` in class `Recursive*`] codetoanalyze/cpp/starvation/skip.cpp, skipped::Skip_not_skipped_bad, 19, DEADLOCK, no_bucket, ERROR, [In method`skipped::Skip_not_skipped_bad`,Method call: `skipped::Skip_private_deadlock`,locks `this.mutex_` in class `skipped::Skip*`,locks `this.mutex_` in class `skipped::Skip*`] diff --git a/infer/tests/codetoanalyze/cpp/starvation/recursive.cpp b/infer/tests/codetoanalyze/cpp/starvation/recursive.cpp index 9321c9471..66abba226 100644 --- a/infer/tests/codetoanalyze/cpp/starvation/recursive.cpp +++ b/infer/tests/codetoanalyze/cpp/starvation/recursive.cpp @@ -22,27 +22,17 @@ class Recursive { public: Recursive() {} - void FP_multi_ok() { + void multi_ok() { std::lock_guard l(recursive_mutex_); { std::lock_guard l(recursive_mutex_); } } - void FP_unknown_ok() { + void unknown_ok() { std::lock_guard l(umutex_); { std::lock_guard l(umutex_); } } - void FP_path_sensitive_ok() { - std::lock_guard l(mutex_); - bool flag = false; - - if (flag) { - std::lock_guard l(mutex_); - } - } - private: std::recursive_mutex recursive_mutex_; - std::mutex mutex_; UnknownMutex umutex_; };