@ -1030,49 +1030,6 @@ let collect_conflicts sink (*kind*) threaded tab = (*kind implicitly Read for no
)
procs_and_threaded_and_accesses
(* keep only the first copy of an access per procedure,
and keep at most one warning per line ( they are usually interprocedural accesses
to different fields generated by the same call ) * )
let de_dup trace =
let select_representatives original_sinks predicate =
let list_of_original_sinks = ThreadSafetyDomain . PathDomain . Sinks . elements original_sinks in
ThreadSafetyDomain . PathDomain . Sinks . filter
( fun sink ->
(* for each sink we will keep one in the equivalence class of those
satisfying predicate . We select that by using find_exn to get
the first element equivalent ot sink in a list of sinks . This
first element is the dedup representative , and it happens to
typically be the first such access in a method . * )
let first_sink =
List . find_exn
~ f : ( fun sink2 -> predicate sink sink2 )
list_of_original_sinks in
Int . equal ( ThreadSafetyDomain . TraceElem . compare sink first_sink ) 0
)
original_sinks in
let de_duped_sinks_by_accesses = select_representatives
( ThreadSafetyDomain . PathDomain . sinks trace )
equal_accesses in
let de_duped_sinks_by_locs_and_accesses = select_representatives
de_duped_sinks_by_accesses
equal_locs in
ThreadSafetyDomain . PathDomain . update_sinks trace de_duped_sinks_by_locs_and_accesses
let strip_reads_that_have_co_located_write reads writes =
let set_of_read_sinks = ThreadSafetyDomain . PathDomain . sinks reads in
let set_of_write_sinks = ThreadSafetyDomain . PathDomain . sinks writes in
let stripped_read_sinks =
ThreadSafetyDomain . PathDomain . Sinks . filter
( fun sink -> not ( ThreadSafetyDomain . PathDomain . Sinks . exists
( fun sink2 -> equal_locs sink sink2 )
set_of_write_sinks
)
)
set_of_read_sinks in
ThreadSafetyDomain . PathDomain . update_sinks reads stripped_read_sinks
(* A helper function used in the error reporting *)
let pp_accesses_sink fmt ~ is_write_access sink =
let access_path , _ = ThreadSafetyDomain . PathDomain . Sink . kind sink in
@ -1116,7 +1073,7 @@ let report_thread_safety_violations
List . iter
~ f : report_one_path
( PathDomain . get_reportable_sink_paths ( de_dup trace ) ~ trace_of_pname )
( PathDomain . get_reportable_sink_paths trace ~ trace_of_pname )
let make_unprotected_write_description
tenv pname final_sink_site initial_sink_site final_sink _ _ =
@ -1190,8 +1147,27 @@ let report_unsafe_accesses tab ~should_report aggregated_access_map =
(* protected write, do nothing *)
reported_sites
| Access . Read , AccessPrecondition . Unprotected _ ->
(* unprotected read. TODO: report all writes as conflicts *)
(* unprotected read. report all writes as conflicts *)
let all_writes =
List . filter
~ f : ( fun ( other_access , _ , other_threaded , _ , _ ) ->
TraceElem . is_write other_access && not ( threaded && other_threaded ) )
accesses in
if List . is_empty all_writes
then
reported_sites
else
begin
report_thread_safety_violations
tenv
pdesc
~ get_unsafe_accesses : get_possibly_unsafe_reads
make_read_write_race_description
( PathDomain . of_sink access )
threaded
tab ;
CallSite . Set . add call_site reported_sites
end ;
| Access . Read , AccessPrecondition . Protected ->
(* protected read. report unprotected writes as conflicts *)
let unprotected_writes =
@ -1251,30 +1227,6 @@ let aggregate_accesses tab =
tab
AccessListMap . empty
(* find those elements of reads which have conflicts
somewhere else , and report them * )
let report_reads ( tenv , pdesc ) reads threaded tab =
let racy_read_sinks =
ThreadSafetyDomain . PathDomain . Sinks . filter
( fun sink ->
(* there exists a postcondition whose write set conflicts with
sink * )
not ( List . is_empty ( collect_conflicts sink threaded tab ) )
)
( ThreadSafetyDomain . PathDomain . sinks reads )
in
let racy_reads =
ThreadSafetyDomain . PathDomain . update_sinks reads racy_read_sinks
in
report_thread_safety_violations
tenv
pdesc
~ get_unsafe_accesses : get_possibly_unsafe_reads
make_read_write_race_description
racy_reads
threaded
tab
(* Currently we analyze if there is an @ThreadSafe annotation on at least one of
the classes in a file . This might be tightened in future or even broadened in future
based on other criteria * )
@ -1293,7 +1245,6 @@ let should_report_on_file file_env =
not ( List . exists ~ f : current_class_marked_not_threadsafe file_env ) &&
List . exists ~ f : current_class_or_super_marked_threadsafe file_env
(* *
Principles for race reporting .
Two accesses are excluded if they are both protetected by the same lock or
@ -1327,33 +1278,7 @@ let process_results_table file_env tab =
is_thread_safe_method pdesc tenv in
aggregate_accesses tab
| > report_unsafe_accesses tab ~ should_report ;
ResultsTableType . iter (* report errors for each method *)
( fun ( ( tenv , pdesc ) as proc_env ) ( threaded , _ , accesses , _ ) ->
if should_report pdesc tenv
then
let open ThreadSafetyDomain in
let reads , writes =
AccessDomain . fold
( fun pre accesses ( reads_acc , writes_acc ) ->
let read_accesses , write_accesses =
PathDomain . Sinks . partition TraceElem . is_read ( PathDomain . sinks accesses ) in
AccessDomain . add pre ( PathDomain . update_sinks accesses read_accesses ) reads_acc ,
AccessDomain . add pre ( PathDomain . update_sinks accesses write_accesses ) writes_acc )
accesses
( AccessDomain . empty , AccessDomain . empty ) in
begin
let unsafe_writes = get_possibly_unsafe_writes writes in
let unsafe_reads = get_possibly_unsafe_reads reads in
let stripped_unsafe_reads = strip_reads_that_have_co_located_write
unsafe_reads
unsafe_writes in
report_reads proc_env stripped_unsafe_reads threaded tab
end
)
tab
| > report_unsafe_accesses tab ~ should_report
(* Gathers results by analyzing all the methods in a file, then post-processes the results to check
an ( approximation of ) thread safety * )