[thread-safety] more precise ownership domain

Summary:
We previously lumped ownership predicates in with all other predicates. That limited us to a flat ownership domain.
This diff separates out the ownership predicates so we can have a richer lattice of predicates with each access path.
This lets us be more precise; for example, we can now show that

```
needToOwnBothParams(Obj o1, Obj o2) {
  Obj alias;
  if (*) { alias = o1; } else { alias = o2; }
  alias.f = ... // both o1 and o2 need to be owned for this to be safe
}

void ownBothParamsOk() {
  needToOwnBothParams(new Obj(), new Obj()); // ok, would have complained before
}
```

is safe.

Reviewed By: jberdine

Differential Revision: D5589898

fbshipit-source-id: 9606a46
master
Sam Blackshear 7 years ago committed by Facebook Github Bot
parent d0ee36b3a8
commit 3a7d50e15b

@ -21,10 +21,6 @@ module Summary = Summary.Make (struct
let read_payload (summary: Specs.summary) = summary.payload.threadsafety let read_payload (summary: Specs.summary) = summary.payload.threadsafety
end) end)
let is_owned access_path attribute_map =
ThreadSafetyDomain.AttributeMapDomain.has_attribute access_path
ThreadSafetyDomain.Attribute.unconditionally_owned attribute_map
(*Bit of redundancy with code in is_unprotected, might alter later *) (*Bit of redundancy with code in is_unprotected, might alter later *)
let make_excluder locks threads = let make_excluder locks threads =
if locks && not threads then ThreadSafetyDomain.Excluder.Lock if locks && not threads then ThreadSafetyDomain.Excluder.Lock
@ -179,108 +175,106 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| _ | _
-> None -> None
let add_conditional_ownership_attribute access_path attribute_map attributes = (* propagate attributes from the leaves to the root of an RHS Hil expression *)
match
Domain.AttributeMapDomain.get_conditional_ownership_index (fst access_path, []) attribute_map
with
| Some formal_index when not (is_owned access_path attribute_map)
-> Domain.AttributeSetDomain.add (Domain.Attribute.OwnedIf (Some formal_index)) attributes
| _
-> attributes
let remove_ownership_attributes attributes =
Domain.AttributeSetDomain.filter
(function Domain.Attribute.OwnedIf _ -> false | _ -> true)
attributes
(* propagate attributes from the leaves to the root of an RHS Hil expression.
Special casing on ownership. *)
let rec attributes_of_expr attribute_map e = let rec attributes_of_expr attribute_map e =
let open HilExp in let open HilExp in
let open Domain in let open Domain in
match e with match e with
| HilExp.AccessPath ap -> (
try AttributeMapDomain.find ap attribute_map
with Not_found -> AttributeSetDomain.empty )
| Constant _ | Constant _
-> AttributeSetDomain.of_list [Attribute.unconditionally_owned; Attribute.Functional] -> AttributeSetDomain.of_list [Attribute.Functional]
| AccessPath ap
-> ( try AttributeMapDomain.find ap attribute_map
with Not_found -> AttributeSetDomain.empty )
|> add_conditional_ownership_attribute ap attribute_map
| Exception expr (* treat exceptions as transparent wrt attributes *) | Cast (_, expr) | Exception expr (* treat exceptions as transparent wrt attributes *) | Cast (_, expr)
-> attributes_of_expr attribute_map expr -> attributes_of_expr attribute_map expr
| UnaryOperator (_, expr, _) | UnaryOperator (_, expr, _)
-> attributes_of_expr attribute_map expr |> remove_ownership_attributes -> attributes_of_expr attribute_map expr
| BinaryOperator (_, expr1, expr2) | BinaryOperator (_, expr1, expr2)
-> let attributes1 = attributes_of_expr attribute_map expr1 in -> let attributes1 = attributes_of_expr attribute_map expr1 in
let attributes2 = attributes_of_expr attribute_map expr2 in let attributes2 = attributes_of_expr attribute_map expr2 in
AttributeSetDomain.join attributes1 attributes2 |> remove_ownership_attributes AttributeSetDomain.join attributes1 attributes2
| Closure _ | Sizeof _ | Closure _ | Sizeof _
-> AttributeSetDomain.empty -> AttributeSetDomain.empty
let rec ownership_of_expr expr ownership =
let open Domain in
let open HilExp in
match expr with
| AccessPath ap
-> OwnershipDomain.get_owned ap ownership
| Constant _
-> OwnershipAbstractValue.owned
| Exception e (* treat exceptions as transparent wrt ownership *) | Cast (_, e)
-> ownership_of_expr e ownership
| _
-> OwnershipAbstractValue.unowned
(* will return true on x.f.g.h when x.f and x.f.g are owned, but not requiring x.f.g.h *) (* will return true on x.f.g.h when x.f and x.f.g are owned, but not requiring x.f.g.h *)
(* must not be called with an empty access list *) (* must not be called with an empty access list *)
let all_prefixes_owned (base, accesses) attribute_map = let all_prefixes_owned (base, accesses) attribute_map =
let but_last_rev = List.rev accesses |> List.tl_exn in let but_last_rev = List.rev accesses |> List.tl_exn in
let rec aux acc = function [] -> acc | _ :: tail as all -> aux (List.rev all :: acc) tail in let rec aux acc = function [] -> acc | _ :: tail as all -> aux (List.rev all :: acc) tail in
let prefixes = aux [] but_last_rev in let prefixes = aux [] but_last_rev in
List.for_all ~f:(fun ap -> is_owned (base, ap) attribute_map) prefixes List.for_all
~f:(fun ap -> ThreadSafetyDomain.OwnershipDomain.is_owned (base, ap) attribute_map)
prefixes
let propagate_attributes lhs_access_path rhs_exp attribute_map = let propagate_attributes lhs_access_path rhs_exp attribute_map =
let rhs_attributes = attributes_of_expr attribute_map rhs_exp in let rhs_attributes = attributes_of_expr attribute_map rhs_exp in
let lhs_root = fst lhs_access_path in Domain.AttributeMapDomain.add lhs_access_path rhs_attributes attribute_map
let filter_on_globals =
let propagate_ownership (lhs_root, accesses as lhs_access_path) rhs_exp ownership =
if Var.is_global (fst lhs_root) then
(* do not assign ownership to access paths rooted at globals *) (* do not assign ownership to access paths rooted at globals *)
if Var.is_global (fst lhs_root) then remove_ownership_attributes else Fn.id ownership
in else
let filter_on_lhs = let ownership_value =
(* do not assign ownership when lhs is not a single var or match accesses with
a single-field ap whose root is conditionally owned, or, | []
all prefixes are owned *) -> ownership_of_expr rhs_exp ownership
match snd lhs_access_path with | [_]
| [] when match Domain.OwnershipDomain.get_owned (lhs_root, []) ownership with
-> Fn.id | Domain.OwnershipAbstractValue.OwnedIf _
| [_] -> true
when Option.is_some | _
(Domain.AttributeMapDomain.get_conditional_ownership_index (lhs_root, []) -> false
attribute_map) -> ownership_of_expr rhs_exp ownership
-> Fn.id | _ when all_prefixes_owned lhs_access_path ownership
| _ when all_prefixes_owned lhs_access_path attribute_map -> ownership_of_expr rhs_exp ownership
-> Fn.id | _
| _ -> Domain.OwnershipAbstractValue.unowned
-> remove_ownership_attributes in
in Domain.OwnershipDomain.add lhs_access_path ownership_value ownership
let final_attributes = filter_on_globals rhs_attributes |> filter_on_lhs in
Domain.AttributeMapDomain.add lhs_access_path final_attributes attribute_map
let propagate_return_attributes ret_opt ret_attributes actuals attribute_map = let propagate_return ret_opt ret_ownership ret_attributes actuals
{Domain.ownership; attribute_map} =
let open Domain in let open Domain in
match ret_opt with match ret_opt with
| None | None
-> attribute_map -> (ownership, attribute_map)
| Some ret | Some ret
-> let ownership_attributes, other_attributes = -> let ret_access_path = (ret, []) in
AttributeSetDomain.partition (function OwnedIf _ -> true | _ -> false) ret_attributes let get_ownership formal_index acc =
match List.nth actuals formal_index with
| Some HilExp.AccessPath actual_ap
-> OwnershipDomain.get_owned actual_ap ownership |> OwnershipAbstractValue.join acc
| Some HilExp.Constant _
-> acc
| _
-> OwnershipAbstractValue.unowned
in in
let caller_return_attributes = let ownership' =
match AttributeSetDomain.elements ownership_attributes with match ret_ownership with
| [] | OwnershipAbstractValue.Owned | Unowned
-> other_attributes -> OwnershipDomain.add ret_access_path ret_ownership ownership
| [(OwnedIf None as unconditionally_owned)] | OwnershipAbstractValue.OwnedIf formal_indexes
-> AttributeSetDomain.add unconditionally_owned other_attributes -> let actuals_ownership =
| [(OwnedIf Some formal_index)] -> ( IntSet.fold get_ownership formal_indexes OwnershipAbstractValue.owned
match List.nth actuals formal_index with in
| Some HilExp.AccessPath actual_ap OwnershipDomain.add ret_access_path actuals_ownership ownership
-> if is_owned actual_ap attribute_map then
AttributeSetDomain.add Attribute.unconditionally_owned other_attributes
else add_conditional_ownership_attribute actual_ap attribute_map other_attributes
| Some HilExp.Constant _
-> AttributeSetDomain.add Attribute.unconditionally_owned other_attributes
| _
-> other_attributes )
| _multiple_ownership_attributes
-> (* TODO: handle multiple ownership attributes *)
other_attributes
in in
AttributeMapDomain.add (ret, []) caller_return_attributes attribute_map let attribute_map' = AttributeMapDomain.add ret_access_path ret_attributes attribute_map in
(ownership', attribute_map')
let is_unprotected is_locked is_threaded pdesc = let is_unprotected is_locked is_threaded pdesc =
not is_locked && not is_threaded && not (Procdesc.is_java_synchronized pdesc) not is_locked && not is_threaded && not (Procdesc.is_java_synchronized pdesc)
@ -318,7 +312,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
then then
let open Domain in let open Domain in
let pre = let pre =
if is_unprotected locks threads proc_data.pdesc then AccessPrecondition.unprotected if is_unprotected locks threads proc_data.pdesc then AccessPrecondition.TotallyUnprotected
else else
AccessPrecondition.Protected AccessPrecondition.Protected
(make_excluder (locks || Procdesc.is_java_synchronized proc_data.pdesc) threads) (make_excluder (locks || Procdesc.is_java_synchronized proc_data.pdesc) threads)
@ -326,7 +320,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
AccessDomain.add_access pre (make_unannotated_call_access pname loc) attribute_map AccessDomain.add_access pre (make_unannotated_call_access pname loc) attribute_map
else attribute_map else attribute_map
let add_access exp loc ~is_write_access accesses locks threads attribute_map let add_access exp loc ~is_write_access accesses locks threads ownership
(proc_data: ProcData.no_extras ProcData.t) = (proc_data: ProcData.no_extras ProcData.t) =
let open Domain in let open Domain in
(* we don't want to warn on accesses to the field if it is (a) thread-confined, or (* we don't want to warn on accesses to the field if it is (a) thread-confined, or
@ -352,7 +346,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
-> let is_write = if List.is_empty access_list' then is_write_access else false in -> let is_write = if List.is_empty access_list' then is_write_access else false in
let access_path = (fst prefix_path, snd prefix_path @ [access]) in let access_path = (fst prefix_path, snd prefix_path @ [access]) in
let access_acc' = let access_acc' =
if is_owned prefix_path attribute_map if OwnershipDomain.is_owned prefix_path ownership
|| is_safe_access access prefix_path proc_data.tenv || is_safe_access access prefix_path proc_data.tenv
then access_acc then access_acc
else else
@ -362,17 +356,17 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
in in
let add_access_ acc (base, accesses) = let add_access_ acc (base, accesses) =
let base_access_path = (base, []) in let base_access_path = (base, []) in
if List.is_empty accesses then acc if List.is_empty accesses || OwnershipDomain.is_owned base_access_path ownership then acc
else else
let pre = let pre =
if is_unprotected locks threads proc_data.pdesc then if is_unprotected locks threads proc_data.pdesc then
match match OwnershipDomain.get_owned base_access_path ownership with
AttributeMapDomain.get_conditional_ownership_index base_access_path attribute_map | OwnershipAbstractValue.OwnedIf formal_indexes
with -> AccessPrecondition.Unprotected formal_indexes
| Some formal_index | OwnershipAbstractValue.Owned
-> AccessPrecondition.Unprotected (Some formal_index) -> assert false
| None | OwnershipAbstractValue.Unowned
-> AccessPrecondition.unprotected -> AccessPrecondition.TotallyUnprotected
else else
AccessPrecondition.Protected AccessPrecondition.Protected
(make_excluder (locks || Procdesc.is_java_synchronized proc_data.pdesc) threads) (make_excluder (locks || Procdesc.is_java_synchronized proc_data.pdesc) threads)
@ -497,13 +491,21 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
let container_access = let container_access =
make_container_access receiver_ap ~is_write callee_pname callee_loc make_container_access receiver_ap ~is_write callee_pname callee_loc
in in
AccessDomain.add_access (Unprotected (Some 0)) container_access AccessDomain.empty AccessDomain.add_access (Unprotected (IntSet.singleton 0)) container_access
AccessDomain.empty
in in
(* TODO: for now all formals escape *) (* TODO: for now all formals escape *)
(* we need a more intelligent escape analysis, that branches on whether (* we need a more intelligent escape analysis, that branches on whether
we own the container *) we own the container *)
let escapee_formals = List.length actuals |> List.range 0 |> FormalsDomain.of_list in let escapee_formals = List.length actuals |> List.range 0 |> FormalsDomain.of_list in
Some (true, false, false, callee_accesses, AttributeSetDomain.empty, escapee_formals) Some
( true
, false
, false
, callee_accesses
, OwnershipAbstractValue.unowned
, AttributeSetDomain.empty
, escapee_formals )
let get_summary caller_pdesc callee_pname actuals callee_loc tenv = let get_summary caller_pdesc callee_pname actuals callee_loc tenv =
let get_receiver_ap actuals = let get_receiver_ap actuals =
@ -539,16 +541,15 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| _ | _
-> false -> false
let add_reads exps loc accesses locks threads attribute_map proc_data = let add_reads exps loc accesses locks threads ownership proc_data =
List.fold List.fold
~f:(fun acc exp -> ~f:(fun acc exp ->
add_access exp loc ~is_write_access:false acc locks threads attribute_map proc_data) add_access exp loc ~is_write_access:false acc locks threads ownership proc_data)
exps ~init:accesses exps ~init:accesses
let add_escapees_from_exp rhs_exp attribute_map escapees = let add_escapees_from_exp rhs_exp ownership escapees =
HilExp.get_access_paths rhs_exp HilExp.get_access_paths rhs_exp |> List.rev_map ~f:(Domain.Escapee.of_access_path ownership)
|> List.rev_map ~f:(Domain.Escapee.of_access_path attribute_map) |> List.concat_no_order |> Domain.EscapeeDomain.add_from_list escapees
|> Domain.EscapeeDomain.add_from_list escapees
let add_escapees_from_exp_list exp_list extras escapees = let add_escapees_from_exp_list exp_list extras escapees =
List.fold ~init:escapees exp_list ~f:(fun acc exp -> add_escapees_from_exp exp extras acc) List.fold ~init:escapees exp_list ~f:(fun acc exp -> add_escapees_from_exp exp extras acc)
@ -565,16 +566,15 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
match instr with match instr with
| Call (Some ret_base, Direct procname, actuals, _, loc) when acquires_ownership procname tenv | Call (Some ret_base, Direct procname, actuals, _, loc) when acquires_ownership procname tenv
-> let accesses = -> let accesses =
add_reads actuals loc astate.accesses astate.locks astate.threads astate.attribute_map add_reads actuals loc astate.accesses astate.locks astate.threads astate.ownership
proc_data proc_data
in in
let attribute_map = let ownership =
AttributeMapDomain.add_attribute (ret_base, []) Attribute.unconditionally_owned OwnershipDomain.add (ret_base, []) OwnershipAbstractValue.owned astate.ownership
astate.attribute_map
in in
(* TODO: we need to model escaping formals, now just assume all escape *) (* TODO: we need to model escaping formals, now just assume all escape *)
let escapees = add_escapees_from_exp_list actuals astate.attribute_map astate.escapees in let escapees = add_escapees_from_exp_list actuals astate.ownership astate.escapees in
{astate with accesses; attribute_map; escapees} {astate with accesses; ownership; escapees}
| 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 =
@ -583,7 +583,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
in in
let accesses = let accesses =
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.attribute_map proc_data astate.ownership proc_data
in in
let astate = {astate with accesses} in let astate = {astate with accesses} in
let astate = let astate =
@ -631,6 +631,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
, callee_threads , callee_threads
, callee_locks , callee_locks
, callee_accesses , callee_accesses
, return_ownership
, return_attributes , return_attributes
, escapee_formals ) , escapee_formals )
-> let update_caller_accesses pre callee_accesses caller_accesses = -> let update_caller_accesses pre callee_accesses caller_accesses =
@ -652,35 +653,33 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
-> (* the actual is a constant, so it's owned in the caller. *) -> (* the actual is a constant, so it's owned in the caller. *)
accesses_acc accesses_acc
| HilExp.AccessPath actual_access_path | HilExp.AccessPath actual_access_path
-> if is_owned actual_access_path astate.attribute_map then -> if OwnershipDomain.is_owned actual_access_path astate.ownership then
(* the actual passed to the current callee is owned. drop all the (* the actual passed to the current callee is owned. drop all the
conditional accesses for that actual, since they're all safe *) conditional accesses for that actual, since they're all safe *)
accesses_acc accesses_acc
else else
let pre = let pre =
if unprotected then if unprotected then
let base = fst actual_access_path in let base = fst actual_access_path in
match match OwnershipDomain.get_owned (base, []) astate.ownership with
AttributeMapDomain.get_conditional_ownership_index (base, []) | OwnershipAbstractValue.OwnedIf formal_indexes
astate.attribute_map
with
| Some formal_index
-> (* the actual passed to the current callee is rooted in a -> (* the actual passed to the current callee is rooted in a
formal *) formal *)
AccessPrecondition.Unprotected (Some formal_index) AccessPrecondition.Unprotected formal_indexes
| None -> | OwnershipAbstractValue.Unowned | OwnershipAbstractValue.Owned ->
match match
AttributeMapDomain.get_conditional_ownership_index OwnershipDomain.get_owned actual_access_path astate.ownership
actual_access_path astate.attribute_map
with with
| Some formal_index | OwnershipAbstractValue.OwnedIf formal_indexes
-> (* access path conditionally owned if [formal_index] is -> (* access path conditionally owned if [formal_indexex] are
owned *) owned *)
AccessPrecondition.Unprotected (Some formal_index) AccessPrecondition.Unprotected formal_indexes
| None | OwnershipAbstractValue.Owned
-> (* access path not rooted in a formal and not -> assert false
conditionally owned *) | OwnershipAbstractValue.Unowned
AccessPrecondition.unprotected -> (* access path not rooted in a formal and not conditionally
owned *)
AccessPrecondition.TotallyUnprotected
else else
(* access protected by held lock *) (* access protected by held lock *)
AccessPrecondition.Protected (make_excluder true threads) AccessPrecondition.Protected (make_excluder true threads)
@ -688,39 +687,39 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
update_caller_accesses pre ownership_accesses accesses_acc update_caller_accesses pre ownership_accesses accesses_acc
| _ | _
-> (* couldn't find access path, don't know if it's owned *) -> (* couldn't find access path, don't know if it's owned *)
update_caller_accesses AccessPrecondition.unprotected ownership_accesses update_caller_accesses AccessPrecondition.TotallyUnprotected
accesses_acc ownership_accesses accesses_acc
in in
let accesses = let accesses =
let update_accesses pre callee_accesses accesses_acc = let update_accesses pre callee_accesses accesses_acc =
match pre with match pre with
| AccessPrecondition.Protected _ | AccessPrecondition.Protected _
-> update_caller_accesses pre callee_accesses accesses_acc -> update_caller_accesses pre callee_accesses accesses_acc
| AccessPrecondition.Unprotected None | AccessPrecondition.TotallyUnprotected
-> let pre' = -> let pre' =
if unprotected then pre if unprotected then pre
else AccessPrecondition.Protected (make_excluder true threads) else AccessPrecondition.Protected (make_excluder true threads)
in in
update_caller_accesses pre' callee_accesses accesses_acc update_caller_accesses pre' callee_accesses accesses_acc
| AccessPrecondition.Unprotected Some index | AccessPrecondition.Unprotected formal_indexes
-> add_ownership_access callee_accesses (List.nth_exn actuals index) -> IntSet.fold
accesses_acc (fun index acc ->
add_ownership_access callee_accesses (List.nth_exn actuals index) acc)
formal_indexes accesses_acc
in in
AccessDomain.fold update_accesses callee_accesses astate.accesses AccessDomain.fold update_accesses callee_accesses astate.accesses
in in
let attribute_map = let ownership, attribute_map =
propagate_return_attributes ret_opt return_attributes actuals propagate_return ret_opt return_ownership return_attributes actuals astate
astate.attribute_map
in in
let escapees = let escapees =
add_escapees_from_call actuals escapee_formals astate.attribute_map add_escapees_from_call actuals escapee_formals astate.ownership astate.escapees
astate.escapees
in in
{thumbs_up; locks; threads; accesses; attribute_map; escapees} {thumbs_up; locks; threads; accesses; ownership; attribute_map; escapees}
| None | None
-> (* TODO: assume actuals escape, should we? *) -> (* TODO: assume actuals escape, should we? *)
let new_escapees = let new_escapees =
add_escapees_from_exp_list actuals astate.attribute_map astate.escapees add_escapees_from_exp_list actuals astate.ownership astate.escapees
in in
let astate = {astate with escapees= new_escapees} in let astate = {astate with escapees= new_escapees} in
let should_assume_returns_ownership (call_flags: CallFlags.t) actuals = let should_assume_returns_ownership (call_flags: CallFlags.t) actuals =
@ -744,11 +743,11 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
else if should_assume_returns_ownership call_flags actuals then else if should_assume_returns_ownership call_flags actuals then
match ret_opt with match ret_opt with
| Some ret | Some ret
-> let attribute_map = -> let ownership =
AttributeMapDomain.add_attribute (ret, []) OwnershipDomain.add (ret, []) OwnershipAbstractValue.owned
Attribute.unconditionally_owned astate.attribute_map astate.ownership
in in
{astate with attribute_map} {astate with ownership}
| None | None
-> astate -> astate
else astate else astate
@ -762,16 +761,21 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
in in
let attribute_map = let attribute_map =
add_if_annotated is_functional Functional astate_callee.attribute_map add_if_annotated is_functional Functional astate_callee.attribute_map
|> add_if_annotated (has_return_annot Annotations.ia_is_returns_ownership)
Domain.Attribute.unconditionally_owned
in in
{astate_callee with attribute_map} let ownership =
if PatternMatch.override_exists
(has_return_annot Annotations.ia_is_returns_ownership) tenv callee_pname
then
OwnershipDomain.add (ret, []) OwnershipAbstractValue.owned astate_callee.ownership
else astate_callee.ownership
in
{astate_callee with ownership; attribute_map}
| _ | _
-> astate_callee ) -> astate_callee )
| Assign (lhs_access_path, rhs_exp, loc) | Assign (lhs_access_path, rhs_exp, loc)
-> let rhs_accesses = -> let rhs_accesses =
add_access rhs_exp loc ~is_write_access:false astate.accesses astate.locks astate.threads add_access rhs_exp loc ~is_write_access:false astate.accesses astate.locks astate.threads
astate.attribute_map proc_data astate.ownership proc_data
in in
let rhs_access_paths = HilExp.get_access_paths rhs_exp in let rhs_access_paths = HilExp.get_access_paths rhs_exp in
let is_functional = let is_functional =
@ -797,15 +801,16 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
rhs_accesses rhs_accesses
else else
add_access (AccessPath lhs_access_path) loc ~is_write_access:true rhs_accesses add_access (AccessPath lhs_access_path) loc ~is_write_access:true rhs_accesses
astate.locks astate.threads astate.attribute_map proc_data astate.locks astate.threads astate.ownership proc_data
in 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
(* assigning to the return variable leads to no new escapees *) (* assigning to the return variable leads to no new escapees *)
let escapees = let escapees =
if fst lhs_access_path |> fst |> Var.is_return then astate.escapees if fst lhs_access_path |> fst |> Var.is_return then astate.escapees
else add_escapees_from_exp rhs_exp astate.attribute_map astate.escapees else add_escapees_from_exp rhs_exp astate.ownership astate.escapees
in in
{astate with accesses; attribute_map; escapees} {astate with accesses; ownership; attribute_map; escapees}
| 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
@ -846,7 +851,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
in in
let accesses = let accesses =
add_access assume_exp loc ~is_write_access:false astate.accesses astate.locks add_access assume_exp loc ~is_write_access:false astate.accesses astate.locks
astate.threads astate.attribute_map proc_data astate.threads astate.ownership proc_data
in in
let astate' = let astate' =
match HilExp.get_access_paths assume_exp with match HilExp.get_access_paths assume_exp with
@ -962,12 +967,14 @@ let empty_post =
let initial_thumbs_up = true let initial_thumbs_up = true
and initial_known_on_ui_thread = false and initial_known_on_ui_thread = false
and has_lock = false and has_lock = false
and return_ownership = ThreadSafetyDomain.OwnershipAbstractValue.unowned
and return_attrs = ThreadSafetyDomain.AttributeSetDomain.empty and return_attrs = ThreadSafetyDomain.AttributeSetDomain.empty
and escapee_formals = ThreadSafetyDomain.FormalsDomain.empty in and escapee_formals = ThreadSafetyDomain.FormalsDomain.empty in
( initial_thumbs_up ( initial_thumbs_up
, initial_known_on_ui_thread , initial_known_on_ui_thread
, has_lock , has_lock
, ThreadSafetyDomain.AccessDomain.empty , ThreadSafetyDomain.AccessDomain.empty
, return_ownership
, return_attrs , return_attrs
, escapee_formals ) , escapee_formals )
@ -987,7 +994,7 @@ let analyze_procedure {Callbacks.proc_desc; tenv; summary} =
let add_owned_formal acc formal_index = let add_owned_formal acc formal_index =
match FormalMap.get_formal_base formal_index formal_map with match FormalMap.get_formal_base formal_index formal_map with
| Some base | Some base
-> AttributeMapDomain.add_attribute (base, []) Attribute.unconditionally_owned acc -> OwnershipDomain.add (base, []) OwnershipAbstractValue.owned acc
| None | None
-> acc -> acc
in in
@ -1000,35 +1007,42 @@ let analyze_procedure {Callbacks.proc_desc; tenv; summary} =
else [0] else [0]
(* express that the constructor owns [this] *) (* express that the constructor owns [this] *)
in in
let attribute_map = let ownership = List.fold ~f:add_owned_formal owned_formals ~init:OwnershipDomain.empty in
List.fold ~f:add_owned_formal owned_formals ~init:ThreadSafetyDomain.empty.attribute_map ({ThreadSafetyDomain.empty with ownership; threads}, IdAccessPathMapDomain.empty)
in
({ThreadSafetyDomain.empty with attribute_map; threads}, IdAccessPathMapDomain.empty)
else else
(* add Owned(formal_index) predicates for each formal to indicate that each one is owned if (* add Owned(formal_index) predicates for each formal to indicate that each one is owned if
it is owned in the caller *) it is owned in the caller *)
let add_owned_formal acc (formal, formal_index) = let add_owned_formal acc (formal, formal_index) =
AttributeMapDomain.add_attribute (formal, []) (Attribute.OwnedIf (Some formal_index)) acc OwnershipDomain.add (formal, []) (OwnershipAbstractValue.make_owned_if formal_index) acc
in in
let attribute_map = let ownership =
List.fold ~f:add_owned_formal (FormalMap.get_formals_indexes formal_map) List.fold ~f:add_owned_formal (FormalMap.get_formals_indexes formal_map)
~init:ThreadSafetyDomain.empty.attribute_map ~init:OwnershipDomain.empty
in in
({ThreadSafetyDomain.empty with attribute_map; threads}, IdAccessPathMapDomain.empty) ({ThreadSafetyDomain.empty with ownership; threads}, IdAccessPathMapDomain.empty)
in in
match Analyzer.compute_post proc_data ~initial ~debug:false with match Analyzer.compute_post proc_data ~initial ~debug:false with
| Some ({thumbs_up; threads; locks; accesses; attribute_map; escapees}, _) | Some ({thumbs_up; threads; locks; accesses; ownership; attribute_map; escapees}, _)
-> 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))
(Procdesc.get_ret_type proc_desc) (Procdesc.get_ret_type proc_desc)
in in
let return_ownership = OwnershipDomain.get_owned return_var_ap ownership in
let return_attributes = let return_attributes =
try AttributeMapDomain.find return_var_ap attribute_map try AttributeMapDomain.find return_var_ap attribute_map
with Not_found -> AttributeSetDomain.empty with Not_found -> AttributeSetDomain.empty
in in
let escapee_formals = FormalsDomain.of_escapees escapees in let escapee_formals = FormalsDomain.of_escapees escapees in
let post = (thumbs_up, threads, locks, accesses, return_attributes, escapee_formals) in let post =
( thumbs_up
, threads
, locks
, accesses
, return_ownership
, return_attributes
, escapee_formals )
in
Summary.update_summary post summary Summary.update_summary post summary
| None | None
-> summary ) -> summary )
@ -1127,7 +1141,7 @@ let trace_of_pname orig_sink orig_pdesc callee_pname =
let open ThreadSafetyDomain in let open ThreadSafetyDomain in
let orig_access = PathDomain.Sink.kind orig_sink in let orig_access = PathDomain.Sink.kind orig_sink in
match Summary.read_summary orig_pdesc callee_pname with match Summary.read_summary orig_pdesc callee_pname with
| Some (_, _, _, access_map, _, _) | Some (_, _, _, access_map, _, _, _)
-> get_all_accesses -> get_all_accesses
(fun access -> Int.equal (Access.compare (PathDomain.Sink.kind access) orig_access) 0) (fun access -> Int.equal (Access.compare (PathDomain.Sink.kind access) orig_access) 0)
access_map access_map
@ -1315,13 +1329,15 @@ let report_unsafe_accesses aggregated_access_map =
if is_duplicate_report access pname reported_acc then reported_acc if is_duplicate_report access pname reported_acc then reported_acc
else else
match (TraceElem.kind access, pre) with match (TraceElem.kind access, pre) with
| Access.InterfaceCall _, AccessPrecondition.Unprotected _ | ( Access.InterfaceCall _
, (AccessPrecondition.Unprotected _ | AccessPrecondition.TotallyUnprotected) )
-> (* un-annotated interface call + no lock. warn *) -> (* un-annotated interface call + no lock. warn *)
update_reported access pname reported_acc update_reported access pname 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 reported_acc
| (Access.Write _ | ContainerWrite _), AccessPrecondition.Unprotected _ -> ( | ( (Access.Write _ | ContainerWrite _)
, (AccessPrecondition.Unprotected _ | AccessPrecondition.TotallyUnprotected) ) -> (
match Procdesc.get_proc_name pdesc with match Procdesc.get_proc_name pdesc with
| Java _ | Java _
-> if threaded then reported_acc -> if threaded then reported_acc
@ -1336,12 +1352,13 @@ let report_unsafe_accesses aggregated_access_map =
| (Access.Write _ | ContainerWrite _), AccessPrecondition.Protected _ | (Access.Write _ | ContainerWrite _), AccessPrecondition.Protected _
-> (* protected write, do nothing *) -> (* protected write, do nothing *)
reported_acc reported_acc
| (Access.Read _ | ContainerRead _), AccessPrecondition.Unprotected _ | ( (Access.Read _ | ContainerRead _)
, (AccessPrecondition.Unprotected _ | AccessPrecondition.TotallyUnprotected) )
-> (* unprotected read. report all writes as conflicts for java *) -> (* unprotected read. report all writes as conflicts for java *)
(* for c++ filter out unprotected writes *) (* for c++ filter out unprotected writes *)
let is_cpp_protected_write pre = let is_cpp_protected_write pre =
match pre with match pre with
| AccessPrecondition.Unprotected _ | AccessPrecondition.Unprotected _ | TotallyUnprotected
-> Typ.Procname.is_java pname -> Typ.Procname.is_java pname
| AccessPrecondition.Protected _ | AccessPrecondition.Protected _
-> true -> true
@ -1561,7 +1578,7 @@ let should_filter_access access =
now, our abstraction is an access path like x.f.g whose concretization is the set of memory cells 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 file_env = let make_results_table file_env =
let aggregate_post (_, threaded, _, accesses, _, _) tenv pdesc acc = let aggregate_post (_, threaded, _, accesses, _, _, _) tenv pdesc acc =
let open ThreadSafetyDomain in let open ThreadSafetyDomain in
AccessDomain.fold AccessDomain.fold
(fun pre accesses acc -> (fun pre accesses acc ->

@ -124,20 +124,14 @@ module Choice = struct
end end
module Attribute = struct module Attribute = struct
type t = OwnedIf of int option | Functional | Choice of Choice.t [@@deriving compare] type t = Functional | Choice of Choice.t [@@deriving compare]
let pp fmt = function let pp fmt = function
| OwnedIf None
-> F.fprintf fmt "Owned"
| OwnedIf Some formal_index
-> F.fprintf fmt "Owned if formal %d is owned" formal_index
| Functional | Functional
-> F.fprintf fmt "Functional" -> F.fprintf fmt "Functional"
| Choice choice | Choice choice
-> Choice.pp fmt choice -> Choice.pp fmt choice
let unconditionally_owned = OwnedIf None
module Set = PrettyPrintable.MakePPSet (struct module Set = PrettyPrintable.MakePPSet (struct
type nonrec t = t type nonrec t = t
@ -212,19 +206,13 @@ end
module AttributeMapDomain = struct module AttributeMapDomain = struct
include AbstractDomain.InvertedMap (AccessPath.Map) (AttributeSetDomain) include AbstractDomain.InvertedMap (AccessPath.Map) (AttributeSetDomain)
let add access_path attribute_set t =
if AttributeSetDomain.is_empty attribute_set then t else add access_path attribute_set t
let has_attribute access_path attribute t = let has_attribute access_path attribute t =
try find access_path t |> AttributeSetDomain.mem attribute try find access_path t |> AttributeSetDomain.mem attribute
with Not_found -> false with Not_found -> false
let get_conditional_ownership_index access_path t =
try
let attributes = find access_path t in
List.find_map
~f:(function
| Attribute.OwnedIf (Some _ as formal_index_opt) -> formal_index_opt | _ -> None)
(AttributeSetDomain.elements attributes)
with Not_found -> None
let get_choices access_path t = let get_choices access_path t =
try try
let attributes = find access_path t in let attributes = find access_path t in
@ -255,17 +243,20 @@ module Excluder = struct
end end
module AccessPrecondition = struct module AccessPrecondition = struct
type t = Protected of Excluder.t | Unprotected of int option [@@deriving compare] type t =
| Protected of Excluder.t
let unprotected = Unprotected None | Unprotected of IntSet.t
| TotallyUnprotected
[@@deriving compare]
let pp fmt = function let pp fmt = function
| Protected excl | Protected excl
-> F.fprintf fmt "ProtectedBy(%a)" Excluder.pp excl -> F.fprintf fmt "ProtectedBy(%a)" Excluder.pp excl
| Unprotected Some index | TotallyUnprotected
-> F.fprintf fmt "Unprotected(%d)" index -> F.fprintf fmt "TotallyUnprotected"
| Unprotected None | Unprotected indexes
-> F.fprintf fmt "Unprotected" -> F.fprintf fmt "Unprotected(%a)" (PrettyPrintable.pp_collection ~pp_item:Int.pp)
(IntSet.elements indexes)
end end
module AccessDomain = struct module AccessDomain = struct
@ -293,12 +284,12 @@ module Escapee = struct
| Local loc | Local loc
-> F.fprintf fmt "Local(%a)" Var.pp loc -> F.fprintf fmt "Local(%a)" Var.pp loc
let of_access_path attribute_map (base, _) = let of_access_path ownership (base, _) =
match AttributeMapDomain.get_conditional_ownership_index (base, []) attribute_map with match OwnershipDomain.get_owned (base, []) ownership with
| Some fml | OwnershipAbstractValue.OwnedIf formal_indexes
-> Formal fml -> List.map ~f:(fun formal_index -> Formal formal_index) (IntSet.elements formal_indexes)
| None | _
-> Local (fst base) -> [Local (fst base)]
end end
module EscapeeDomain = struct module EscapeeDomain = struct
@ -323,6 +314,7 @@ type astate =
; threads: ThreadsDomain.astate ; threads: ThreadsDomain.astate
; locks: LocksDomain.astate ; locks: LocksDomain.astate
; accesses: AccessDomain.astate ; accesses: AccessDomain.astate
; ownership: OwnershipDomain.astate
; attribute_map: AttributeMapDomain.astate ; attribute_map: AttributeMapDomain.astate
; escapees: EscapeeDomain.astate } ; escapees: EscapeeDomain.astate }
@ -331,6 +323,7 @@ type summary =
* ThreadsDomain.astate * ThreadsDomain.astate
* LocksDomain.astate * LocksDomain.astate
* AccessDomain.astate * AccessDomain.astate
* OwnershipAbstractValue.astate
* AttributeSetDomain.astate * AttributeSetDomain.astate
* FormalsDomain.astate * FormalsDomain.astate
@ -339,9 +332,10 @@ let empty =
let threads = false in let threads = false in
let locks = false in let locks = false in
let accesses = AccessDomain.empty in let accesses = AccessDomain.empty in
let ownership = OwnershipDomain.empty in
let attribute_map = AccessPath.Map.empty in let attribute_map = AccessPath.Map.empty in
let escapees = EscapeeDomain.empty in let escapees = EscapeeDomain.empty in
{thumbs_up; threads; locks; accesses; attribute_map; escapees} {thumbs_up; threads; locks; accesses; ownership; attribute_map; escapees}
let ( <= ) ~lhs ~rhs = let ( <= ) ~lhs ~rhs =
if phys_equal lhs rhs then true if phys_equal lhs rhs then true
@ -358,9 +352,10 @@ let join astate1 astate2 =
let threads = ThreadsDomain.join astate1.threads astate2.threads in let threads = ThreadsDomain.join astate1.threads astate2.threads in
let locks = LocksDomain.join astate1.locks astate2.locks in let locks = LocksDomain.join astate1.locks astate2.locks in
let accesses = AccessDomain.join astate1.accesses astate2.accesses in let accesses = AccessDomain.join astate1.accesses astate2.accesses in
let ownership = OwnershipDomain.join astate1.ownership astate2.ownership in
let attribute_map = AttributeMapDomain.join astate1.attribute_map astate2.attribute_map in let attribute_map = AttributeMapDomain.join astate1.attribute_map astate2.attribute_map in
let escapees = EscapeeDomain.join astate1.escapees astate2.escapees in let escapees = EscapeeDomain.join astate1.escapees astate2.escapees in
{thumbs_up; threads; locks; accesses; attribute_map; escapees} {thumbs_up; threads; locks; accesses; ownership; attribute_map; escapees}
let widen ~prev ~next ~num_iters = let widen ~prev ~next ~num_iters =
if phys_equal prev next then prev if phys_equal prev next then prev
@ -369,20 +364,24 @@ let widen ~prev ~next ~num_iters =
let threads = ThreadsDomain.widen ~prev:prev.threads ~next:next.threads ~num_iters in let threads = ThreadsDomain.widen ~prev:prev.threads ~next:next.threads ~num_iters in
let locks = LocksDomain.widen ~prev:prev.locks ~next:next.locks ~num_iters in let locks = LocksDomain.widen ~prev:prev.locks ~next:next.locks ~num_iters in
let accesses = AccessDomain.widen ~prev:prev.accesses ~next:next.accesses ~num_iters in let accesses = AccessDomain.widen ~prev:prev.accesses ~next:next.accesses ~num_iters in
let ownership = OwnershipDomain.widen ~prev:prev.ownership ~next:next.ownership ~num_iters in
let attribute_map = let attribute_map =
AttributeMapDomain.widen ~prev:prev.attribute_map ~next:next.attribute_map ~num_iters AttributeMapDomain.widen ~prev:prev.attribute_map ~next:next.attribute_map ~num_iters
in in
let escapees = EscapeeDomain.widen ~prev:prev.escapees ~next:next.escapees ~num_iters in let escapees = EscapeeDomain.widen ~prev:prev.escapees ~next:next.escapees ~num_iters in
{thumbs_up; threads; locks; accesses; attribute_map; escapees} {thumbs_up; threads; locks; accesses; ownership; attribute_map; escapees}
let pp_summary fmt (thumbs_up, threads, locks, accesses, return_attributes, escapee_formals) = let pp_summary fmt
(thumbs_up, threads, locks, accesses, ownership, return_attributes, escapee_formals) =
F.fprintf fmt F.fprintf fmt
"@\nThumbsUp: %a, Threads: %a, Locks: %a @\nAccesses %a @\nReturn Attributes: %a @\nEscapee Formals: %a @\n" "@\nThumbsUp: %a, Threads: %a, Locks: %a @\nAccesses %a @\nOwnership: %a @\nReturn Attributes: %a @\nEscapee Formals: %a @\n"
ThumbsUpDomain.pp thumbs_up ThreadsDomain.pp threads LocksDomain.pp locks AccessDomain.pp ThumbsUpDomain.pp thumbs_up ThreadsDomain.pp threads LocksDomain.pp locks AccessDomain.pp
accesses AttributeSetDomain.pp return_attributes FormalsDomain.pp escapee_formals accesses OwnershipAbstractValue.pp ownership AttributeSetDomain.pp return_attributes
FormalsDomain.pp escapee_formals
let pp fmt {thumbs_up; threads; locks; accesses; attribute_map; escapees} = let pp fmt {thumbs_up; threads; locks; accesses; ownership; attribute_map; escapees} =
F.fprintf fmt F.fprintf fmt
"@\nThumbsUp: %a, Threads: %a, Locks: %a @\nAccesses %a @\nReturn Attributes: %a @\nEscapees: %a @\n" "@\nThumbsUp: %a, Threads: %a, Locks: %a @\nAccesses %a @\n Ownership: %a @\nAttributes: %a @\nEscapees: %a @\n"
ThumbsUpDomain.pp thumbs_up ThreadsDomain.pp threads LocksDomain.pp locks AccessDomain.pp ThumbsUpDomain.pp thumbs_up ThreadsDomain.pp threads LocksDomain.pp locks AccessDomain.pp
accesses AttributeMapDomain.pp attribute_map EscapeeDomain.pp escapees accesses OwnershipDomain.pp ownership AttributeMapDomain.pp attribute_map EscapeeDomain.pp
escapees

@ -87,15 +87,10 @@ end
module Attribute : sig module Attribute : sig
type t = type t =
| OwnedIf of int option
(** owned unconditionally if OwnedIf None, owned when formal at index i is owned otherwise *)
| Functional (** holds a value returned from a callee marked @Functional *) | Functional (** holds a value returned from a callee marked @Functional *)
| Choice of Choice.t (** holds a boolean choice variable *) | Choice of Choice.t (** holds a boolean choice variable *)
[@@deriving compare] [@@deriving compare]
val unconditionally_owned : t
(** alias for OwnedIf None *)
val pp : F.formatter -> t -> unit val pp : F.formatter -> t -> unit
module Set : PrettyPrintable.PPSet with type elt = t module Set : PrettyPrintable.PPSet with type elt = t
@ -106,10 +101,9 @@ module AttributeSetDomain : module type of AbstractDomain.InvertedSet (Attribute
module AttributeMapDomain : sig module AttributeMapDomain : sig
include module type of AbstractDomain.InvertedMap (AccessPath.Map) (AttributeSetDomain) include module type of AbstractDomain.InvertedMap (AccessPath.Map) (AttributeSetDomain)
val has_attribute : AccessPath.t -> Attribute.t -> astate -> bool val add : AccessPath.t -> AttributeSetDomain.astate -> astate -> astate
val get_conditional_ownership_index : AccessPath.t -> astate -> int option val has_attribute : AccessPath.t -> Attribute.t -> astate -> bool
(** get the formal index of the the formal that must own the given access path (if any) *)
val get_choices : AccessPath.t -> astate -> Choice.t list val get_choices : AccessPath.t -> astate -> Choice.t list
(** get the choice attributes associated with the given access path *) (** get the choice attributes associated with the given access path *)
@ -137,15 +131,12 @@ module AccessPrecondition : sig
| Protected of Excluder.t | Protected of Excluder.t
(** access potentially protected for mutual exclusion by (** access potentially protected for mutual exclusion by
lock or thread or both *) lock or thread or both *)
| Unprotected of int option | Unprotected of IntSet.t
(** access rooted in formal at index i. Safe if actual passed at index is owned (i.e., (** access rooted in formal(s) at indexes i. Safe if actuals passed at given indexes are
!owned(i) implies an unsafe access). Unprotected None means the access is unsafe unless a owned (i.e., !owned(i) implies an unsafe access). *)
lock is held in the caller *) | TotallyUnprotected (** access is unsafe unless a lock is held in a caller *)
[@@deriving compare] [@@deriving compare]
val unprotected : t
(** type of an unprotected access *)
val pp : F.formatter -> t -> unit val pp : F.formatter -> t -> unit
end end
@ -167,7 +158,7 @@ module Escapee : sig
val pp : F.formatter -> t -> unit val pp : F.formatter -> t -> unit
val of_access_path : AttributeMapDomain.astate -> AccessPath.t -> t val of_access_path : OwnershipDomain.astate -> AccessPath.t -> t list
end end
(** set of formals or locals that may escape *) (** set of formals or locals that may escape *)
@ -190,6 +181,7 @@ type astate =
; locks: LocksDomain.astate (** boolean that is true if a lock must currently be held *) ; locks: LocksDomain.astate (** boolean that is true if a lock must currently be held *)
; accesses: AccessDomain.astate ; accesses: AccessDomain.astate
(** read and writes accesses performed without ownership permissions *) (** read and writes accesses performed without ownership permissions *)
; ownership: OwnershipDomain.astate (** map of access paths to ownership predicates *)
; attribute_map: AttributeMapDomain.astate ; attribute_map: AttributeMapDomain.astate
(** map of access paths to attributes such as owned, functional, ... *) (** map of access paths to attributes such as owned, functional, ... *)
; escapees: EscapeeDomain.astate (** set of formals and locals that may escape *) } ; escapees: EscapeeDomain.astate (** set of formals and locals that may escape *) }
@ -202,6 +194,7 @@ type summary =
* ThreadsDomain.astate * ThreadsDomain.astate
* LocksDomain.astate * LocksDomain.astate
* AccessDomain.astate * AccessDomain.astate
* OwnershipAbstractValue.astate
* AttributeSetDomain.astate * AttributeSetDomain.astate
* FormalsDomain.astate * FormalsDomain.astate

@ -246,7 +246,7 @@ class Containers {
return list; return list;
} }
public void FP_addToNullListOk() { public void addToNullListOk() {
List list = null; List list = null;
addOrCreateList(list); addOrCreateList(list);
} }

@ -284,7 +284,7 @@ public class Ownership {
} }
// TODO: need to handle multiple ownership attributes in order to get this one // TODO: need to handle multiple ownership attributes in order to get this one
public void FP_ownAndConditionalOwnOk() { public void ownAndConditionalOwnOk() {
Obj owned = new Obj(); Obj owned = new Obj();
Obj shouldBeOwned = returnOwnedOrConditionalOwned(owned); Obj shouldBeOwned = returnOwnedOrConditionalOwned(owned);
shouldBeOwned.f = new Object(); shouldBeOwned.f = new Object();
@ -303,7 +303,7 @@ public class Ownership {
} }
// need to handle multiple ownership attributes in order to get this one // need to handle multiple ownership attributes in order to get this one
public void FP_twoDifferentConditionalOwnsOk() { public void twoDifferentConditionalOwnsOk() {
Obj owned1 = new Obj(); Obj owned1 = new Obj();
Obj owned2 = new Obj(); Obj owned2 = new Obj();
Obj shouldBeOwned = twoDifferentConditionalOwns(owned1, owned2); Obj shouldBeOwned = twoDifferentConditionalOwns(owned1, owned2);
@ -495,10 +495,7 @@ public class Ownership {
alias.f = null; // ok if both o1 and o2 are owned alias.f = null; // ok if both o1 and o2 are owned
} }
// we don't allow multiple owned(formal_index) predicates to be associated with a single access void conditionalAliasOk() {
// path. this means we can't tell that alias is owned in conditionAlias, and so we'll report a
// false positive
void FP_conditionalAliasOk() {
conditionalAlias(new Obj(), new Obj()); conditionalAlias(new Obj(), new Obj());
} }

@ -27,7 +27,6 @@ codetoanalyze/java/threadsafety/Constructors.java, Constructors.<init>(), 1, THR
codetoanalyze/java/threadsafety/Constructors.java, Constructors.<init>(Constructors), 1, THREAD_SAFETY_VIOLATION, [access to `Constructors.field`] codetoanalyze/java/threadsafety/Constructors.java, Constructors.<init>(Constructors), 1, THREAD_SAFETY_VIOLATION, [access to `Constructors.field`]
codetoanalyze/java/threadsafety/Containers.java, boolean Containers.listReadBad(String), 1, THREAD_SAFETY_VIOLATION, [<Beginning of read trace>,Read of container `&this.codetoanalyze.java.checkers.Containers.mList` via call to `contains`,<Beginning of write trace>,Write to container `&this.codetoanalyze.java.checkers.Containers.mList` via call to `set`] codetoanalyze/java/threadsafety/Containers.java, boolean Containers.listReadBad(String), 1, THREAD_SAFETY_VIOLATION, [<Beginning of read trace>,Read of container `&this.codetoanalyze.java.checkers.Containers.mList` via call to `contains`,<Beginning of write trace>,Write to container `&this.codetoanalyze.java.checkers.Containers.mList` via call to `set`]
codetoanalyze/java/threadsafety/Containers.java, int Containers.readSimpleArrayMap(), 1, THREAD_SAFETY_VIOLATION, [<Beginning of read trace>,Read of container `&this.codetoanalyze.java.checkers.Containers.si_map` via call to `get`,<Beginning of write trace>,Write to container `&this.codetoanalyze.java.checkers.Containers.si_map` via call to `put`] codetoanalyze/java/threadsafety/Containers.java, int Containers.readSimpleArrayMap(), 1, THREAD_SAFETY_VIOLATION, [<Beginning of read trace>,Read of container `&this.codetoanalyze.java.checkers.Containers.si_map` via call to `get`,<Beginning of write trace>,Write to container `&this.codetoanalyze.java.checkers.Containers.si_map` via call to `put`]
codetoanalyze/java/threadsafety/Containers.java, void Containers.FP_addToNullListOk(), 2, THREAD_SAFETY_VIOLATION, [call to List Containers.addOrCreateList(List),Write to container `&list` via call to `add`]
codetoanalyze/java/threadsafety/Containers.java, void Containers.addToSimpleArrayMapBad(SimpleArrayMap), 1, THREAD_SAFETY_VIOLATION, [Write to container `&map` via call to `put`] codetoanalyze/java/threadsafety/Containers.java, void Containers.addToSimpleArrayMapBad(SimpleArrayMap), 1, THREAD_SAFETY_VIOLATION, [Write to container `&map` via call to `put`]
codetoanalyze/java/threadsafety/Containers.java, void Containers.addToSparseArrayBad(SparseArray), 1, THREAD_SAFETY_VIOLATION, [Write to container `&sparseArray` via call to `put`] codetoanalyze/java/threadsafety/Containers.java, void Containers.addToSparseArrayBad(SparseArray), 1, THREAD_SAFETY_VIOLATION, [Write to container `&sparseArray` via call to `put`]
codetoanalyze/java/threadsafety/Containers.java, void Containers.addToSparseArrayCompatBad(SparseArrayCompat), 1, THREAD_SAFETY_VIOLATION, [Write to container `&sparseArray` via call to `put`] codetoanalyze/java/threadsafety/Containers.java, void Containers.addToSparseArrayCompatBad(SparseArrayCompat), 1, THREAD_SAFETY_VIOLATION, [Write to container `&sparseArray` via call to `put`]
@ -64,9 +63,6 @@ codetoanalyze/java/threadsafety/Locks.java, void Locks.tryLockNoCheckBad(), 2, T
codetoanalyze/java/threadsafety/Locks.java, void Locks.tryLockWrongBranchBad(), 3, THREAD_SAFETY_VIOLATION, [access to `codetoanalyze.java.checkers.Locks.f`] codetoanalyze/java/threadsafety/Locks.java, void Locks.tryLockWrongBranchBad(), 3, THREAD_SAFETY_VIOLATION, [access to `codetoanalyze.java.checkers.Locks.f`]
codetoanalyze/java/threadsafety/Ownership.java, Ownership.<init>(Obj,Object), 1, THREAD_SAFETY_VIOLATION, [access to `codetoanalyze.java.checkers.Obj.f`] codetoanalyze/java/threadsafety/Ownership.java, Ownership.<init>(Obj,Object), 1, THREAD_SAFETY_VIOLATION, [access to `codetoanalyze.java.checkers.Obj.f`]
codetoanalyze/java/threadsafety/Ownership.java, int Ownership.readGlobalBad(), 1, THREAD_SAFETY_VIOLATION, [<Beginning of read trace>,access to `codetoanalyze.java.checkers.Ownership.global`,<Beginning of write trace>,access to `codetoanalyze.java.checkers.Ownership.global`] codetoanalyze/java/threadsafety/Ownership.java, int Ownership.readGlobalBad(), 1, THREAD_SAFETY_VIOLATION, [<Beginning of read trace>,access to `codetoanalyze.java.checkers.Ownership.global`,<Beginning of write trace>,access to `codetoanalyze.java.checkers.Ownership.global`]
codetoanalyze/java/threadsafety/Ownership.java, void Ownership.FP_conditionalAliasOk(), 1, THREAD_SAFETY_VIOLATION, [call to void Ownership.conditionalAlias(Obj,Obj),access to `codetoanalyze.java.checkers.Obj.f`]
codetoanalyze/java/threadsafety/Ownership.java, void Ownership.FP_ownAndConditionalOwnOk(), 3, THREAD_SAFETY_VIOLATION, [access to `codetoanalyze.java.checkers.Obj.f`]
codetoanalyze/java/threadsafety/Ownership.java, void Ownership.FP_twoDifferentConditionalOwnsOk(), 4, THREAD_SAFETY_VIOLATION, [access to `codetoanalyze.java.checkers.Obj.f`]
codetoanalyze/java/threadsafety/Ownership.java, void Ownership.cantOwnThisBad(), 1, THREAD_SAFETY_VIOLATION, [call to void Ownership.setField(Obj),access to `codetoanalyze.java.checkers.Ownership.field`] codetoanalyze/java/threadsafety/Ownership.java, void Ownership.cantOwnThisBad(), 1, THREAD_SAFETY_VIOLATION, [call to void Ownership.setField(Obj),access to `codetoanalyze.java.checkers.Ownership.field`]
codetoanalyze/java/threadsafety/Ownership.java, void Ownership.castThenCallBad(), 2, THREAD_SAFETY_VIOLATION, [call to void Ownership.castThenCall(Obj),call to void Subclass.doWrite(),access to `codetoanalyze.java.checkers.Obj.f`] codetoanalyze/java/threadsafety/Ownership.java, void Ownership.castThenCallBad(), 2, THREAD_SAFETY_VIOLATION, [call to void Ownership.castThenCall(Obj),call to void Subclass.doWrite(),access to `codetoanalyze.java.checkers.Obj.f`]
codetoanalyze/java/threadsafety/Ownership.java, void Ownership.castThenReturnBad(), 2, THREAD_SAFETY_VIOLATION, [access to `codetoanalyze.java.checkers.Obj.f`] codetoanalyze/java/threadsafety/Ownership.java, void Ownership.castThenReturnBad(), 2, THREAD_SAFETY_VIOLATION, [access to `codetoanalyze.java.checkers.Obj.f`]

Loading…
Cancel
Save