[AI][pulse] lists of disjuncts instead of sets

Summary:
The disjunctive domain shouldn't really be a set in the first place as
comparing abstract states for equality is expensive to do naively
(walking the whole maps representing the abstract heap). Moreover in
practice these sets have a small max size (currently 50 for pulse, the
only client), so switching them to plain lists makes sense.

Reviewed By: mbouaziz

Differential Revision: D14258489

fbshipit-source-id: c512169eb
master
Jules Villard 6 years ago committed by Facebook Github Bot
parent 44007f054c
commit a19db6605c

@ -44,13 +44,11 @@ module type DisjReady = sig
module Domain : AbstractDomain.S module Domain : AbstractDomain.S
module DisjunctiveDomain : Caml.Set.S with type elt = Domain.t
type extras type extras
type instr type instr
val exec_instr : Domain.t -> extras ProcData.t -> CFG.Node.t -> instr -> DisjunctiveDomain.t val exec_instr : Domain.t -> extras ProcData.t -> CFG.Node.t -> instr -> Domain.t list
val pp_session_name : CFG.Node.t -> Format.formatter -> unit val pp_session_name : CFG.Node.t -> Format.formatter -> unit
end end
@ -64,41 +62,64 @@ module MakeHILDisjunctive (TransferFunctions : HILDisjReady) (DConfig : Disjunct
type extras = TransferFunctions.extras type extras = TransferFunctions.extras
module Disjuncts = struct
type disjunct = TransferFunctions.Domain.t
let pp_disjunct fmt astate = TransferFunctions.Domain.pp fmt astate
type t = disjunct list
let mk_disjunct astate = astate
let singleton astate = [mk_disjunct astate]
let elements disjuncts = disjuncts
let pp f disjuncts = PrettyPrintable.pp_collection ~pp_item:pp_disjunct f disjuncts
end
module Domain = struct module Domain = struct
module Set = TransferFunctions.DisjunctiveDomain type t = Disjuncts.t
let join_normalize_into s_froms ~into:s_intos =
let s_from_not_in_intos =
List.rev_filter s_froms ~f:(fun s_from ->
List.exists s_intos ~f:(fun s_into -> phys_equal s_from s_into) |> not )
in
List.rev_append s_from_not_in_intos s_intos
let join lhs rhs =
let join : t -> t -> t =
fun lhs rhs ->
if phys_equal lhs rhs then lhs if phys_equal lhs rhs then lhs
else else
let union = Set.union lhs rhs in
match DConfig.join_policy with match DConfig.join_policy with
| `NeverJoin -> | `NeverJoin ->
union rhs @ lhs
| `UnderApproximateAfter n -> | `UnderApproximateAfter n ->
if Set.cardinal union <= n then union else lhs let lhs_length = List.length lhs in
if lhs_length >= n then lhs else join_normalize_into rhs ~into:lhs
let widen ~prev ~next ~num_iters = let rec ( <= ) ~lhs ~rhs =
if phys_equal prev next then prev if phys_equal lhs rhs then (* also takes care of the case [lhs = rhs = []] *) true
else else
let (`UnderApproximateAfterNumIterations max_iter) = DConfig.widen_policy in (* quick check: [lhs <= rhs] if [rhs] is [something @ lhs] *)
if num_iters > max_iter then prev else join prev next match rhs with [] -> false | _ :: rhs' -> ( <= ) ~lhs ~rhs:rhs'
let ( <= ) ~lhs ~rhs = if phys_equal lhs rhs then true else Set.subset lhs rhs
let pp f set = let widen ~prev ~next ~num_iters =
PrettyPrintable.pp_collection ~pp_item:TransferFunctions.Domain.pp f (Set.elements set) let (`UnderApproximateAfterNumIterations max_iter) = DConfig.widen_policy in
if phys_equal prev next then prev else if num_iters > max_iter then prev else join prev next
include Set let pp = Disjuncts.pp
end end
let exec_instr disj_dom extras node instr = let exec_instr pre_disjuncts extras node instr =
Domain.fold List.fold pre_disjuncts ~init:[] ~f:(fun post_disjuncts pre_disjunct ->
(fun dom result -> let disjuncts' = TransferFunctions.exec_instr pre_disjunct extras node instr in
TransferFunctions.exec_instr dom extras node instr |> Domain.Set.union result ) Domain.join post_disjuncts (List.map disjuncts' ~f:Disjuncts.mk_disjunct) )
disj_dom Domain.Set.empty
let pp_session_name node f = TransferFunctions.pp_session_name node f let pp_session_name node f = TransferFunctions.pp_session_name node f

@ -53,13 +53,11 @@ module type DisjReady = sig
module Domain : AbstractDomain.S module Domain : AbstractDomain.S
module DisjunctiveDomain : Caml.Set.S with type elt = Domain.t
type extras type extras
type instr type instr
val exec_instr : Domain.t -> extras ProcData.t -> CFG.Node.t -> instr -> DisjunctiveDomain.t val exec_instr : Domain.t -> extras ProcData.t -> CFG.Node.t -> instr -> Domain.t list
val pp_session_name : CFG.Node.t -> Format.formatter -> unit val pp_session_name : CFG.Node.t -> Format.formatter -> unit
end end
@ -73,9 +71,17 @@ end
disjunct independently. The join on the disjunctive state is governed by the policy described in disjunct independently. The join on the disjunctive state is governed by the policy described in
[DConfig]. *) [DConfig]. *)
module MakeHILDisjunctive (TransferFunctions : HILDisjReady) (DConfig : DisjunctiveConfig) : sig module MakeHILDisjunctive (TransferFunctions : HILDisjReady) (DConfig : DisjunctiveConfig) : sig
module Disjuncts : sig
type t
val singleton : TransferFunctions.Domain.t -> t
val elements : t -> TransferFunctions.Domain.t list [@@warning "-32"]
end
include include
HIL HIL
with type extras = TransferFunctions.extras with type extras = TransferFunctions.extras
and module CFG = TransferFunctions.CFG and module CFG = TransferFunctions.CFG
and type Domain.t = TransferFunctions.DisjunctiveDomain.t and type Domain.t = Disjuncts.t
end end

@ -27,7 +27,6 @@ let check_error summary = function
module PulseTransferFunctions = struct module PulseTransferFunctions = struct
module CFG = ProcCfg.Exceptional module CFG = ProcCfg.Exceptional
module Domain = PulseDomain module Domain = PulseDomain
module DisjunctiveDomain = Caml.Set.Make (Domain)
type extras = Summary.t type extras = Summary.t
@ -138,16 +137,22 @@ module PulseTransferFunctions = struct
= =
match instr with match instr with
| Assign (lhs_access, rhs_exp, loc) -> | Assign (lhs_access, rhs_exp, loc) ->
exec_assign summary lhs_access rhs_exp loc astate let post = exec_assign summary lhs_access rhs_exp loc astate |> check_error summary in
|> check_error summary |> DisjunctiveDomain.singleton [post]
| Assume (condition, _, _, loc) -> | Assume (condition, _, _, loc) ->
let post =
PulseOperations.read_all loc (HilExp.get_access_exprs condition) astate PulseOperations.read_all loc (HilExp.get_access_exprs condition) astate
|> check_error summary |> DisjunctiveDomain.singleton |> check_error summary
in
[post]
| Call (ret, call, actuals, flags, loc) -> | Call (ret, call, actuals, flags, loc) ->
dispatch_call summary ret call actuals flags loc astate let post =
|> check_error summary |> DisjunctiveDomain.singleton dispatch_call summary ret call actuals flags loc astate |> check_error summary
in
[post]
| ExitScope (vars, _) -> | ExitScope (vars, _) ->
PulseOperations.remove_vars vars astate |> DisjunctiveDomain.singleton let post = PulseOperations.remove_vars vars astate in
[post]
let pp_session_name _node fmt = F.pp_print_string fmt "Pulse" let pp_session_name _node fmt = F.pp_print_string fmt "Pulse"
@ -176,6 +181,6 @@ let checker {Callbacks.proc_desc; tenv; summary} =
( try ( try
ignore ignore
(DisjunctiveAnalyzer.compute_post proc_data (DisjunctiveAnalyzer.compute_post proc_data
~initial:(PulseTransferFunctions.DisjunctiveDomain.singleton PulseDomain.initial)) ~initial:(DisjunctiveTransferFunctions.Disjuncts.singleton PulseDomain.initial))
with AbstractDomain.Stop_analysis -> () ) ; with AbstractDomain.Stop_analysis -> () ) ;
summary summary

@ -1,5 +1,5 @@
codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_branch_bad, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete ptr` at line 58 here,accessed `*(ptr)` here] codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_branch_bad, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete ptr` at line 58 here,accessed `*(ptr)` here]
codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_loop_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete ptr` at line 68 here,accessed `ptr` here] codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_loop_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete ptr` at line 70 here,accessed `ptr` here]
codetoanalyze/cpp/pulse/closures.cpp, implicit_ref_capture_destroy_invoke_bad, 6, USE_AFTER_DESTRUCTOR, no_bucket, ERROR, [returned from call to `S_S(&(s),&(0$?%__sil_tmpSIL_materialize_temp__n$12))`,`&(s)` captured as `s`,invalidated by destructor call `S_~S(s)` at line 29 here,accessed `&(f)` here] codetoanalyze/cpp/pulse/closures.cpp, implicit_ref_capture_destroy_invoke_bad, 6, USE_AFTER_DESTRUCTOR, no_bucket, ERROR, [returned from call to `S_S(&(s),&(0$?%__sil_tmpSIL_materialize_temp__n$12))`,`&(s)` captured as `s`,invalidated by destructor call `S_~S(s)` at line 29 here,accessed `&(f)` here]
codetoanalyze/cpp/pulse/closures.cpp, ref_capture_destroy_invoke_bad, 6, USE_AFTER_DESTRUCTOR, no_bucket, ERROR, [returned from call to `S_S(&(s))`,`&(s)` captured as `s`,invalidated by destructor call `S_~S(s)` at line 20 here,accessed `&(f)` here] codetoanalyze/cpp/pulse/closures.cpp, ref_capture_destroy_invoke_bad, 6, USE_AFTER_DESTRUCTOR, no_bucket, ERROR, [returned from call to `S_S(&(s))`,`&(s)` captured as `s`,invalidated by destructor call `S_~S(s)` at line 20 here,accessed `&(f)` here]
codetoanalyze/cpp/pulse/join.cpp, invalidate_node_alias_bad, 12, USE_AFTER_DELETE, no_bucket, ERROR, [assigned to `result`,invalidated by call to `delete result` at line 25 here,accessed `*(result)` here] codetoanalyze/cpp/pulse/join.cpp, invalidate_node_alias_bad, 12, USE_AFTER_DELETE, no_bucket, ERROR, [assigned to `result`,invalidated by call to `delete result` at line 25 here,accessed `*(result)` here]

Loading…
Cancel
Save