@ -100,10 +100,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| Some rhs_access_path -> IdAccessPathMapDomain . add lhs_id rhs_access_path id_map
| Some rhs_access_path -> IdAccessPathMapDomain . add lhs_id rhs_access_path id_map
| None -> id_map
| None -> id_map
let exec_instr
let exec_instr ( astate : Domain . astate ) { ProcData . pdesc ; tenv ; } _ =
( { ThreadSafetyDomain . locks ; reads ; writes ; id_map ; owned ; } as astate )
{ ProcData . pdesc ; tenv ; } _ =
let is_allocation pn =
let is_allocation pn =
Procname . equal pn BuiltinDecl . __new | |
Procname . equal pn BuiltinDecl . __new | |
Procname . equal pn BuiltinDecl . __new_array in
Procname . equal pn BuiltinDecl . __new_array in
@ -124,20 +121,21 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
let dummy_fieldname =
let dummy_fieldname =
Ident . create_fieldname ( Mangled . from_string ( Procname . get_method pn ) ) 0 in
Ident . create_fieldname ( Mangled . from_string ( Procname . get_method pn ) ) 0 in
let dummy_access_exp = Exp . Lfield ( exp , dummy_fieldname , typ ) in
let dummy_access_exp = Exp . Lfield ( exp , dummy_fieldname , typ ) in
let writes =
let unconditional_writes =
add_path_to_state dummy_access_exp typ loc astate . writes astate . id_map astate . owned tenv in
add_path_to_state
{ astate with writes ; } in
dummy_access_exp typ loc astate . unconditional_writes astate . id_map astate . owned tenv in
{ astate with unconditional_writes ; } in
let is_unprotected is_locked =
let is_unprotected is_locked =
not is_locked && not ( Procdesc . is_java_synchronized pdesc ) in
not is_locked && not ( Procdesc . is_java_synchronized pdesc ) in
let f_resolve_id = resolve_id id_map in
let f_resolve_id = resolve_id astate. id_map in
function
function
| Sil . Call ( Some ( lhs_id , lhs_typ ) , Const ( Cfun pn ) , _ , _ , _ ) when is_allocation pn ->
| Sil . Call ( Some ( lhs_id , lhs_typ ) , Const ( Cfun pn ) , _ , _ , _ ) when is_allocation pn ->
begin
begin
match AccessPath . of_lhs_exp ( Exp . Var lhs_id ) lhs_typ ~ f_resolve_id with
match AccessPath . of_lhs_exp ( Exp . Var lhs_id ) lhs_typ ~ f_resolve_id with
| Some lhs_access_path ->
| Some lhs_access_path ->
let owned ' = ThreadSafetyDomain . OwnershipDomain . add lhs_access_path owned in
let owned = ThreadSafetyDomain . OwnershipDomain . add lhs_access_path astate . owned in
{ astate with owned = owned' ; }
{ astate with owned ; }
| None ->
| None ->
astate
astate
end
end
@ -150,7 +148,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| Unlock ->
| Unlock ->
{ astate with locks = false ; }
{ astate with locks = false ; }
| NoEffect ->
| NoEffect ->
if is_unprotected locks && is_container_write pn tenv
if is_unprotected astate. locks && is_container_write pn tenv
then
then
match actuals with
match actuals with
| ( receiver_exp , receiver_typ ) :: _ ->
| ( receiver_exp , receiver_typ ) :: _ ->
@ -162,21 +160,23 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
else
else
begin
begin
match Summary . read_summary pdesc pn with
match Summary . read_summary pdesc pn with
| Some ( callee_locks , callee_reads , callee _writes) ->
| Some ( callee_locks , callee_reads , _ , callee _unconditional _writes) ->
let locks' = callee_locks | | locks in
let locks' = callee_locks | | astate. locks in
let astate' =
let astate' =
(* TODO ( 14842325 ) : report on constructors that aren't threadsafe
(* TODO ( 14842325 ) : report on constructors that aren't threadsafe
( e . g . , constructors that access static fields ) * )
( e . g . , constructors that access static fields ) * )
if is_unprotected locks'
if is_unprotected locks'
then
then
let call_site = CallSite . make pn loc in
let call_site = CallSite . make pn loc in
let reads ' =
let reads =
ThreadSafetyDomain . PathDomain . with_callsite callee_reads call_site
ThreadSafetyDomain . PathDomain . with_callsite callee_reads call_site
| > ThreadSafetyDomain . PathDomain . join reads in
| > ThreadSafetyDomain . PathDomain . join astate . reads in
let writes' =
let unconditional_writes =
ThreadSafetyDomain . PathDomain . with_callsite callee_writes call_site
ThreadSafetyDomain . PathDomain . with_callsite
| > ThreadSafetyDomain . PathDomain . join writes in
callee_unconditional_writes
{ astate with reads = reads' ; writes = writes' ; }
call_site
| > ThreadSafetyDomain . PathDomain . join astate . unconditional_writes in
{ astate with reads ; unconditional_writes ; }
else
else
astate in
astate in
{ astate' with locks = locks' ; }
{ astate' with locks = locks' ; }
@ -190,50 +190,54 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
{ astate with id_map = id_map' ; }
{ astate with id_map = id_map' ; }
| Sil . Store ( lhs_exp , lhs_typ , rhs_exp , loc ) ->
| Sil . Store ( lhs_exp , lhs_typ , rhs_exp , loc ) ->
let writes' =
let unconditional_ writes =
match lhs_exp with
match lhs_exp with
| Lfield ( _ , _ , typ ) when is_unprotected locks -> (* abstracts no lock being held *)
| Lfield ( _ , _ , typ )
add_path_to_state lhs_exp typ loc writes id_map owned tenv
when is_unprotected astate . locks -> (* abstracts no lock being held *)
| _ -> writes in
add_path_to_state
lhs_exp typ loc astate . unconditional_writes astate . id_map astate . owned tenv
| _ ->
astate . unconditional_writes in
(* if rhs is owned, propagate ownership to lhs. otherwise, remove lhs from ownerhsip set
(* if rhs is owned, propagate ownership to lhs. otherwise, remove lhs from ownerhsip set
( since it may have previously held an owned memory loc and is now being reassigned * )
( since it may have previously held an owned memory loc and is now being reassigned * )
let owned' =
let owned =
match AccessPath . of_lhs_exp lhs_exp lhs_typ ~ f_resolve_id ,
match AccessPath . of_lhs_exp lhs_exp lhs_typ ~ f_resolve_id ,
AccessPath . of_lhs_exp rhs_exp lhs_typ ~ f_resolve_id with
AccessPath . of_lhs_exp rhs_exp lhs_typ ~ f_resolve_id with
| Some lhs_access_path , Some rhs_access_path ->
| Some lhs_access_path , Some rhs_access_path ->
if ThreadSafetyDomain . OwnershipDomain . mem rhs_access_path owned
if ThreadSafetyDomain . OwnershipDomain . mem rhs_access_path astate. owned
then ThreadSafetyDomain . OwnershipDomain . add lhs_access_path owned
then ThreadSafetyDomain . OwnershipDomain . add lhs_access_path astate. owned
else ThreadSafetyDomain . OwnershipDomain . remove lhs_access_path owned
else ThreadSafetyDomain . OwnershipDomain . remove lhs_access_path astate. owned
| Some lhs_access_path , None ->
| Some lhs_access_path , None ->
ThreadSafetyDomain . OwnershipDomain . remove lhs_access_path owned
ThreadSafetyDomain . OwnershipDomain . remove lhs_access_path astate . owned
| _ -> owned in
| _ ->
{ astate with writes = writes' ; owned = owned' ; }
astate . owned in
{ astate with unconditional_writes ; owned ; }
| Sil . Load ( lhs_id , rhs_exp , rhs_typ , loc ) ->
| Sil . Load ( lhs_id , rhs_exp , rhs_typ , loc ) ->
let id_map ' = analyze_id_assignment ( Var . of_id lhs_id ) rhs_exp rhs_typ astate in
let id_map = analyze_id_assignment ( Var . of_id lhs_id ) rhs_exp rhs_typ astate in
let reads ' =
let reads =
match rhs_exp with
match rhs_exp with
| Lfield ( _ , _ , typ ) when is_unprotected locks ->
| Lfield ( _ , _ , typ ) when is_unprotected astate. locks ->
add_path_to_state rhs_exp typ loc reads id_map owned tenv
add_path_to_state rhs_exp typ loc astate. reads astate. id_map astate . owned tenv
| _ ->
| _ ->
reads in
astate. reads in
(* if rhs is owned, propagate ownership to lhs *)
(* if rhs is owned, propagate ownership to lhs *)
let owned ' =
let owned =
match AccessPath . of_lhs_exp rhs_exp rhs_typ ~ f_resolve_id with
match AccessPath . of_lhs_exp rhs_exp rhs_typ ~ f_resolve_id with
| Some rhs_access_path
| Some rhs_access_path
when ThreadSafetyDomain . OwnershipDomain . mem rhs_access_path owned ->
when ThreadSafetyDomain . OwnershipDomain . mem rhs_access_path astate. owned ->
ThreadSafetyDomain . OwnershipDomain . add ( AccessPath . of_id lhs_id rhs_typ ) owned
ThreadSafetyDomain . OwnershipDomain . add ( AccessPath . of_id lhs_id rhs_typ ) astate. owned
| _ ->
| _ ->
owned in
astate. owned in
{ astate with Domain . reads = reads' ; id_map = id_map' ; owned = owned' ; }
{ astate with Domain . reads ; id_map ; owned ; }
| Sil . Remove_temps ( ids , _ ) ->
| Sil . Remove_temps ( ids , _ ) ->
let id_map ' =
let id_map =
IList . fold_left
IList . fold_left
( fun acc id -> IdAccessPathMapDomain . remove ( Var . of_id id ) acc )
( fun acc id -> IdAccessPathMapDomain . remove ( Var . of_id id ) acc )
astate . id_map
astate . id_map
ids in
ids in
{ astate with id_map = id_map' ; }
{ astate with id_map ; }
| _ ->
| _ ->
astate
astate
@ -338,7 +342,11 @@ let make_results_table get_proc_desc file_env =
in
in
let compute_post_for_procedure = (* takes proc_env as arg *)
let compute_post_for_procedure = (* takes proc_env as arg *)
fun ( idenv , tenv , proc_name , proc_desc ) ->
fun ( idenv , tenv , proc_name , proc_desc ) ->
let empty = false , ThreadSafetyDomain . PathDomain . empty , ThreadSafetyDomain . PathDomain . empty in
let empty =
false ,
ThreadSafetyDomain . PathDomain . empty ,
ThreadSafetyDomain . ConditionalWritesDomain . empty ,
ThreadSafetyDomain . PathDomain . empty in
(* convert the abstract state to a summary by dropping the id map *)
(* convert the abstract state to a summary by dropping the id map *)
let compute_post ( { ProcData . pdesc ; tenv ; } as proc_data ) =
let compute_post ( { ProcData . pdesc ; tenv ; } as proc_data ) =
if should_analyze_proc pdesc tenv
if should_analyze_proc pdesc tenv
@ -346,8 +354,10 @@ let make_results_table get_proc_desc file_env =
begin
begin
if not ( Procdesc . did_preanalysis pdesc ) then Preanal . do_liveness pdesc tenv ;
if not ( Procdesc . did_preanalysis pdesc ) then Preanal . do_liveness pdesc tenv ;
match Analyzer . compute_post proc_data ~ initial : ThreadSafetyDomain . empty with
match Analyzer . compute_post proc_data ~ initial : ThreadSafetyDomain . empty with
| Some { locks ; reads ; writes ; } -> Some ( locks , reads , writes )
| Some { locks ; reads ; conditional_writes ; unconditional_writes ; } ->
| None -> None
Some ( locks , reads , conditional_writes , unconditional_writes )
| None ->
None
end
end
else
else
Some empty in
Some empty in
@ -390,7 +400,7 @@ let report_thread_safety_violations ( _, tenv, pname, pdesc) trace =
let open ThreadSafetyDomain in
let open ThreadSafetyDomain in
let trace_of_pname callee_pname =
let trace_of_pname callee_pname =
match Summary . read_summary pdesc callee_pname with
match Summary . read_summary pdesc callee_pname with
| Some ( _ , _ , writes) -> writes
| Some ( _ , _ , _ , unconditional_ writes) -> unconditional_ writes
| _ -> PathDomain . empty in
| _ -> PathDomain . empty in
let report_one_path ( ( _ , sinks ) as path ) =
let report_one_path ( ( _ , sinks ) as path ) =
let pp_accesses fmt sink =
let pp_accesses fmt sink =
@ -449,8 +459,8 @@ let process_results_table file_env tab =
( should_report_on_all_procs | | is_annotated Annotations . ia_is_thread_safe_method pdesc )
( should_report_on_all_procs | | is_annotated Annotations . ia_is_thread_safe_method pdesc )
&& should_report_on_proc proc_env in
&& should_report_on_proc proc_env in
ResultsTableType . iter (* report errors for each method *)
ResultsTableType . iter (* report errors for each method *)
( fun proc_env ( _ , _ , writes) ->
( fun proc_env ( _ , _ , _ , unconditional_ writes) ->
if should_report proc_env then report_thread_safety_violations proc_env writes)
if should_report proc_env then report_thread_safety_violations proc_env unconditional_ writes)
tab
tab
(* This is a "cluster checker" *)
(* This is a "cluster checker" *)