|
|
@ -227,7 +227,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
|
|
|
|
; threads= ThreadsDomain.empty
|
|
|
|
; threads= ThreadsDomain.empty
|
|
|
|
; accesses= callee_accesses
|
|
|
|
; accesses= callee_accesses
|
|
|
|
; return_ownership
|
|
|
|
; return_ownership
|
|
|
|
; return_attributes= AttributeSetDomain.empty }
|
|
|
|
; return_attributes= AttributeSetDomain.empty
|
|
|
|
|
|
|
|
; wobbly_paths= StabilityDomain.empty }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let get_summary caller_pdesc callee_pname actuals callee_loc tenv (astate: Domain.astate) =
|
|
|
|
let get_summary caller_pdesc callee_pname actuals callee_loc tenv (astate: Domain.astate) =
|
|
|
@ -379,6 +380,11 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
|
|
|
|
AccessDomain.fold update_accesses accesses caller_astate.accesses
|
|
|
|
AccessDomain.fold update_accesses accesses caller_astate.accesses
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(***********************************************************************)
|
|
|
|
|
|
|
|
(* Wobbly paths and where to find them. *)
|
|
|
|
|
|
|
|
(***********************************************************************)
|
|
|
|
|
|
|
|
(***********************************************************************)
|
|
|
|
|
|
|
|
|
|
|
|
let exec_instr (astate: Domain.astate) ({ProcData.tenv; extras; pdesc} as proc_data) _
|
|
|
|
let exec_instr (astate: Domain.astate) ({ProcData.tenv; extras; pdesc} as proc_data) _
|
|
|
|
(instr: HilInstr.t) =
|
|
|
|
(instr: HilInstr.t) =
|
|
|
|
let open Domain in
|
|
|
|
let open Domain in
|
|
|
@ -393,7 +399,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
|
|
|
|
let ownership =
|
|
|
|
let ownership =
|
|
|
|
OwnershipDomain.add (ret_base, []) OwnershipAbstractValue.owned astate.ownership
|
|
|
|
OwnershipDomain.add (ret_base, []) OwnershipAbstractValue.owned astate.ownership
|
|
|
|
in
|
|
|
|
in
|
|
|
|
{astate with accesses; ownership}
|
|
|
|
(* Record all actuals as wobbly paths *)
|
|
|
|
|
|
|
|
let wobbly_paths = StabilityDomain.add_wobbly_actuals actuals astate.wobbly_paths in
|
|
|
|
|
|
|
|
{astate with accesses; ownership; wobbly_paths}
|
|
|
|
| Call (ret_opt, Direct callee_pname, actuals, call_flags, loc)
|
|
|
|
| Call (ret_opt, Direct callee_pname, actuals, call_flags, loc)
|
|
|
|
-> (
|
|
|
|
-> (
|
|
|
|
let accesses_with_unannotated_calls =
|
|
|
|
let accesses_with_unannotated_calls =
|
|
|
@ -404,7 +412,8 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
|
|
|
|
add_reads actuals loc accesses_with_unannotated_calls astate.locks astate.threads
|
|
|
|
add_reads actuals loc accesses_with_unannotated_calls astate.locks astate.threads
|
|
|
|
astate.ownership proc_data
|
|
|
|
astate.ownership proc_data
|
|
|
|
in
|
|
|
|
in
|
|
|
|
let astate = {astate with accesses} in
|
|
|
|
let wobbly_paths = StabilityDomain.add_wobbly_actuals actuals astate.wobbly_paths in
|
|
|
|
|
|
|
|
let astate = {astate with accesses; wobbly_paths} in
|
|
|
|
let astate =
|
|
|
|
let astate =
|
|
|
|
match Models.get_thread callee_pname with
|
|
|
|
match Models.get_thread callee_pname with
|
|
|
|
| BackgroundThread ->
|
|
|
|
| BackgroundThread ->
|
|
|
@ -472,7 +481,13 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
|
|
|
|
in
|
|
|
|
in
|
|
|
|
{summary with accesses= rebased_accesses} )
|
|
|
|
{summary with accesses= rebased_accesses} )
|
|
|
|
with
|
|
|
|
with
|
|
|
|
| Some {threads; locks; accesses; return_ownership; return_attributes} ->
|
|
|
|
| Some
|
|
|
|
|
|
|
|
{ threads
|
|
|
|
|
|
|
|
; locks
|
|
|
|
|
|
|
|
; accesses
|
|
|
|
|
|
|
|
; return_ownership
|
|
|
|
|
|
|
|
; return_attributes
|
|
|
|
|
|
|
|
; wobbly_paths= callee_wps } ->
|
|
|
|
let locks = LocksDomain.join locks astate.locks in
|
|
|
|
let locks = LocksDomain.join locks astate.locks in
|
|
|
|
let threads =
|
|
|
|
let threads =
|
|
|
|
match (astate.threads, threads) with
|
|
|
|
match (astate.threads, threads) with
|
|
|
@ -490,7 +505,14 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
|
|
|
|
let ownership, attribute_map =
|
|
|
|
let ownership, attribute_map =
|
|
|
|
propagate_return ret_opt return_ownership return_attributes actuals astate
|
|
|
|
propagate_return ret_opt return_ownership return_attributes actuals astate
|
|
|
|
in
|
|
|
|
in
|
|
|
|
{locks; threads; accesses; ownership; attribute_map}
|
|
|
|
(* Remapping wobble paths; also bases that are not in caller's wobbly paths, i.e., callee's locals *)
|
|
|
|
|
|
|
|
let callee_wps_rebased =
|
|
|
|
|
|
|
|
Option.value_map ~default:callee_wps
|
|
|
|
|
|
|
|
~f:(fun summary -> StabilityDomain.rebase_paths actuals summary callee_wps)
|
|
|
|
|
|
|
|
callee_pdesc
|
|
|
|
|
|
|
|
in
|
|
|
|
|
|
|
|
let wobbly_paths = StabilityDomain.join wobbly_paths callee_wps_rebased in
|
|
|
|
|
|
|
|
{locks; threads; accesses; ownership; attribute_map; wobbly_paths}
|
|
|
|
| None ->
|
|
|
|
| None ->
|
|
|
|
let should_assume_returns_ownership (call_flags: CallFlags.t) actuals =
|
|
|
|
let should_assume_returns_ownership (call_flags: CallFlags.t) actuals =
|
|
|
|
(* assume non-interface methods with no summary and no parameters return
|
|
|
|
(* assume non-interface methods with no summary and no parameters return
|
|
|
@ -582,7 +604,17 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
|
|
|
|
in
|
|
|
|
in
|
|
|
|
let ownership = propagate_ownership lhs_access_path rhs_exp astate.ownership in
|
|
|
|
let ownership = propagate_ownership lhs_access_path rhs_exp astate.ownership in
|
|
|
|
let attribute_map = propagate_attributes lhs_access_path rhs_exp astate.attribute_map in
|
|
|
|
let attribute_map = propagate_attributes lhs_access_path rhs_exp astate.attribute_map in
|
|
|
|
{astate with accesses; ownership; attribute_map}
|
|
|
|
(* [TODO] Do not add this path as wobbly, if it's the _first_
|
|
|
|
|
|
|
|
initialization of a local variable (e.g. A z = getA(); -->
|
|
|
|
|
|
|
|
now z is considered wobbly).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
At the moment, I don't know how to distinguish those from
|
|
|
|
|
|
|
|
plain re-assignnments, so a lot of spurious wobbly paths is
|
|
|
|
|
|
|
|
negerated. *)
|
|
|
|
|
|
|
|
let wobbly_paths =
|
|
|
|
|
|
|
|
StabilityDomain.add_wobbly_paths_assign lhs_access_path rhs_exp astate.wobbly_paths
|
|
|
|
|
|
|
|
in
|
|
|
|
|
|
|
|
{astate with accesses; ownership; attribute_map; wobbly_paths}
|
|
|
|
| Assume (assume_exp, _, _, loc) ->
|
|
|
|
| Assume (assume_exp, _, _, loc) ->
|
|
|
|
let rec eval_binop op var e1 e2 =
|
|
|
|
let rec eval_binop op var e1 e2 =
|
|
|
|
match (eval_bexp var e1, eval_bexp var e2) with
|
|
|
|
match (eval_bexp var e1, eval_bexp var e2) with
|
|
|
@ -662,7 +694,8 @@ let empty_post : RacerDDomain.summary =
|
|
|
|
; locks= RacerDDomain.LocksDomain.empty
|
|
|
|
; locks= RacerDDomain.LocksDomain.empty
|
|
|
|
; accesses= RacerDDomain.AccessDomain.empty
|
|
|
|
; accesses= RacerDDomain.AccessDomain.empty
|
|
|
|
; return_ownership= RacerDDomain.OwnershipAbstractValue.unowned
|
|
|
|
; return_ownership= RacerDDomain.OwnershipAbstractValue.unowned
|
|
|
|
; return_attributes= RacerDDomain.AttributeSetDomain.empty }
|
|
|
|
; return_attributes= RacerDDomain.AttributeSetDomain.empty
|
|
|
|
|
|
|
|
; wobbly_paths= RacerDDomain.StabilityDomain.empty }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let analyze_procedure {Callbacks.proc_desc; get_proc_desc; tenv; summary} =
|
|
|
|
let analyze_procedure {Callbacks.proc_desc; get_proc_desc; tenv; summary} =
|
|
|
@ -736,7 +769,7 @@ let analyze_procedure {Callbacks.proc_desc; get_proc_desc; tenv; summary} =
|
|
|
|
{RacerDDomain.empty with ownership; threads}
|
|
|
|
{RacerDDomain.empty with ownership; threads}
|
|
|
|
in
|
|
|
|
in
|
|
|
|
match Analyzer.compute_post proc_data ~initial with
|
|
|
|
match Analyzer.compute_post proc_data ~initial with
|
|
|
|
| Some {threads; locks; accesses; ownership; attribute_map} ->
|
|
|
|
| Some {threads; locks; accesses; ownership; attribute_map; wobbly_paths} ->
|
|
|
|
let return_var_ap =
|
|
|
|
let return_var_ap =
|
|
|
|
AccessPath.of_pvar
|
|
|
|
AccessPath.of_pvar
|
|
|
|
(Pvar.get_ret_pvar (Procdesc.get_proc_name proc_desc))
|
|
|
|
(Pvar.get_ret_pvar (Procdesc.get_proc_name proc_desc))
|
|
|
@ -747,7 +780,7 @@ let analyze_procedure {Callbacks.proc_desc; get_proc_desc; tenv; summary} =
|
|
|
|
try AttributeMapDomain.find return_var_ap attribute_map with Not_found ->
|
|
|
|
try AttributeMapDomain.find return_var_ap attribute_map with Not_found ->
|
|
|
|
AttributeSetDomain.empty
|
|
|
|
AttributeSetDomain.empty
|
|
|
|
in
|
|
|
|
in
|
|
|
|
let post = {threads; locks; accesses; return_ownership; return_attributes} in
|
|
|
|
let post = {threads; locks; accesses; return_ownership; return_attributes; wobbly_paths} in
|
|
|
|
Summary.update_summary post summary
|
|
|
|
Summary.update_summary post summary
|
|
|
|
| None ->
|
|
|
|
| None ->
|
|
|
|
summary
|
|
|
|
summary
|
|
|
@ -919,7 +952,45 @@ let make_trace ~report_kind original_path pdesc =
|
|
|
|
(original_trace, original_end, None)
|
|
|
|
(original_trace, original_end, None)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let report_thread_safety_violation tenv pdesc ~make_description ~report_kind access thread =
|
|
|
|
let ignore_var v = Var.is_global v || Var.is_return v
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(* Checking for a wobbly path *)
|
|
|
|
|
|
|
|
let contaminated_race_msg access wobbly_paths =
|
|
|
|
|
|
|
|
let open RacerDDomain in
|
|
|
|
|
|
|
|
let wobbly_path_opt =
|
|
|
|
|
|
|
|
match TraceElem.kind access with
|
|
|
|
|
|
|
|
| TraceElem.Kind.Read access_path
|
|
|
|
|
|
|
|
| Write access_path
|
|
|
|
|
|
|
|
(* Access paths rooted in static variables are always race-prone,
|
|
|
|
|
|
|
|
hence do not complain about contamination. *)
|
|
|
|
|
|
|
|
when not (access_path |> fst |> fst |> ignore_var) ->
|
|
|
|
|
|
|
|
let proper_prefix_path = AccessPath.truncate access_path in
|
|
|
|
|
|
|
|
let base, accesses = proper_prefix_path in
|
|
|
|
|
|
|
|
let rec prefix_in_wobbly_paths prefix = function
|
|
|
|
|
|
|
|
| [] ->
|
|
|
|
|
|
|
|
let wobbly = (base, []) in
|
|
|
|
|
|
|
|
if StabilityDomain.mem (AccessPath.Abs.Exact wobbly) wobbly_paths then
|
|
|
|
|
|
|
|
Some (wobbly, access_path)
|
|
|
|
|
|
|
|
else None
|
|
|
|
|
|
|
|
| access :: accesses ->
|
|
|
|
|
|
|
|
let prefix' = prefix @ [access] in
|
|
|
|
|
|
|
|
let candidate = (base, prefix') in
|
|
|
|
|
|
|
|
if StabilityDomain.mem (AccessPath.Abs.Exact candidate) wobbly_paths then
|
|
|
|
|
|
|
|
Some (candidate, access_path)
|
|
|
|
|
|
|
|
else prefix_in_wobbly_paths prefix' accesses
|
|
|
|
|
|
|
|
in
|
|
|
|
|
|
|
|
prefix_in_wobbly_paths [] accesses
|
|
|
|
|
|
|
|
| _ ->
|
|
|
|
|
|
|
|
None
|
|
|
|
|
|
|
|
in
|
|
|
|
|
|
|
|
Option.map wobbly_path_opt ~f:(fun (wobbly_path, access_path) ->
|
|
|
|
|
|
|
|
F.asprintf
|
|
|
|
|
|
|
|
"@\n\nNote that the prefix path %a has been contaminated during the execution, so the reported race on %a might be a false positive.@\n\n"
|
|
|
|
|
|
|
|
AccessPath.pp wobbly_path AccessPath.pp access_path )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let report_thread_safety_violation tenv pdesc ~make_description ~report_kind access thread
|
|
|
|
|
|
|
|
wobbly_paths =
|
|
|
|
let open RacerDDomain in
|
|
|
|
let open RacerDDomain in
|
|
|
|
let pname = Procdesc.get_proc_name pdesc in
|
|
|
|
let pname = Procdesc.get_proc_name pdesc in
|
|
|
|
let report_one_path ((_, sinks) as path) =
|
|
|
|
let report_one_path ((_, sinks) as path) =
|
|
|
@ -952,7 +1023,12 @@ let report_thread_safety_violation tenv pdesc ~make_description ~report_kind acc
|
|
|
|
(* why we are reporting it *)
|
|
|
|
(* why we are reporting it *)
|
|
|
|
let issue_type, explanation = get_reporting_explanation report_kind tenv pname thread in
|
|
|
|
let issue_type, explanation = get_reporting_explanation report_kind tenv pname thread in
|
|
|
|
let error_message = F.sprintf "%s%s" description explanation in
|
|
|
|
let error_message = F.sprintf "%s%s" description explanation in
|
|
|
|
let exn = Exceptions.Checkers (issue_type, Localise.verbatim_desc error_message) in
|
|
|
|
let contaminated = contaminated_race_msg access wobbly_paths in
|
|
|
|
|
|
|
|
let exn =
|
|
|
|
|
|
|
|
Exceptions.Checkers
|
|
|
|
|
|
|
|
( issue_type
|
|
|
|
|
|
|
|
, Localise.verbatim_desc (error_message ^ Option.value contaminated ~default:"") )
|
|
|
|
|
|
|
|
in
|
|
|
|
let end_locs = Option.to_list original_end @ Option.to_list conflict_end in
|
|
|
|
let end_locs = Option.to_list original_end @ Option.to_list conflict_end in
|
|
|
|
let access = IssueAuxData.encode (pname, access, end_locs) in
|
|
|
|
let access = IssueAuxData.encode (pname, access, end_locs) in
|
|
|
|
Reporting.log_error_deprecated ~store_summary:true pname ~loc ~ltr ~access exn
|
|
|
|
Reporting.log_error_deprecated ~store_summary:true pname ~loc ~ltr ~access exn
|
|
|
@ -971,7 +1047,7 @@ let report_unannotated_interface_violation tenv pdesc access thread reported_pna
|
|
|
|
class_name MF.pp_monospaced "@ThreadSafe"
|
|
|
|
class_name MF.pp_monospaced "@ThreadSafe"
|
|
|
|
in
|
|
|
|
in
|
|
|
|
report_thread_safety_violation tenv pdesc ~make_description ~report_kind:UnannotatedInterface
|
|
|
|
report_thread_safety_violation tenv pdesc ~make_description ~report_kind:UnannotatedInterface
|
|
|
|
access thread
|
|
|
|
access thread RacerDDomain.StabilityDomain.empty
|
|
|
|
| _ ->
|
|
|
|
| _ ->
|
|
|
|
(* skip reporting on C++ *)
|
|
|
|
(* skip reporting on C++ *)
|
|
|
|
()
|
|
|
|
()
|
|
|
@ -1000,7 +1076,8 @@ type reported_access =
|
|
|
|
; threads: RacerDDomain.ThreadsDomain.astate
|
|
|
|
; threads: RacerDDomain.ThreadsDomain.astate
|
|
|
|
; precondition: RacerDDomain.AccessData.t
|
|
|
|
; precondition: RacerDDomain.AccessData.t
|
|
|
|
; tenv: Tenv.t
|
|
|
|
; tenv: Tenv.t
|
|
|
|
; procdesc: Procdesc.t }
|
|
|
|
; procdesc: Procdesc.t
|
|
|
|
|
|
|
|
; wobbly_paths: RacerDDomain.StabilityDomain.astate }
|
|
|
|
|
|
|
|
|
|
|
|
let make_read_write_race_description ~read_is_sync (conflict: reported_access) pname
|
|
|
|
let make_read_write_race_description ~read_is_sync (conflict: reported_access) pname
|
|
|
|
final_sink_site initial_sink_site final_sink =
|
|
|
|
final_sink_site initial_sink_site final_sink =
|
|
|
@ -1095,7 +1172,8 @@ let report_unsafe_accesses (aggregated_access_map: reported_access list AccessLi
|
|
|
|
reported
|
|
|
|
reported
|
|
|
|
else reported
|
|
|
|
else reported
|
|
|
|
in
|
|
|
|
in
|
|
|
|
let report_unsafe_access {access; precondition; threads; tenv; procdesc} accesses reported_acc =
|
|
|
|
let report_unsafe_access {access; precondition; threads; tenv; procdesc; wobbly_paths} accesses
|
|
|
|
|
|
|
|
reported_acc =
|
|
|
|
let pname = Procdesc.get_proc_name procdesc in
|
|
|
|
let pname = Procdesc.get_proc_name procdesc in
|
|
|
|
if is_duplicate_report access pname reported_acc then reported_acc
|
|
|
|
if is_duplicate_report access pname reported_acc then reported_acc
|
|
|
|
else
|
|
|
|
else
|
|
|
@ -1133,7 +1211,7 @@ let report_unsafe_accesses (aggregated_access_map: reported_access list AccessLi
|
|
|
|
let conflict = List.hd writes_on_background_thread in
|
|
|
|
let conflict = List.hd writes_on_background_thread in
|
|
|
|
report_thread_safety_violation tenv procdesc
|
|
|
|
report_thread_safety_violation tenv procdesc
|
|
|
|
~make_description:make_unprotected_write_description
|
|
|
|
~make_description:make_unprotected_write_description
|
|
|
|
~report_kind:(WriteWriteRace conflict) access threads ;
|
|
|
|
~report_kind:(WriteWriteRace conflict) access threads wobbly_paths ;
|
|
|
|
update_reported access pname reported_acc
|
|
|
|
update_reported access pname reported_acc
|
|
|
|
else reported_acc
|
|
|
|
else reported_acc
|
|
|
|
| _ ->
|
|
|
|
| _ ->
|
|
|
@ -1163,7 +1241,7 @@ let report_unsafe_accesses (aggregated_access_map: reported_access list AccessLi
|
|
|
|
let conflict = List.hd_exn all_writes in
|
|
|
|
let conflict = List.hd_exn all_writes in
|
|
|
|
report_thread_safety_violation tenv procdesc
|
|
|
|
report_thread_safety_violation tenv procdesc
|
|
|
|
~make_description:(make_read_write_race_description ~read_is_sync:false conflict)
|
|
|
|
~make_description:(make_read_write_race_description ~read_is_sync:false conflict)
|
|
|
|
~report_kind:(ReadWriteRace conflict.access) access threads ;
|
|
|
|
~report_kind:(ReadWriteRace conflict.access) access threads wobbly_paths ;
|
|
|
|
update_reported access pname reported_acc
|
|
|
|
update_reported access pname reported_acc
|
|
|
|
else reported_acc
|
|
|
|
else reported_acc
|
|
|
|
| Access.Read _ | ContainerRead _ ->
|
|
|
|
| Access.Read _ | ContainerRead _ ->
|
|
|
@ -1191,7 +1269,7 @@ let report_unsafe_accesses (aggregated_access_map: reported_access list AccessLi
|
|
|
|
(* protected read with conflicting unprotected write(s). warn. *)
|
|
|
|
(* protected read with conflicting unprotected write(s). warn. *)
|
|
|
|
report_thread_safety_violation tenv procdesc
|
|
|
|
report_thread_safety_violation tenv procdesc
|
|
|
|
~make_description:(make_read_write_race_description ~read_is_sync:true conflict)
|
|
|
|
~make_description:(make_read_write_race_description ~read_is_sync:true conflict)
|
|
|
|
~report_kind:(ReadWriteRace conflict.access) access threads ;
|
|
|
|
~report_kind:(ReadWriteRace conflict.access) access threads wobbly_paths ;
|
|
|
|
update_reported access pname reported_acc
|
|
|
|
update_reported access pname reported_acc
|
|
|
|
else reported_acc
|
|
|
|
else reported_acc
|
|
|
|
in
|
|
|
|
in
|
|
|
@ -1331,8 +1409,8 @@ module MayAliasQuotientedAccessListMap : QuotientedAccessListMap = struct
|
|
|
|
AccessPath.equal p1 p2
|
|
|
|
AccessPath.equal p1 p2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(* equivalence relation computing whether two access paths may refer to the
|
|
|
|
(* equivalence relation computing whether two access paths may refer
|
|
|
|
same heap location. *)
|
|
|
|
to the same heap location. *)
|
|
|
|
let may_alias tenv p1 p2 =
|
|
|
|
let may_alias tenv p1 p2 =
|
|
|
|
let open Typ in
|
|
|
|
let open Typ in
|
|
|
|
let open AccessPath in
|
|
|
|
let open AccessPath in
|
|
|
@ -1403,12 +1481,13 @@ let should_filter_access access =
|
|
|
|
false
|
|
|
|
false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(* create a map from [abstraction of a memory loc] -> accesses that may touch that memory loc. for
|
|
|
|
(* create a map from [abstraction of a memory loc] -> accesses that
|
|
|
|
now, our abstraction is an access path like x.f.g whose concretization is the set of memory cells
|
|
|
|
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 *)
|
|
|
|
that x.f.g may point to during execution *)
|
|
|
|
let make_results_table (module AccessListMap : QuotientedAccessListMap) file_env =
|
|
|
|
let make_results_table (module AccessListMap : QuotientedAccessListMap) file_env =
|
|
|
|
let open RacerDDomain in
|
|
|
|
let open RacerDDomain in
|
|
|
|
let aggregate_post {threads; accesses} tenv procdesc acc =
|
|
|
|
let aggregate_post {threads; accesses; wobbly_paths} tenv procdesc acc =
|
|
|
|
AccessDomain.fold
|
|
|
|
AccessDomain.fold
|
|
|
|
(fun precondition accesses acc ->
|
|
|
|
(fun precondition accesses acc ->
|
|
|
|
PathDomain.Sinks.fold
|
|
|
|
PathDomain.Sinks.fold
|
|
|
@ -1416,7 +1495,9 @@ let make_results_table (module AccessListMap : QuotientedAccessListMap) file_env
|
|
|
|
let access_kind = TraceElem.kind access in
|
|
|
|
let access_kind = TraceElem.kind access in
|
|
|
|
if should_filter_access access_kind then acc
|
|
|
|
if should_filter_access access_kind then acc
|
|
|
|
else
|
|
|
|
else
|
|
|
|
let reported_access = {access; precondition; threads; tenv; procdesc} in
|
|
|
|
let reported_access : reported_access =
|
|
|
|
|
|
|
|
{access; threads; precondition; tenv; procdesc; wobbly_paths}
|
|
|
|
|
|
|
|
in
|
|
|
|
AccessListMap.add access_kind reported_access acc )
|
|
|
|
AccessListMap.add access_kind reported_access acc )
|
|
|
|
(PathDomain.sinks accesses) acc )
|
|
|
|
(PathDomain.sinks accesses) acc )
|
|
|
|
accesses acc
|
|
|
|
accesses acc
|
|
|
@ -1431,8 +1512,8 @@ let make_results_table (module AccessListMap : QuotientedAccessListMap) file_env
|
|
|
|
List.fold ~f:aggregate_posts file_env ~init:AccessListMap.empty |> AccessListMap.quotient
|
|
|
|
List.fold ~f:aggregate_posts file_env ~init:AccessListMap.empty |> AccessListMap.quotient
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(* aggregate all of the procedures in the file env by their declaring class. this lets us analyze
|
|
|
|
(* aggregate all of the procedures in the file env by their declaring
|
|
|
|
each class individually *)
|
|
|
|
class. this lets us analyze each class individually *)
|
|
|
|
let aggregate_by_class file_env =
|
|
|
|
let aggregate_by_class file_env =
|
|
|
|
List.fold file_env
|
|
|
|
List.fold file_env
|
|
|
|
~f:(fun acc ((_, pdesc) as proc) ->
|
|
|
|
~f:(fun acc ((_, pdesc) as proc) ->
|
|
|
@ -1449,8 +1530,9 @@ let aggregate_by_class file_env =
|
|
|
|
~init:String.Map.empty
|
|
|
|
~init:String.Map.empty
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(* Gathers results by analyzing all the methods in a file, then post-processes the results to check
|
|
|
|
(* Gathers results by analyzing all the methods in a file, then
|
|
|
|
an (approximation of) thread safety *)
|
|
|
|
post-processes the results to check an (approximation of) thread
|
|
|
|
|
|
|
|
safety *)
|
|
|
|
let file_analysis {Callbacks.procedures} =
|
|
|
|
let file_analysis {Callbacks.procedures} =
|
|
|
|
String.Map.iter
|
|
|
|
String.Map.iter
|
|
|
|
~f:(fun class_env ->
|
|
|
|
~f:(fun class_env ->
|
|
|
|