@ -1179,7 +1179,8 @@ let report_thread_safety_violation tenv pdesc ~make_description ~report_kind acc
let exn =
let exn =
Exceptions . Checkers ( issue_type . IssueType . unique_id , Localise . verbatim_desc error_message )
Exceptions . Checkers ( issue_type . IssueType . unique_id , Localise . verbatim_desc error_message )
in
in
Reporting . log_error_deprecated ~ store_summary : true pname ~ loc ~ ltr exn
let access = B64 . encode ( Marshal . to_string ( pname , access ) [] ) in
Reporting . log_error_deprecated ~ store_summary : true pname ~ loc ~ ltr ~ access exn
in
in
let trace_of_pname = trace_of_pname access pdesc in
let trace_of_pname = trace_of_pname access pdesc in
Option . iter ~ f : report_one_path ( PathDomain . get_reportable_sink_path access ~ trace_of_pname )
Option . iter ~ f : report_one_path ( PathDomain . get_reportable_sink_path access ~ trace_of_pname )
@ -1244,22 +1245,6 @@ let make_read_write_race_description ~read_is_sync conflicts pname final_sink_si
pp_access final_sink conflicts_description
pp_access final_sink conflicts_description
(* * type for remembering what we have already reported to avoid duplicates. our policy is to report
each kind of access ( read / write ) to the same field reachable from the same procedure only once .
in addition , if a call to a procedure ( transitively ) accesses multiple fields , we will only
report one of each kind of access * )
type reported =
{ reported_sites : CallSite . Set . t
; reported_writes : Typ . Procname . Set . t
; reported_reads : Typ . Procname . Set . t }
let empty_reported =
let reported_sites = CallSite . Set . empty in
let reported_writes = Typ . Procname . Set . empty in
let reported_reads = Typ . Procname . Set . empty in
{ reported_sites ; reported_reads ; reported_writes }
(* return true if procedure is at an abstraction boundary or reporting has been explicitly
(* return true if procedure is at an abstraction boundary or reporting has been explicitly
requested via @ ThreadSafe * )
requested via @ ThreadSafe * )
let should_report_on_proc proc_desc tenv =
let should_report_on_proc proc_desc tenv =
@ -1308,47 +1293,17 @@ let report_unsafe_accesses
list
list
AccessListMap . t ) =
AccessListMap . t ) =
let open RacerDDomain in
let open RacerDDomain in
let is_duplicate_report access pname { reported_sites ; reported_writes ; reported_reads } =
let report_unsafe_access ( access , pre , thread , tenv , pdesc ) accesses =
if Config . filtering then CallSite . Set . mem ( TraceElem . call_site access ) reported_sites
| |
match TraceElem . kind access with
| Access . Write _ | Access . ContainerWrite _ ->
Typ . Procname . Set . mem pname reported_writes
| Access . Read _ | Access . ContainerRead _ ->
Typ . Procname . Set . mem pname reported_reads
| Access . InterfaceCall _ ->
false
else false
in
let update_reported access pname reported =
if Config . filtering then
let reported_sites = CallSite . Set . add ( TraceElem . call_site access ) reported . reported_sites in
match TraceElem . kind access with
| Access . Write _ | Access . ContainerWrite _ ->
let reported_writes = Typ . Procname . Set . add pname reported . reported_writes in
{ reported with reported_writes ; reported_sites }
| Access . Read _ | Access . ContainerRead _ ->
let reported_reads = Typ . Procname . Set . add pname reported . reported_reads in
{ reported with reported_reads ; reported_sites }
| Access . InterfaceCall _ ->
reported
else reported
in
let report_unsafe_access ( access , pre , thread , tenv , pdesc ) accesses reported_acc =
let pname = Procdesc . get_proc_name pdesc in
let pname = Procdesc . get_proc_name pdesc in
if is_duplicate_report access pname reported_acc then reported_acc
else
match ( TraceElem . kind access , pre ) with
match ( TraceElem . kind access , pre ) with
| ( Access . InterfaceCall unannoted_call_pname
| ( Access . InterfaceCall unannoted_call_pname
, ( AccessPrecondition . Unprotected _ | AccessPrecondition . TotallyUnprotected ) ) ->
, ( AccessPrecondition . Unprotected _ | AccessPrecondition . TotallyUnprotected ) ) ->
if ThreadsDomain . is_any thread && is_marked_thread_safe pdesc tenv then (
if ThreadsDomain . is_any thread && is_marked_thread_safe pdesc tenv then
(* un-annotated interface call + no lock in method marked thread-safe. warn *)
(* un-annotated interface call + no lock in method marked thread-safe. warn *)
report_unannotated_interface_violation tenv pdesc access thread unannoted_call_pname ;
report_unannotated_interface_violation tenv pdesc access thread unannoted_call_pname
update_reported access pname reported_acc )
else reported_acc
| Access . InterfaceCall _ , AccessPrecondition . Protected _ ->
| Access . InterfaceCall _ , AccessPrecondition . Protected _ ->
(* un-annotated interface call, but it's protected by a lock/thread. don't report *)
(* un-annotated interface call, but it's protected by a lock/thread. don't report *)
reported_acc
()
| ( ( Access . Write _ | ContainerWrite _ )
| ( ( Access . Write _ | ContainerWrite _ )
, ( AccessPrecondition . Unprotected _ | AccessPrecondition . TotallyUnprotected ) ) -> (
, ( AccessPrecondition . Unprotected _ | AccessPrecondition . TotallyUnprotected ) ) -> (
match Procdesc . get_proc_name pdesc with
match Procdesc . get_proc_name pdesc with
@ -1368,20 +1323,18 @@ let report_unsafe_accesses
else None )
else None )
accesses
accesses
in
in
if List . is_empty writes_on_background_thread && not ( ThreadsDomain . is_any thread ) then
if not ( List . is_empty writes_on_background_thread && not ( ThreadsDomain . is_any thread ) )
reported_acc
then
else (
report_thread_safety_violation tenv pdesc
report_thread_safety_violation tenv pdesc
~ make_description : make_unprotected_write_description
~ make_description : make_unprotected_write_description
~ report_kind : ( WriteWriteRace writes_on_background_thread ) access thread ;
~ report_kind : ( WriteWriteRace writes_on_background_thread ) access thread
update_reported access pname reported_acc )
| _ ->
| _ ->
(* Do not report unprotected writes when an access can't run in parallel with itself, or
(* Do not report unprotected writes when an access can't run in parallel with itself, or
for ObjC_Cpp * )
for ObjC_Cpp * )
reported_acc )
() )
| ( Access . Write _ | ContainerWrite _ ) , AccessPrecondition . Protected _ ->
| ( Access . Write _ | ContainerWrite _ ) , AccessPrecondition . Protected _ ->
(* protected write, do nothing *)
(* protected write, do nothing *)
reported_acc
()
| ( ( Access . Read _ | ContainerRead _ )
| ( ( Access . Read _ | ContainerRead _ )
, ( AccessPrecondition . Unprotected _ | AccessPrecondition . TotallyUnprotected ) ) ->
, ( AccessPrecondition . Unprotected _ | AccessPrecondition . TotallyUnprotected ) ) ->
(* unprotected read. report all writes as conflicts for java. for c++ filter out
(* unprotected read. report all writes as conflicts for java. for c++ filter out
@ -1406,14 +1359,12 @@ let report_unsafe_accesses
is_conflict other_access pre other_thread )
is_conflict other_access pre other_thread )
accesses
accesses
in
in
if List . is_empty all_writes then reported_acc
if not ( List . is_empty all_writes ) then
else (
report_thread_safety_violation tenv pdesc
report_thread_safety_violation tenv pdesc
~ make_description : ( make_read_write_race_description ~ read_is_sync : false all_writes )
~ make_description : ( make_read_write_race_description ~ read_is_sync : false all_writes )
~ report_kind :
~ report_kind :
( ReadWriteRace ( List . map ~ f : ( fun ( access , _ , _ , _ , _ ) -> access ) all_writes ) )
( ReadWriteRace ( List . map ~ f : ( fun ( access , _ , _ , _ , _ ) -> access ) all_writes ) )
access thread ;
access thread
update_reported access pname reported_acc )
| ( Access . Read _ | ContainerRead _ ) , AccessPrecondition . Protected excl ->
| ( Access . Read _ | ContainerRead _ ) , AccessPrecondition . Protected excl ->
(* protected read. report unprotected writes and opposite protected writes as conflicts
(* protected read. report unprotected writes and opposite protected writes as conflicts
Thread and Lock are opposites of one another , and Both has no opposite * )
Thread and Lock are opposites of one another , and Both has no opposite * )
@ -1437,25 +1388,17 @@ let report_unsafe_accesses
false )
false )
accesses
accesses
in
in
if List . is_empty conflicting_writes then reported_acc
if not ( List . is_empty conflicting_writes ) then
else (
(* protected read with conflicting unprotected write ( s ) . warn. *)
(* protected read with conflicting unprotected write ( s ) . warn. *)
report_thread_safety_violation tenv pdesc
report_thread_safety_violation tenv pdesc
~ make_description :
~ make_description :
( make_read_write_race_description ~ read_is_sync : true conflicting_writes )
( make_read_write_race_description ~ read_is_sync : true conflicting_writes )
~ report_kind :
~ report_kind :
( ReadWriteRace
( ReadWriteRace ( List . map ~ f : ( fun ( access , _ , _ , _ , _ ) -> access ) conflicting_writes ) )
( List . map ~ f : ( fun ( access , _ , _ , _ , _ ) -> access ) conflicting_writes ) )
access thread
access thread ;
update_reported access pname reported_acc )
in
AccessListMap . fold
( fun _ grouped_accesses 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
in
AccessListMap . iter
( fun _ grouped_accesses ->
let class_has_mutex_member objc_cpp tenv =
let class_has_mutex_member objc_cpp tenv =
let class_name = Typ . Procname . objc_cpp_get_class_type_name objc_cpp in
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
let matcher = QualifiedCppName . Match . of_fuzzy_qual_names [ " std::mutex " ] in
@ -1487,10 +1430,10 @@ let report_unsafe_accesses
let reportable_accesses =
let reportable_accesses =
List . filter ~ f : ( fun ( _ , _ , _ , tenv , pdesc ) -> should_report pdesc tenv ) grouped_accesses
List . filter ~ f : ( fun ( _ , _ , _ , tenv , pdesc ) -> should_report pdesc tenv ) grouped_accesses
in
in
List . fold
List . iter
~ f : ( fun acc acc ess -> report_unsafe_access access reportable_accesses acc )
~ f : ( fun acc ess -> report_unsafe_access access reportable_accesses )
reportable_accesses ~ init : reported )
reportable_accesses )
aggregated_access_map empty_reported
aggregated_access_map
| > ignore
| > ignore