[pulse] refactor initial state creation

Summary:
The creation of the inital state has evolved several times recently,
time for a refactor:
- do not create then pass the initial state around when callees can
  create it instead
- slightly hackish assertions that creating the initial state conditions
  cannot fail to simplify the types (could eventually be moved in
  AbductiveDomain.ml to avoid breaking abstraction layers like this, but
  trivial enough that it doesn't really matter I think)

Reviewed By: skcho

Differential Revision: D29550319

fbshipit-source-id: 63919ae71
master
Jules Villard 3 years ago committed by Facebook GitHub Bot
parent 07cb347912
commit f61425db29

@ -471,25 +471,25 @@ let with_debug_exit_node proc_desc ~f =
~f ~f
let initial tenv proc_desc =
[ ( ContinueProgram (PulseObjectiveCSummary.mk_initial_with_positive_self tenv proc_desc)
, PathContext.initial ) ]
let checker ({InterproceduralAnalysis.tenv; proc_desc; err_log} as analysis_data) = let checker ({InterproceduralAnalysis.tenv; proc_desc; err_log} as analysis_data) =
AbstractValue.State.reset () ; AbstractValue.State.reset () ;
let initial_astate = ExecutionDomain.mk_initial tenv proc_desc in match
let initial_self_positive = DisjunctiveAnalyzer.compute_post analysis_data ~initial:(initial tenv proc_desc) proc_desc
PulseObjectiveCSummary.append_objc_self_positive analysis_data initial_astate with
in
let initial = List.map ~f:(fun initial -> (initial, PathContext.initial)) initial_self_positive in
match DisjunctiveAnalyzer.compute_post analysis_data ~initial proc_desc with
| Some posts -> | Some posts ->
(* forget path contexts, we don't propagate them across functions *) (* forget path contexts, we don't propagate them across functions *)
let posts = List.map ~f:fst posts in let posts = List.map ~f:fst posts in
with_debug_exit_node proc_desc ~f:(fun () -> with_debug_exit_node proc_desc ~f:(fun () ->
let updated_posts = let objc_nil_summary = PulseObjectiveCSummary.mk_nil_messaging_summary tenv proc_desc in
PulseObjectiveCSummary.update_objc_method_posts analysis_data ~initial_astate ~posts
in
let summary = let summary =
PulseSummary.of_posts tenv proc_desc err_log PulseSummary.of_posts tenv proc_desc err_log
(Procdesc.get_exit_node proc_desc |> Procdesc.Node.get_loc) (Procdesc.get_exit_node proc_desc |> Procdesc.Node.get_loc)
updated_posts (Option.to_list objc_nil_summary @ posts)
in in
report_topl_errors proc_desc err_log summary ; report_topl_errors proc_desc err_log summary ;
Some summary ) Some summary )

@ -285,8 +285,7 @@ let call tenv path ~caller_proc_desc ~(callee_data : (Procdesc.t * PulseSummary.
astate_unknown astate_unknown
in in
let result_unknown_nil = let result_unknown_nil =
PulseObjectiveCSummary.mk_objc_method_nil_summary tenv procdesc PulseObjectiveCSummary.mk_nil_messaging_summary tenv procdesc
(ExecutionDomain.mk_initial tenv procdesc)
|> Option.value_map ~default:[] ~f:(fun nil_summary -> |> Option.value_map ~default:[] ~f:(fun nil_summary ->
call_aux tenv path caller_proc_desc call_loc callee_pname ret actuals procdesc call_aux tenv path caller_proc_desc call_loc callee_pname ret actuals procdesc
[nil_summary] astate ) [nil_summary] astate )

@ -32,8 +32,6 @@ type t = AbductiveDomain.t base_t
let continue astate = ContinueProgram astate let continue astate = ContinueProgram astate
let mk_initial tenv pdesc = ContinueProgram (AbductiveDomain.mk_initial tenv pdesc)
let leq ~lhs ~rhs = let leq ~lhs ~rhs =
phys_equal lhs rhs phys_equal lhs rhs
|| ||

@ -35,8 +35,6 @@ include AbstractDomain.Disjunct with type t := t
val continue : AbductiveDomain.t -> t val continue : AbductiveDomain.t -> t
val mk_initial : Tenv.t -> Procdesc.t -> t
val is_unsat_cheap : t -> bool val is_unsat_cheap : t -> bool
(** see {!PulsePathCondition.is_unsat_cheap} *) (** see {!PulsePathCondition.is_unsat_cheap} *)

@ -38,10 +38,11 @@ let init_fields_zero tenv path location ~zero addr typ astate =
(** Constructs summary [{self = 0} {return = self}] when [proc_desc] returns a POD type. This allows (** Constructs summary [{self = 0} {return = self}] when [proc_desc] returns a POD type. This allows
us to connect invalidation with invalid access in the trace *) us to connect invalidation with invalid access in the trace *)
let mk_objc_method_nil_summary_aux tenv proc_desc astate = let mk_nil_messaging_summary_aux tenv proc_desc =
let path = PathContext.initial in let path = PathContext.initial in
let location = Procdesc.get_loc proc_desc in let location = Procdesc.get_loc proc_desc in
let self = mk_objc_self_pvar proc_desc in let self = mk_objc_self_pvar proc_desc in
let astate = AbductiveDomain.mk_initial tenv proc_desc in
(* HACK: we are operating on an "empty" initial state and do not expect to create any alarms (* HACK: we are operating on an "empty" initial state and do not expect to create any alarms
(nothing is Invalid in the initial state) or unsatisfiability (we won't create arithmetic (nothing is Invalid in the initial state) or unsatisfiability (we won't create arithmetic
contradictions) *) contradictions) *)
@ -71,12 +72,13 @@ let mk_objc_method_nil_summary_aux tenv proc_desc astate =
|> assert_ok |> assert_ok
let mk_objc_latent_non_POD_nil_messaging tenv proc_desc astate = let mk_latent_non_POD_nil_messaging tenv proc_desc =
(* same HACK as above *)
let assert_ok = function Ok x -> x | Error _ -> assert false in
let path = PathContext.initial in let path = PathContext.initial in
let location = Procdesc.get_loc proc_desc in let location = Procdesc.get_loc proc_desc in
let self = mk_objc_self_pvar proc_desc in let self = mk_objc_self_pvar proc_desc in
let astate = AbductiveDomain.mk_initial tenv proc_desc in
(* same HACK as above *)
let assert_ok = function Ok x -> x | Error _ -> assert false in
let astate, (self_value, _self_history) = let astate, (self_value, _self_history) =
PulseOperations.eval_deref path location (Lvar self) astate |> assert_ok PulseOperations.eval_deref path location (Lvar self) astate |> assert_ok
in in
@ -94,50 +96,41 @@ let mk_objc_latent_non_POD_nil_messaging tenv proc_desc astate =
; calling_context= [] } ; calling_context= [] }
let mk_objc_method_nil_summary tenv proc_desc initial = let mk_nil_messaging_summary tenv proc_desc =
let proc_name = Procdesc.get_proc_name proc_desc in let proc_name = Procdesc.get_proc_name proc_desc in
match (initial, proc_name) with match proc_name with
| ContinueProgram astate, Procname.ObjC_Cpp {kind= ObjCInstanceMethod} -> | Procname.ObjC_Cpp {kind= ObjCInstanceMethod} ->
if Procdesc.is_ret_type_pod proc_desc then if Procdesc.is_ret_type_pod proc_desc then
(* In ObjC, when a method is called on nil, there is no NPE, (* In ObjC, when a method is called on nil, there is no NPE,
the method is actually not called and the return value is 0/false/nil. the method is actually not called and the return value is 0/false/nil.
We create a nil summary to avoid reporting NPE in this case. We create a nil summary to avoid reporting NPE in this case.
However, there is an exception in the case where the return type is non-POD. However, there is an exception in the case where the return type is non-POD.
In that case it's UB and we want to report an error. *) In that case it's UB and we want to report an error. *)
let astate = mk_objc_method_nil_summary_aux tenv proc_desc astate in let astate = mk_nil_messaging_summary_aux tenv proc_desc in
Some (ContinueProgram astate) Some (ContinueProgram astate)
else else
let summary = mk_objc_latent_non_POD_nil_messaging tenv proc_desc astate in let summary = mk_latent_non_POD_nil_messaging tenv proc_desc in
Some summary Some summary
| ContinueProgram _, _ | _ ->
| ExitProgram _, _
| AbortProgram _, _
| LatentAbortProgram _, _
| LatentInvalidAccess _, _
| ISLLatentMemoryError _, _ ->
None None
let append_objc_self_positive {InterproceduralAnalysis.tenv; proc_desc; err_log} astate = let mk_initial_with_positive_self tenv proc_desc =
let location = Procdesc.get_loc proc_desc in let location = Procdesc.get_loc proc_desc in
let self = mk_objc_self_pvar proc_desc in let self = mk_objc_self_pvar proc_desc in
let proc_name = Procdesc.get_proc_name proc_desc in let proc_name = Procdesc.get_proc_name proc_desc in
match (astate, proc_name) with let initial_astate = AbductiveDomain.mk_initial tenv proc_desc in
| ContinueProgram astate, Procname.ObjC_Cpp {kind= ObjCInstanceMethod} -> (* same HACK as above *)
let result = let assert_ok = function Ok x -> x | Error _ -> assert false in
let* astate, value = match proc_name with
PulseOperations.eval_deref PathContext.initial location (Lvar self) astate | Procname.ObjC_Cpp {kind= ObjCInstanceMethod} ->
in let astate, value =
PulseArithmetic.and_positive (fst value) astate PulseOperations.eval_deref PathContext.initial location (Lvar self) initial_astate
|> assert_ok
in in
PulseReport.report_result tenv proc_desc err_log location result PulseArithmetic.and_positive (fst value) astate |> assert_ok
| ContinueProgram _, _ | _ ->
| ExitProgram _, _ initial_astate
| AbortProgram _, _
| LatentAbortProgram _, _
| LatentInvalidAccess _, _
| ISLLatentMemoryError _, _ ->
[astate]
let append_objc_actual_self_positive procdesc self_actual astate = let append_objc_actual_self_positive procdesc self_actual astate =
@ -148,11 +141,3 @@ let append_objc_actual_self_positive procdesc self_actual astate =
PulseArithmetic.prune_positive self astate ) PulseArithmetic.prune_positive self astate )
| _ -> | _ ->
Ok astate Ok astate
let update_objc_method_posts {InterproceduralAnalysis.tenv; proc_desc} ~initial_astate ~posts =
match mk_objc_method_nil_summary tenv proc_desc initial_astate with
| None ->
posts
| Some nil_summary ->
nil_summary :: posts

@ -9,24 +9,14 @@ open! IStd
open PulseBasicInterface open PulseBasicInterface
open PulseDomainInterface open PulseDomainInterface
val update_objc_method_posts :
PulseSummary.t InterproceduralAnalysis.t
-> initial_astate:ExecutionDomain.t
-> posts:ExecutionDomain.t list
-> ExecutionDomain.t list
(** For ObjC instance methods: appends additional nil summary and replaces must be valid reason for
non-pod return type. Does nothing to posts for other kinds of methods *)
val append_objc_actual_self_positive : val append_objc_actual_self_positive :
Procdesc.t Procdesc.t
-> ((AbstractValue.t * ValueHistory.t) * Typ.t) option -> ((AbstractValue.t * ValueHistory.t) * Typ.t) option
-> AbductiveDomain.t -> AbductiveDomain.t
-> AbductiveDomain.t AccessResult.t -> AbductiveDomain.t AccessResult.t
val append_objc_self_positive : val mk_initial_with_positive_self : Tenv.t -> Procdesc.t -> AbductiveDomain.t
PulseSummary.t InterproceduralAnalysis.t -> ExecutionDomain.t -> ExecutionDomain.t list (** The initial state of the analysis, with the additional path condition [self > 0] for Objective-C
(** For ObjC instance methods: adds path condition `self > 0` to a given post. Does nothing to posts instance methods. *)
for other kinds of methods *)
val mk_objc_method_nil_summary : val mk_nil_messaging_summary : Tenv.t -> Procdesc.t -> ExecutionDomain.t option
Tenv.t -> Procdesc.t -> ExecutionDomain.t -> ExecutionDomain.t option

@ -8,13 +8,7 @@
open! IStd open! IStd
open PulseDomainInterface open PulseDomainInterface
val report_result : [@@@warning "-32"]
Tenv.t
-> Procdesc.t
-> Errlog.t
-> Location.t
-> AbductiveDomain.t AccessResult.t
-> ExecutionDomain.t list
val report_summary_error : val report_summary_error :
Tenv.t Tenv.t
@ -23,6 +17,14 @@ val report_summary_error :
-> AbductiveDomain.summary AccessResult.error -> AbductiveDomain.summary AccessResult.error
-> _ ExecutionDomain.base_t -> _ ExecutionDomain.base_t
val report_result :
Tenv.t
-> Procdesc.t
-> Errlog.t
-> Location.t
-> AbductiveDomain.t AccessResult.t
-> ExecutionDomain.t list
val report_results : val report_results :
Tenv.t Tenv.t
-> Procdesc.t -> Procdesc.t

Loading…
Cancel
Save