[pulse] do not short-circuit valid paths because of errors

Summary:
Change most `t list access_result` to `t access_result list` so that the
Ok/Error is individual to each result in the list instead of having only
a toplevel Ok/Error affecting the whole list.

To make it not horrible to write this introduces new "monadic" operators
`let<*>` and `let<+>`. They are not entirely satisfactory but perhaps
it's just a notation issue as they are not quite bind/map operators
unlike what their notation might suggest. I'd say good enough for now.

The type change induced quite the churn but the new operators simplify
the code overall.

Reviewed By: skcho

Differential Revision: D26150505

fbshipit-source-id: 33764fae3
master
Jules Villard 4 years ago committed by Facebook GitHub Bot
parent 5112fd2f32
commit 1f6b9edacc

@ -8,10 +8,10 @@
open! IStd open! IStd
module F = Format module F = Format
module L = Logging module L = Logging
open IResult.Let_syntax
module IRAttributes = Attributes module IRAttributes = Attributes
open PulseBasicInterface open PulseBasicInterface
open PulseDomainInterface open PulseDomainInterface
open PulseOperations.Import
let exec_list_of_list_result = function let exec_list_of_list_result = function
| Ok posts -> | Ok posts ->
@ -24,17 +24,27 @@ let exec_list_of_list_result = function
let report_on_error {InterproceduralAnalysis.proc_desc; tenv; err_log} result = let report_on_error {InterproceduralAnalysis.proc_desc; tenv; err_log} result =
PulseReport.report_error proc_desc tenv err_log result PulseReport.report_error proc_desc tenv err_log result
>>| List.map ~f:(fun post -> ExecutionDomain.ContinueProgram post) >>| List.map ~f:(fun post -> ContinueProgram post)
|> exec_list_of_list_result |> exec_list_of_list_result
let report_on_error_list {InterproceduralAnalysis.proc_desc; tenv; err_log} result = let post_of_report_result = function
PulseReport.report_error proc_desc tenv err_log result |> exec_list_of_list_result | Ok post ->
Some post
| Error Unsat ->
None
| Error (Sat post) ->
Some post
let report_on_error_list {InterproceduralAnalysis.proc_desc; tenv; err_log} results =
List.filter_map results ~f:(fun exec_result ->
PulseReport.report_error proc_desc tenv err_log exec_result |> post_of_report_result )
let report_topl_errors proc_desc err_log summary = let report_topl_errors proc_desc err_log summary =
let f = function let f = function
| ExecutionDomain.ContinueProgram astate -> | ContinueProgram astate ->
PulseTopl.report_errors proc_desc err_log (AbductiveDomain.Topl.get astate) PulseTopl.report_errors proc_desc err_log (AbductiveDomain.Topl.get astate)
| _ -> | _ ->
() ()
@ -60,24 +70,24 @@ module PulseTransferFunctions = struct
AnalysisCallbacks.proc_resolve_attributes pname |> Option.map ~f:ProcAttributes.get_pvar_formals AnalysisCallbacks.proc_resolve_attributes pname |> Option.map ~f:ProcAttributes.get_pvar_formals
let interprocedural_call {InterproceduralAnalysis.analyze_dependency; proc_desc; tenv; err_log} let interprocedural_call {InterproceduralAnalysis.analyze_dependency; proc_desc} ret callee_pname
ret callee_pname call_exp actuals call_loc astate = call_exp actuals call_loc astate =
match callee_pname with match callee_pname with
| Some callee_pname when not Config.pulse_intraprocedural_only -> | Some callee_pname when not Config.pulse_intraprocedural_only ->
let formals_opt = get_pvar_formals callee_pname in let formals_opt = get_pvar_formals callee_pname in
let callee_data = analyze_dependency callee_pname in let callee_data = analyze_dependency callee_pname in
PulseOperations.call ~caller_proc_desc:proc_desc tenv err_log ~callee_data call_loc PulseOperations.call ~caller_proc_desc:proc_desc ~callee_data call_loc callee_pname ~ret
callee_pname ~ret ~actuals ~formals_opt astate ~actuals ~formals_opt astate
| _ -> | _ ->
L.d_printfln "Skipping indirect call %a@\n" Exp.pp call_exp ; L.d_printfln "Skipping indirect call %a@\n" Exp.pp call_exp ;
let astate = let astate =
let arg_values = List.map actuals ~f:(fun ((value, _), _) -> value) in let arg_values = List.map actuals ~f:(fun ((value, _), _) -> value) in
PulseOperations.conservatively_initialize_args arg_values astate PulseOperations.conservatively_initialize_args arg_values astate
in in
Ok [ Ok
[ Domain.ContinueProgram (ContinueProgram
(PulseOperations.unknown_call call_loc (SkippedUnknownCall call_exp) ~ret ~actuals (PulseOperations.unknown_call call_loc (SkippedUnknownCall call_exp) ~ret ~actuals
~formals_opt:None astate) ] ~formals_opt:None astate)) ]
(** has an object just gone out of scope? *) (** has an object just gone out of scope? *)
@ -126,7 +136,7 @@ module PulseTransferFunctions = struct
| ISLLatentMemoryError _ | AbortProgram _ | LatentAbortProgram _ | ExitProgram _ -> | ISLLatentMemoryError _ | AbortProgram _ | LatentAbortProgram _ | ExitProgram _ ->
exec_state exec_state
in in
Result.map ~f:(List.map ~f:do_one_exec_state) exec_state_res List.map ~f:(Result.map ~f:do_one_exec_state) exec_state_res
let topl_store_step loc ~lhs ~rhs:_ astate = let topl_store_step loc ~lhs ~rhs:_ astate =
@ -145,7 +155,7 @@ module PulseTransferFunctions = struct
let dispatch_call ({InterproceduralAnalysis.proc_desc; tenv} as analysis_data) ret call_exp let dispatch_call ({InterproceduralAnalysis.proc_desc; tenv} as analysis_data) ret call_exp
actuals call_loc flags astate = actuals call_loc flags astate =
(* evaluate all actuals *) (* evaluate all actuals *)
let* astate, rev_func_args = let<*> astate, rev_func_args =
List.fold_result actuals ~init:(astate, []) List.fold_result actuals ~init:(astate, [])
~f:(fun (astate, rev_func_args) (actual_exp, actual_typ) -> ~f:(fun (astate, rev_func_args) (actual_exp, actual_typ) ->
let+ astate, actual_evaled = PulseOperations.eval Read call_loc actual_exp astate in let+ astate, actual_evaled = PulseOperations.eval Read call_loc actual_exp astate in
@ -166,7 +176,7 @@ module PulseTransferFunctions = struct
None None
in in
(* do interprocedural call then destroy objects going out of scope *) (* do interprocedural call then destroy objects going out of scope *)
let exec_state_res = let exec_states_res =
match model with match model with
| Some (model, callee_procname) -> | Some (model, callee_procname) ->
L.d_printfln "Found model for call@\n" ; L.d_printfln "Found model for call@\n" ;
@ -191,29 +201,33 @@ module PulseTransferFunctions = struct
PerfEvent.(log (fun logger -> log_end_event logger ())) ; PerfEvent.(log (fun logger -> log_end_event logger ())) ;
r r
in in
let exec_state_res = let exec_states_res =
if Topl.is_deep_active () then if Topl.is_deep_active () then
match callee_pname with match callee_pname with
| Some callee_pname -> | Some callee_pname ->
topl_small_step call_loc callee_pname func_args ret exec_state_res topl_small_step call_loc callee_pname func_args ret exec_states_res
| None -> | None ->
(* skip, as above for non-topl *) exec_state_res (* skip, as above for non-topl *) exec_states_res
else exec_state_res else exec_states_res
in in
let+ exec_state = let exec_states_res =
match get_out_of_scope_object callee_pname actuals flags with match get_out_of_scope_object callee_pname actuals flags with
| Some pvar_typ -> | Some pvar_typ ->
L.d_printfln "%a is going out of scope" Pvar.pp_value (fst pvar_typ) ; L.d_printfln "%a is going out of scope" Pvar.pp_value (fst pvar_typ) ;
let* exec_states = exec_state_res in List.map exec_states_res ~f:(fun exec_state ->
List.map exec_states ~f:(fun exec_state -> exec_state >>= exec_object_out_of_scope call_loc pvar_typ )
exec_object_out_of_scope call_loc pvar_typ exec_state )
|> Result.all
| None -> | None ->
exec_state_res exec_states_res
in in
if Option.exists callee_pname ~f:IRAttributes.is_no_return then if Option.exists callee_pname ~f:IRAttributes.is_no_return then
List.filter_map exec_states_res ~f:(fun exec_state_res ->
match exec_state_res with
| Error _ as err ->
Some err
| Ok exec_state ->
ExecutionDomain.force_exit_program proc_desc exec_state ExecutionDomain.force_exit_program proc_desc exec_state
else exec_state |> Option.map ~f:(fun exec_state -> Ok exec_state) )
else exec_states_res
(* [get_dealloc_from_dynamic_types vars_types loc] returns a dealloc procname and vars and (* [get_dealloc_from_dynamic_types vars_types loc] returns a dealloc procname and vars and
@ -271,7 +285,7 @@ module PulseTransferFunctions = struct
L.d_printfln ~color:Pp.Orange L.d_printfln ~color:Pp.Orange
"Executing injected call to dealloc for vars (%a) that are exiting the scope@." "Executing injected call to dealloc for vars (%a) that are exiting the scope@."
(Pp.seq ~sep:"," Var.pp) vars ; (Pp.seq ~sep:"," Var.pp) vars ;
let astates = List.fold ~f:call_dealloc dealloc_data ~init:[Domain.ContinueProgram astate] in let astates = List.fold ~f:call_dealloc dealloc_data ~init:[ContinueProgram astate] in
(astates, ret_vars) (astates, ret_vars)
@ -362,13 +376,13 @@ module PulseTransferFunctions = struct
in in
report_on_error analysis_data result report_on_error analysis_data result
| Prune (condition, loc, _is_then_branch, _if_kind) -> | Prune (condition, loc, _is_then_branch, _if_kind) ->
(let+ astate = PulseOperations.prune loc ~condition astate in (let<*> astate = PulseOperations.prune loc ~condition astate in
if PulseArithmetic.is_unsat_cheap astate then if PulseArithmetic.is_unsat_cheap astate then
(* [condition] is known to be unsatisfiable: prune path *) (* [condition] is known to be unsatisfiable: prune path *)
[] []
else else
(* [condition] is true or unknown value: go into the branch *) (* [condition] is true or unknown value: go into the branch *)
[Domain.ContinueProgram astate]) [Ok (ContinueProgram astate)])
|> report_on_error_list analysis_data |> report_on_error_list analysis_data
| Call (ret, call_exp, actuals, loc, call_flags) -> | Call (ret, call_exp, actuals, loc, call_flags) ->
dispatch_call analysis_data ret call_exp actuals loc call_flags astate dispatch_call analysis_data ret call_exp actuals loc call_flags astate
@ -393,7 +407,7 @@ module PulseTransferFunctions = struct
~init:[] astates ~init:[] astates
in in
if Procname.is_java (Procdesc.get_proc_name proc_desc) then if Procname.is_java (Procdesc.get_proc_name proc_desc) then
remove_vars vars [Domain.ContinueProgram astate] remove_vars vars [ContinueProgram astate]
else else
(* Here we add and execute calls to dealloc for Objective-C objects (* Here we add and execute calls to dealloc for Objective-C objects
before removing the variables *) before removing the variables *)
@ -411,7 +425,7 @@ module PulseTransferFunctions = struct
| Metadata (VariableLifetimeBegins (pvar, typ, location)) when not (Pvar.is_global pvar) -> | Metadata (VariableLifetimeBegins (pvar, typ, location)) when not (Pvar.is_global pvar) ->
[PulseOperations.realloc_pvar pvar typ location astate |> Domain.continue] [PulseOperations.realloc_pvar pvar typ location astate |> Domain.continue]
| Metadata (Abstract _ | VariableLifetimeBegins _ | Nullify _ | Skip) -> | Metadata (Abstract _ | VariableLifetimeBegins _ | Nullify _ | Skip) ->
[Domain.ContinueProgram astate] ) [ContinueProgram astate] )
let exec_instr astate analysis_data cfg_node instr = let exec_instr astate analysis_data cfg_node instr =

@ -78,12 +78,9 @@ let is_unsat_cheap exec_state = PathCondition.is_unsat_cheap (get_astate exec_st
type summary = AbductiveDomain.summary base_t [@@deriving compare, equal, yojson_of] type summary = AbductiveDomain.summary base_t [@@deriving compare, equal, yojson_of]
let summary_of_posts_common ~continue_program pdesc posts = let summary_of_post_common ~continue_program proc_desc = function
List.filter_mapi posts ~f:(fun i exec_state ->
L.d_printfln "Creating spec out of state #%d:@\n%a" i pp exec_state ;
match exec_state with
| ContinueProgram astate | ISLLatentMemoryError astate -> ( | ContinueProgram astate | ISLLatentMemoryError astate -> (
match AbductiveDomain.summary_of_post pdesc astate with match AbductiveDomain.summary_of_post proc_desc astate with
| Unsat -> | Unsat ->
None None
| Sat astate -> | Sat astate ->
@ -94,17 +91,19 @@ let summary_of_posts_common ~continue_program pdesc posts =
| ExitProgram astate -> | ExitProgram astate ->
Some (ExitProgram astate) Some (ExitProgram astate)
| LatentAbortProgram {astate; latent_issue} -> | LatentAbortProgram {astate; latent_issue} ->
Some (LatentAbortProgram {astate; latent_issue}) ) Some (LatentAbortProgram {astate; latent_issue})
let summary_of_posts = let summary_of_posts proc_desc posts =
summary_of_posts_common ~continue_program:(fun astate -> List.filter_mapi posts ~f:(fun i exec_state ->
L.d_printfln "Creating spec out of state #%d:@\n%a" i pp exec_state ;
summary_of_post_common proc_desc exec_state ~continue_program:(fun astate ->
match (astate :> AbductiveDomain.t).isl_status with match (astate :> AbductiveDomain.t).isl_status with
| ISLOk -> | ISLOk ->
ContinueProgram astate ContinueProgram astate
| ISLError -> | ISLError ->
ISLLatentMemoryError astate ) ISLLatentMemoryError astate ) )
let force_exit_program = let force_exit_program proc_desc post =
summary_of_posts_common ~continue_program:(fun astate -> ExitProgram astate) summary_of_post_common proc_desc post ~continue_program:(fun astate -> ExitProgram astate)

@ -36,4 +36,4 @@ type summary = AbductiveDomain.summary base_t [@@deriving compare, equal, yojson
val summary_of_posts : Procdesc.t -> t list -> summary list val summary_of_posts : Procdesc.t -> t list -> summary list
val force_exit_program : Procdesc.t -> t list -> t list val force_exit_program : Procdesc.t -> t -> t option

@ -6,9 +6,9 @@
*) *)
open! IStd open! IStd
open IResult.Let_syntax
open PulseBasicInterface open PulseBasicInterface
open PulseDomainInterface open PulseDomainInterface
open PulseOperations.Import
type arg_payload = AbstractValue.t * ValueHistory.t type arg_payload = AbstractValue.t * ValueHistory.t
@ -18,16 +18,16 @@ type model =
-> Location.t -> Location.t
-> ret:Ident.t * Typ.t -> ret:Ident.t * Typ.t
-> AbductiveDomain.t -> AbductiveDomain.t
-> ExecutionDomain.t list PulseOperations.access_result -> ExecutionDomain.t PulseOperations.access_result list
let ok_continue post = Ok [ExecutionDomain.ContinueProgram post] let ok_continue post = [Ok (ContinueProgram post)]
let cpp_model_namespace = QualifiedCppName.of_list ["__infer_pulse_model"] let cpp_model_namespace = QualifiedCppName.of_list ["__infer_pulse_model"]
module Misc = struct module Misc = struct
let shallow_copy_value location event ret_id dest_pointer_hist src_value_hist astate = let shallow_copy_value location event ret_id dest_pointer_hist src_value_hist astate =
let* astate, obj_copy = PulseOperations.shallow_copy location src_value_hist astate in let<*> astate, obj_copy = PulseOperations.shallow_copy location src_value_hist astate in
let+ astate = let<+> astate =
PulseOperations.write_deref location ~ref:dest_pointer_hist PulseOperations.write_deref location ~ref:dest_pointer_hist
~obj:(fst obj_copy, event :: snd obj_copy) ~obj:(fst obj_copy, event :: snd obj_copy)
astate astate
@ -36,7 +36,7 @@ module Misc = struct
let shallow_copy location event ret_id dest_pointer_hist src_pointer_hist astate = let shallow_copy location event ret_id dest_pointer_hist src_pointer_hist astate =
let* astate, obj = let<*> astate, obj =
PulseOperations.eval_access Read location src_pointer_hist Dereference astate PulseOperations.eval_access Read location src_pointer_hist Dereference astate
in in
shallow_copy_value location event ret_id dest_pointer_hist obj astate shallow_copy_value location event ret_id dest_pointer_hist obj astate
@ -45,17 +45,16 @@ module Misc = struct
let shallow_copy_model model_desc dest_pointer_hist src_pointer_hist : model = let shallow_copy_model model_desc dest_pointer_hist src_pointer_hist : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model model_desc; location; in_call= []} in let event = ValueHistory.Call {f= Model model_desc; location; in_call= []} in
let+ astate = shallow_copy location event ret_id dest_pointer_hist src_pointer_hist astate in shallow_copy location event ret_id dest_pointer_hist src_pointer_hist astate
[ExecutionDomain.ContinueProgram astate]
let early_exit : model = let early_exit : model =
fun {proc_desc} ~callee_procname:_ _ ~ret:_ astate -> fun {proc_desc} ~callee_procname:_ _ ~ret:_ astate ->
match AbductiveDomain.summary_of_post proc_desc astate with match AbductiveDomain.summary_of_post proc_desc astate with
| Unsat -> | Unsat ->
Ok [] []
| Sat astate -> | Sat astate ->
Ok [ExecutionDomain.ExitProgram astate] [Ok (ExitProgram astate)]
let return_int : Int64.t -> model = let return_int : Int64.t -> model =
@ -96,10 +95,8 @@ module Misc = struct
AnalysisCallbacks.proc_resolve_attributes callee_procname AnalysisCallbacks.proc_resolve_attributes callee_procname
|> Option.map ~f:ProcAttributes.get_pvar_formals |> Option.map ~f:ProcAttributes.get_pvar_formals
in in
Ok PulseOperations.unknown_call location (Model skip_reason) ~ret ~actuals ~formals_opt astate
[ ExecutionDomain.ContinueProgram |> ok_continue
(PulseOperations.unknown_call location (Model skip_reason) ~ret ~actuals ~formals_opt
astate) ]
(** don't actually do nothing, apply the heuristics for unknown calls (this may or may not be a (** don't actually do nothing, apply the heuristics for unknown calls (this may or may not be a
@ -129,13 +126,14 @@ module Misc = struct
match operation with `Free -> Invalidation.CFree | `Delete -> Invalidation.CppDelete match operation with `Free -> Invalidation.CFree | `Delete -> Invalidation.CppDelete
in in
if Config.pulse_isl then if Config.pulse_isl then
let+ astates = match PulseOperations.invalidate_biad_isl location invalidation deleted_access astate with
PulseOperations.invalidate_biad_isl location invalidation deleted_access astate | Error _ as err ->
in [err]
List.map astates ~f:(fun astate -> ExecutionDomain.ContinueProgram astate) | Ok astates ->
List.map astates ~f:(fun astate -> Ok (ContinueProgram astate))
else else
let+ astate = PulseOperations.invalidate location invalidation deleted_access astate in let<+> astate = PulseOperations.invalidate location invalidation deleted_access astate in
[ExecutionDomain.ContinueProgram astate] astate
end end
module C = struct module C = struct
@ -160,11 +158,14 @@ module C = struct
|> PulseOperations.allocate callee_procname location ret_value |> PulseOperations.allocate callee_procname location ret_value
|> set_uninitialized size_exp_opt location ret_addr |> set_uninitialized size_exp_opt location ret_addr
in in
let result_null =
let+ astate_null = let+ astate_null =
PulseArithmetic.and_eq_int ret_addr IntLit.zero astate PulseArithmetic.and_eq_int ret_addr IntLit.zero astate
|> PulseOperations.invalidate location (ConstantDereference IntLit.zero) ret_value |> PulseOperations.invalidate location (ConstantDereference IntLit.zero) ret_value
in in
[ExecutionDomain.ContinueProgram astate_alloc; ExecutionDomain.ContinueProgram astate_null] ContinueProgram astate_null
in
[Ok (ContinueProgram astate_alloc); result_null]
let malloc_not_null_common ~size_exp_opt : model = let malloc_not_null_common ~size_exp_opt : model =
@ -217,19 +218,19 @@ module ObjC = struct
let dispatch_sync args : model = let dispatch_sync args : model =
fun {analyze_dependency; proc_desc; tenv; err_log} ~callee_procname:_ location ~ret astate -> fun {analyze_dependency; proc_desc} ~callee_procname:_ location ~ret astate ->
match List.last args with match List.last args with
| None -> | None ->
Ok [ExecutionDomain.ContinueProgram astate] ok_continue astate
| Some {ProcnameDispatcher.Call.FuncArg.arg_payload= lambda_ptr_hist} -> ( | Some {ProcnameDispatcher.Call.FuncArg.arg_payload= lambda_ptr_hist} -> (
let* astate, (lambda, _) = let<*> astate, (lambda, _) =
PulseOperations.eval_access Read location lambda_ptr_hist Dereference astate PulseOperations.eval_access Read location lambda_ptr_hist Dereference astate
in in
match AddressAttributes.get_closure_proc_name lambda astate with match AddressAttributes.get_closure_proc_name lambda astate with
| None -> | None ->
Ok [ExecutionDomain.ContinueProgram astate] ok_continue astate
| Some callee_proc_name -> | Some callee_proc_name ->
PulseOperations.call ~caller_proc_desc:proc_desc tenv err_log PulseOperations.call ~caller_proc_desc:proc_desc
~callee_data:(analyze_dependency callee_proc_name) ~callee_data:(analyze_dependency callee_proc_name)
location callee_proc_name ~ret ~actuals:[] ~formals_opt:None astate ) location callee_proc_name ~ret ~actuals:[] ~formals_opt:None astate )
end end
@ -262,41 +263,40 @@ module Optional = struct
let assign_none this ~desc : model = let assign_none this ~desc : model =
fun _ ~callee_procname:_ location ~ret:_ astate -> fun _ ~callee_procname:_ location ~ret:_ astate ->
let* astate, value = assign_value_fresh location this ~desc astate in let<*> astate, value = assign_value_fresh location this ~desc astate in
let astate = PulseArithmetic.and_eq_int (fst value) IntLit.zero astate in let astate = PulseArithmetic.and_eq_int (fst value) IntLit.zero astate in
let+ astate = PulseOperations.invalidate location Invalidation.OptionalEmpty value astate in let<+> astate = PulseOperations.invalidate location Invalidation.OptionalEmpty value astate in
[ExecutionDomain.ContinueProgram astate] astate
let assign_value this _value ~desc : model = let assign_value this _value ~desc : model =
fun _ ~callee_procname:_ location ~ret:_ astate -> fun _ ~callee_procname:_ location ~ret:_ astate ->
(* TODO: call the copy constructor of a value *) (* TODO: call the copy constructor of a value *)
let+ astate, value = assign_value_fresh location this ~desc astate in let<+> astate, value = assign_value_fresh location this ~desc astate in
let astate = PulseArithmetic.and_positive (fst value) astate in PulseArithmetic.and_positive (fst value) astate
[ExecutionDomain.ContinueProgram astate]
let assign_optional_value this init ~desc : model = let assign_optional_value this init ~desc : model =
fun _ ~callee_procname:_ location ~ret:_ astate -> fun _ ~callee_procname:_ location ~ret:_ astate ->
let* astate, value = to_internal_value_deref Read location init astate in let<*> astate, value = to_internal_value_deref Read location init astate in
let+ astate, _ = write_value location this ~value ~desc astate in let<+> astate, _ = write_value location this ~value ~desc astate in
[ExecutionDomain.ContinueProgram astate] astate
let emplace optional ~desc : model = let emplace optional ~desc : model =
fun _ ~callee_procname:_ location ~ret:_ astate -> fun _ ~callee_procname:_ location ~ret:_ astate ->
let+ astate, _ = assign_value_fresh location optional ~desc astate in let<+> astate, _ = assign_value_fresh location optional ~desc astate in
[ExecutionDomain.ContinueProgram astate] astate
let value optional ~desc : model = let value optional ~desc : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let event = ValueHistory.Call {f= Model desc; location; in_call= []} in
let* astate, ((value_addr, value_hist) as value) = let<*> astate, ((value_addr, value_hist) as value) =
to_internal_value_deref Write location optional astate to_internal_value_deref Write location optional astate
in in
(* Check dereference to show an error at the callsite of `value()` *) (* Check dereference to show an error at the callsite of `value()` *)
let* astate, _ = PulseOperations.eval_access Write location value Dereference astate in let<*> astate, _ = PulseOperations.eval_access Write location value Dereference astate in
PulseOperations.write_id ret_id (value_addr, event :: value_hist) astate |> ok_continue PulseOperations.write_id ret_id (value_addr, event :: value_hist) astate |> ok_continue
@ -304,51 +304,51 @@ module Optional = struct
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let ret_addr = AbstractValue.mk_fresh () in let ret_addr = AbstractValue.mk_fresh () in
let ret_value = (ret_addr, [ValueHistory.Call {f= Model desc; location; in_call= []}]) in let ret_value = (ret_addr, [ValueHistory.Call {f= Model desc; location; in_call= []}]) in
let+ astate, (value_addr, _) = to_internal_value_deref Read location optional astate in let<*> astate, (value_addr, _) = to_internal_value_deref Read location optional astate in
let astate = PulseOperations.write_id ret_id ret_value astate in let astate = PulseOperations.write_id ret_id ret_value astate in
let astate_non_empty = PulseArithmetic.and_positive value_addr astate in let astate_non_empty = PulseArithmetic.and_positive value_addr astate in
let astate_true = PulseArithmetic.and_positive ret_addr astate_non_empty in let astate_true = PulseArithmetic.and_positive ret_addr astate_non_empty in
let astate_empty = PulseArithmetic.and_eq_int value_addr IntLit.zero astate in let astate_empty = PulseArithmetic.and_eq_int value_addr IntLit.zero astate in
let astate_false = PulseArithmetic.and_eq_int ret_addr IntLit.zero astate_empty in let astate_false = PulseArithmetic.and_eq_int ret_addr IntLit.zero astate_empty in
[ExecutionDomain.ContinueProgram astate_false; ExecutionDomain.ContinueProgram astate_true] [Ok (ContinueProgram astate_false); Ok (ContinueProgram astate_true)]
let get_pointer optional ~desc : model = let get_pointer optional ~desc : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let event = ValueHistory.Call {f= Model desc; location; in_call= []} in
let* astate, value_addr = to_internal_value_deref Read location optional astate in let<*> astate, value_addr = to_internal_value_deref Read location optional astate in
let value_update_hist = (fst value_addr, event :: snd value_addr) in let value_update_hist = (fst value_addr, event :: snd value_addr) in
let astate_value_addr = let astate_value_addr =
PulseOperations.write_id ret_id value_update_hist astate PulseOperations.write_id ret_id value_update_hist astate
|> PulseArithmetic.and_positive (fst value_addr) |> PulseArithmetic.and_positive (fst value_addr)
in in
let nullptr = (AbstractValue.mk_fresh (), [event]) in let nullptr = (AbstractValue.mk_fresh (), [event]) in
let+ astate_null = let<*> astate_null =
PulseOperations.write_id ret_id nullptr astate PulseOperations.write_id ret_id nullptr astate
|> PulseArithmetic.and_eq_int (fst value_addr) IntLit.zero |> PulseArithmetic.and_eq_int (fst value_addr) IntLit.zero
|> PulseArithmetic.and_eq_int (fst nullptr) IntLit.zero |> PulseArithmetic.and_eq_int (fst nullptr) IntLit.zero
|> PulseOperations.invalidate location (ConstantDereference IntLit.zero) nullptr |> PulseOperations.invalidate location (ConstantDereference IntLit.zero) nullptr
in in
[ExecutionDomain.ContinueProgram astate_value_addr; ExecutionDomain.ContinueProgram astate_null] [Ok (ContinueProgram astate_value_addr); Ok (ContinueProgram astate_null)]
let value_or optional default ~desc : model = let value_or optional default ~desc : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let event = ValueHistory.Call {f= Model desc; location; in_call= []} in
let* astate, value_addr = to_internal_value_deref Read location optional astate in let<*> astate, value_addr = to_internal_value_deref Read location optional astate in
let astate_non_empty = PulseArithmetic.and_positive (fst value_addr) astate in let astate_non_empty = PulseArithmetic.and_positive (fst value_addr) astate in
let* astate_non_empty, value = let<*> astate_non_empty, value =
PulseOperations.eval_access Read location value_addr Dereference astate_non_empty PulseOperations.eval_access Read location value_addr Dereference astate_non_empty
in in
let value_update_hist = (fst value, event :: snd value) in let value_update_hist = (fst value, event :: snd value) in
let astate_value = PulseOperations.write_id ret_id value_update_hist astate_non_empty in let astate_value = PulseOperations.write_id ret_id value_update_hist astate_non_empty in
let+ astate, (default_val, default_hist) = let<*> astate, (default_val, default_hist) =
PulseOperations.eval_access Read location default Dereference astate PulseOperations.eval_access Read location default Dereference astate
in in
let default_value_hist = (default_val, event :: default_hist) in let default_value_hist = (default_val, event :: default_hist) in
let astate_empty = PulseArithmetic.and_eq_int (fst value_addr) IntLit.zero astate in let astate_empty = PulseArithmetic.and_eq_int (fst value_addr) IntLit.zero astate in
let astate_default = PulseOperations.write_id ret_id default_value_hist astate_empty in let astate_default = PulseOperations.write_id ret_id default_value_hist astate_empty in
[ExecutionDomain.ContinueProgram astate_value; ExecutionDomain.ContinueProgram astate_default] [Ok (ContinueProgram astate_value); Ok (ContinueProgram astate_default)]
end end
module Cplusplus = struct module Cplusplus = struct
@ -385,12 +385,12 @@ module StdAtomicInteger = struct
fun _ ~callee_procname:_ location ~ret:_ astate -> fun _ ~callee_procname:_ location ~ret:_ astate ->
let event = ValueHistory.Call {f= Model "std::atomic::atomic()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "std::atomic::atomic()"; location; in_call= []} in
let this = (AbstractValue.mk_fresh (), [event]) in let this = (AbstractValue.mk_fresh (), [event]) in
let* astate, int_field = let<*> astate, int_field =
PulseOperations.eval_access Write location this (FieldAccess internal_int) astate PulseOperations.eval_access Write location this (FieldAccess internal_int) astate
in in
let* astate = PulseOperations.write_deref location ~ref:int_field ~obj:init_value astate in let<*> astate = PulseOperations.write_deref location ~ref:int_field ~obj:init_value astate in
let+ astate = PulseOperations.write_deref location ~ref:this_address ~obj:this astate in let<+> astate = PulseOperations.write_deref location ~ref:this_address ~obj:this astate in
[ExecutionDomain.ContinueProgram astate] astate
let arith_bop prepost location event ret_id bop this operand astate = let arith_bop prepost location event ret_id bop this operand astate =
@ -409,30 +409,30 @@ module StdAtomicInteger = struct
let fetch_add this (increment, _) _memory_ordering : model = let fetch_add this (increment, _) _memory_ordering : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model "std::atomic::fetch_add()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "std::atomic::fetch_add()"; location; in_call= []} in
let+ astate = let<+> astate =
arith_bop `Post location event ret_id (PlusA None) this (AbstractValueOperand increment) arith_bop `Post location event ret_id (PlusA None) this (AbstractValueOperand increment)
astate astate
in in
[ExecutionDomain.ContinueProgram astate] astate
let fetch_sub this (increment, _) _memory_ordering : model = let fetch_sub this (increment, _) _memory_ordering : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model "std::atomic::fetch_sub()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "std::atomic::fetch_sub()"; location; in_call= []} in
let+ astate = let<+> astate =
arith_bop `Post location event ret_id (MinusA None) this (AbstractValueOperand increment) arith_bop `Post location event ret_id (MinusA None) this (AbstractValueOperand increment)
astate astate
in in
[ExecutionDomain.ContinueProgram astate] astate
let operator_plus_plus_pre this : model = let operator_plus_plus_pre this : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model "std::atomic::operator++()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "std::atomic::operator++()"; location; in_call= []} in
let+ astate = let<+> astate =
arith_bop `Pre location event ret_id (PlusA None) this (LiteralOperand IntLit.one) astate arith_bop `Pre location event ret_id (PlusA None) this (LiteralOperand IntLit.one) astate
in in
[ExecutionDomain.ContinueProgram astate] astate
let operator_plus_plus_post this _int : model = let operator_plus_plus_post this _int : model =
@ -440,19 +440,19 @@ module StdAtomicInteger = struct
let event = let event =
ValueHistory.Call {f= Model "std::atomic<T>::operator++(T)"; location; in_call= []} ValueHistory.Call {f= Model "std::atomic<T>::operator++(T)"; location; in_call= []}
in in
let+ astate = let<+> astate =
arith_bop `Post location event ret_id (PlusA None) this (LiteralOperand IntLit.one) astate arith_bop `Post location event ret_id (PlusA None) this (LiteralOperand IntLit.one) astate
in in
[ExecutionDomain.ContinueProgram astate] astate
let operator_minus_minus_pre this : model = let operator_minus_minus_pre this : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model "std::atomic::operator--()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "std::atomic::operator--()"; location; in_call= []} in
let+ astate = let<+> astate =
arith_bop `Pre location event ret_id (MinusA None) this (LiteralOperand IntLit.one) astate arith_bop `Pre location event ret_id (MinusA None) this (LiteralOperand IntLit.one) astate
in in
[ExecutionDomain.ContinueProgram astate] astate
let operator_minus_minus_post this _int : model = let operator_minus_minus_post this _int : model =
@ -460,18 +460,17 @@ module StdAtomicInteger = struct
let event = let event =
ValueHistory.Call {f= Model "std::atomic<T>::operator--(T)"; location; in_call= []} ValueHistory.Call {f= Model "std::atomic<T>::operator--(T)"; location; in_call= []}
in in
let+ astate = let<+> astate =
arith_bop `Post location event ret_id (MinusA None) this (LiteralOperand IntLit.one) astate arith_bop `Post location event ret_id (MinusA None) this (LiteralOperand IntLit.one) astate
in in
[ExecutionDomain.ContinueProgram astate] astate
let load_instr model_desc this _memory_ordering_opt : model = let load_instr model_desc this _memory_ordering_opt : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model model_desc; location; in_call= []} in let event = ValueHistory.Call {f= Model model_desc; location; in_call= []} in
let+ astate, _int_addr, (int, hist) = load_backing_int location this astate in let<+> astate, _int_addr, (int, hist) = load_backing_int location this astate in
let astate = PulseOperations.write_id ret_id (int, event :: hist) astate in PulseOperations.write_id ret_id (int, event :: hist) astate
[ExecutionDomain.ContinueProgram astate]
let load = load_instr "std::atomic<T>::load()" let load = load_instr "std::atomic<T>::load()"
@ -489,17 +488,16 @@ module StdAtomicInteger = struct
let store this_address (new_value, new_hist) _memory_ordering : model = let store this_address (new_value, new_hist) _memory_ordering : model =
fun _ ~callee_procname:_ location ~ret:_ astate -> fun _ ~callee_procname:_ location ~ret:_ astate ->
let event = ValueHistory.Call {f= Model "std::atomic::store()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "std::atomic::store()"; location; in_call= []} in
let+ astate = store_backing_int location this_address (new_value, event :: new_hist) astate in let<+> astate = store_backing_int location this_address (new_value, event :: new_hist) astate in
[ExecutionDomain.ContinueProgram astate] astate
let exchange this_address (new_value, new_hist) _memory_ordering : model = let exchange this_address (new_value, new_hist) _memory_ordering : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model "std::atomic::exchange()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "std::atomic::exchange()"; location; in_call= []} in
let* astate, _int_addr, (old_int, old_hist) = load_backing_int location this_address astate in let<*> astate, _int_addr, (old_int, old_hist) = load_backing_int location this_address astate in
let+ astate = store_backing_int location this_address (new_value, event :: new_hist) astate in let<+> astate = store_backing_int location this_address (new_value, event :: new_hist) astate in
let astate = PulseOperations.write_id ret_id (old_int, event :: old_hist) astate in PulseOperations.write_id ret_id (old_int, event :: old_hist) astate
[ExecutionDomain.ContinueProgram astate]
end end
module JavaObject = struct module JavaObject = struct
@ -507,12 +505,11 @@ module JavaObject = struct
let clone src_pointer_hist : model = let clone src_pointer_hist : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model "Object.clone"; location; in_call= []} in let event = ValueHistory.Call {f= Model "Object.clone"; location; in_call= []} in
let* astate, obj = let<*> astate, obj =
PulseOperations.eval_access Read location src_pointer_hist Dereference astate PulseOperations.eval_access Read location src_pointer_hist Dereference astate
in in
let+ astate, obj_copy = PulseOperations.shallow_copy location obj astate in let<+> astate, obj_copy = PulseOperations.shallow_copy location obj astate in
let astate = PulseOperations.write_id ret_id (fst obj_copy, event :: snd obj_copy) astate in PulseOperations.write_id ret_id (fst obj_copy, event :: snd obj_copy) astate
[ExecutionDomain.ContinueProgram astate]
end end
module StdBasicString = struct module StdBasicString = struct
@ -531,52 +528,49 @@ module StdBasicString = struct
let data this_hist : model = let data this_hist : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model "std::basic_string::data()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "std::basic_string::data()"; location; in_call= []} in
let* astate, string_addr_hist = to_internal_string location this_hist astate in let<*> astate, string_addr_hist = to_internal_string location this_hist astate in
let+ astate, (string, hist) = let<+> astate, (string, hist) =
PulseOperations.eval_access Read location string_addr_hist Dereference astate PulseOperations.eval_access Read location string_addr_hist Dereference astate
in in
let astate = PulseOperations.write_id ret_id (string, event :: hist) astate in PulseOperations.write_id ret_id (string, event :: hist) astate
[ExecutionDomain.ContinueProgram astate]
let destructor this_hist : model = let destructor this_hist : model =
fun _ ~callee_procname:_ location ~ret:_ astate -> fun _ ~callee_procname:_ location ~ret:_ astate ->
let model = CallEvent.Model "std::basic_string::~basic_string()" in let model = CallEvent.Model "std::basic_string::~basic_string()" in
let call_event = ValueHistory.Call {f= model; location; in_call= []} in let call_event = ValueHistory.Call {f= model; location; in_call= []} in
let* astate, (string_addr, string_hist) = to_internal_string location this_hist astate in let<*> astate, (string_addr, string_hist) = to_internal_string location this_hist astate in
let string_addr_hist = (string_addr, call_event :: string_hist) in let string_addr_hist = (string_addr, call_event :: string_hist) in
let* astate = let<*> astate =
PulseOperations.invalidate_access location CppDelete string_addr_hist Dereference astate PulseOperations.invalidate_access location CppDelete string_addr_hist Dereference astate
in in
let+ astate = PulseOperations.invalidate location CppDelete string_addr_hist astate in let<+> astate = PulseOperations.invalidate location CppDelete string_addr_hist astate in
[ExecutionDomain.ContinueProgram astate] astate
end end
module StdFunction = struct module StdFunction = struct
let operator_call ProcnameDispatcher.Call.FuncArg.{arg_payload= lambda_ptr_hist; typ} actuals : let operator_call ProcnameDispatcher.Call.FuncArg.{arg_payload= lambda_ptr_hist; typ} actuals :
model = model =
fun {analyze_dependency; proc_desc; tenv; err_log} ~callee_procname:_ location ~ret astate -> fun {analyze_dependency; proc_desc} ~callee_procname:_ location ~ret astate ->
let havoc_ret (ret_id, _) astate = let havoc_ret (ret_id, _) astate =
let event = ValueHistory.Call {f= Model "std::function::operator()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "std::function::operator()"; location; in_call= []} in
[PulseOperations.havoc_id ret_id [event] astate] [PulseOperations.havoc_id ret_id [event] astate]
in in
let* astate, (lambda, _) = let<*> astate, (lambda, _) =
PulseOperations.eval_access Read location lambda_ptr_hist Dereference astate PulseOperations.eval_access Read location lambda_ptr_hist Dereference astate
in in
let* astate = PulseOperations.Closures.check_captured_addresses location lambda astate in let<*> astate = PulseOperations.Closures.check_captured_addresses location lambda astate in
match AddressAttributes.get_closure_proc_name lambda astate with match AddressAttributes.get_closure_proc_name lambda astate with
| None -> | None ->
(* we don't know what proc name this lambda resolves to *) (* we don't know what proc name this lambda resolves to *)
Ok havoc_ret ret astate |> List.map ~f:(fun astate -> Ok (ContinueProgram astate))
( havoc_ret ret astate
|> List.map ~f:(fun astate -> ExecutionDomain.ContinueProgram astate) )
| Some callee_proc_name -> | Some callee_proc_name ->
let actuals = let actuals =
(lambda_ptr_hist, typ) (lambda_ptr_hist, typ)
:: List.map actuals ~f:(fun ProcnameDispatcher.Call.FuncArg.{arg_payload; typ} -> :: List.map actuals ~f:(fun ProcnameDispatcher.Call.FuncArg.{arg_payload; typ} ->
(arg_payload, typ) ) (arg_payload, typ) )
in in
PulseOperations.call ~caller_proc_desc:proc_desc tenv err_log PulseOperations.call ~caller_proc_desc:proc_desc
~callee_data:(analyze_dependency callee_proc_name) ~callee_data:(analyze_dependency callee_proc_name)
location callee_proc_name ~ret ~actuals ~formals_opt:None astate location callee_proc_name ~ret ~actuals ~formals_opt:None astate
@ -584,10 +578,9 @@ module StdFunction = struct
let assign dest ProcnameDispatcher.Call.FuncArg.{arg_payload= src; typ= src_typ} ~desc : model = let assign dest ProcnameDispatcher.Call.FuncArg.{arg_payload= src; typ= src_typ} ~desc : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let event = ValueHistory.Call {f= Model desc; location; in_call= []} in
let+ astate =
if PulseArithmetic.is_known_zero astate (fst src) then if PulseArithmetic.is_known_zero astate (fst src) then
let empty_target = AbstractValue.mk_fresh () in let empty_target = AbstractValue.mk_fresh () in
let+ astate = let<+> astate =
PulseOperations.write_deref location ~ref:dest ~obj:(empty_target, [event]) astate PulseOperations.write_deref location ~ref:dest ~obj:(empty_target, [event]) astate
in in
PulseOperations.havoc_id ret_id [event] astate PulseOperations.havoc_id ret_id [event] astate
@ -597,8 +590,6 @@ module StdFunction = struct
Misc.shallow_copy location event ret_id dest src astate Misc.shallow_copy location event ret_id dest src astate
| _ -> | _ ->
Misc.shallow_copy_value location event ret_id dest src astate Misc.shallow_copy_value location event ret_id dest src astate
in
[ExecutionDomain.ContinueProgram astate]
end end
module GenericArrayBackedCollection = struct module GenericArrayBackedCollection = struct
@ -689,14 +680,15 @@ module GenericArrayBackedCollectionIterator = struct
let constructor ~desc this init : model = let constructor ~desc this init : model =
fun _ ~callee_procname:_ location ~ret:_ astate -> fun _ ~callee_procname:_ location ~ret:_ astate ->
let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let event = ValueHistory.Call {f= Model desc; location; in_call= []} in
construct location event ~init ~ref:this astate >>| ExecutionDomain.continue >>| List.return let<+> astate = construct location event ~init ~ref:this astate in
astate
let operator_compare comparison ~desc iter_lhs iter_rhs _ ~callee_procname:_ location let operator_compare comparison ~desc iter_lhs iter_rhs : model =
~ret:(ret_id, _) astate = fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let event = ValueHistory.Call {f= Model desc; location; in_call= []} in
let* astate, _, (index_lhs, _) = to_internal_pointer_deref Read location iter_lhs astate in let<*> astate, _, (index_lhs, _) = to_internal_pointer_deref Read location iter_lhs astate in
let+ astate, _, (index_rhs, _) = to_internal_pointer_deref Read location iter_rhs astate in let<*> astate, _, (index_rhs, _) = to_internal_pointer_deref Read location iter_rhs astate in
let ret_val = AbstractValue.mk_fresh () in let ret_val = AbstractValue.mk_fresh () in
let astate = PulseOperations.write_id ret_id (ret_val, [event]) astate in let astate = PulseOperations.write_id ret_id (ret_val, [event]) astate in
let ret_val_equal, ret_val_notequal = let ret_val_equal, ret_val_notequal =
@ -716,24 +708,29 @@ module GenericArrayBackedCollectionIterator = struct
|> PulseArithmetic.prune_binop ~negated:false Ne (AbstractValueOperand index_lhs) |> PulseArithmetic.prune_binop ~negated:false Ne (AbstractValueOperand index_lhs)
(AbstractValueOperand index_rhs) (AbstractValueOperand index_rhs)
in in
[ExecutionDomain.ContinueProgram astate_equal; ExecutionDomain.ContinueProgram astate_notequal] [Ok (ContinueProgram astate_equal); Ok (ContinueProgram astate_notequal)]
let operator_star ~desc iter _ ~callee_procname:_ location ~ret astate = let operator_star ~desc iter : model =
fun _ ~callee_procname:_ location ~ret astate ->
let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let event = ValueHistory.Call {f= Model desc; location; in_call= []} in
let+ astate, pointer, (elem, _) = to_elem_pointed_by_iterator Read location iter astate in let<+> astate, pointer, (elem, _) = to_elem_pointed_by_iterator Read location iter astate in
let astate = PulseOperations.write_id (fst ret) (elem, event :: snd pointer) astate in PulseOperations.write_id (fst ret) (elem, event :: snd pointer) astate
[ExecutionDomain.ContinueProgram astate]
let operator_step step ~desc iter _ ~callee_procname:_ location ~ret:_ astate = let operator_step step ~desc iter : model =
fun _ ~callee_procname:_ location ~ret:_ astate ->
let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let event = ValueHistory.Call {f= Model desc; location; in_call= []} in
let index_new = AbstractValue.mk_fresh () in let index_new = AbstractValue.mk_fresh () in
let* astate, pointer, _ = let<*> astate, pointer, _ =
to_elem_pointed_by_iterator Read ~step:(Some step) location iter astate to_elem_pointed_by_iterator Read ~step:(Some step) location iter astate
in in
PulseOperations.write_deref location ~ref:pointer ~obj:(index_new, event :: snd pointer) astate let<+> astate =
>>| ExecutionDomain.continue >>| List.return PulseOperations.write_deref location ~ref:pointer
~obj:(index_new, event :: snd pointer)
astate
in
astate
end end
module JavaIterator = struct module JavaIterator = struct
@ -741,54 +738,54 @@ module JavaIterator = struct
fun _ ~callee_procname:_ location ~ret astate -> fun _ ~callee_procname:_ location ~ret astate ->
let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let event = ValueHistory.Call {f= Model desc; location; in_call= []} in
let ref = (AbstractValue.mk_fresh (), [event]) in let ref = (AbstractValue.mk_fresh (), [event]) in
let+ astate = GenericArrayBackedCollectionIterator.construct location event ~init ~ref astate in let<+> astate =
let astate = PulseOperations.write_id (fst ret) ref astate in GenericArrayBackedCollectionIterator.construct location event ~init ~ref astate
[ExecutionDomain.ContinueProgram astate] in
PulseOperations.write_id (fst ret) ref astate
(* {curr -> v_c} is modified to {curr -> v_fresh} and returns array[v_c] *) (* {curr -> v_c} is modified to {curr -> v_fresh} and returns array[v_c] *)
let next ~desc iter _ ~callee_procname:_ location ~ret astate = let next ~desc iter : model =
fun _ ~callee_procname:_ location ~ret astate ->
let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let event = ValueHistory.Call {f= Model desc; location; in_call= []} in
let new_index = AbstractValue.mk_fresh () in let new_index = AbstractValue.mk_fresh () in
let* astate, (curr_index, curr_index_hist) = let<*> astate, (curr_index, curr_index_hist) =
GenericArrayBackedCollectionIterator.to_internal_pointer Read location iter astate GenericArrayBackedCollectionIterator.to_internal_pointer Read location iter astate
in in
let* astate, (curr_elem_val, curr_elem_hist) = let<*> astate, (curr_elem_val, curr_elem_hist) =
GenericArrayBackedCollection.element location iter curr_index astate GenericArrayBackedCollection.element location iter curr_index astate
in in
let+ astate = let<+> astate =
PulseOperations.write_field location ~ref:iter PulseOperations.write_field location ~ref:iter
GenericArrayBackedCollectionIterator.internal_pointer GenericArrayBackedCollectionIterator.internal_pointer
~obj:(new_index, event :: curr_index_hist) ~obj:(new_index, event :: curr_index_hist)
astate astate
in in
let astate =
PulseOperations.write_id (fst ret) (curr_elem_val, event :: curr_elem_hist) astate PulseOperations.write_id (fst ret) (curr_elem_val, event :: curr_elem_hist) astate
in
[ExecutionDomain.ContinueProgram astate]
(* {curr -> v_c } is modified to {curr -> v_fresh} and writes to array[v_c] *) (* {curr -> v_c } is modified to {curr -> v_fresh} and writes to array[v_c] *)
let remove ~desc iter _ ~callee_procname:_ location ~ret:_ astate = let remove ~desc iter : model =
fun _ ~callee_procname:_ location ~ret:_ astate ->
let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let event = ValueHistory.Call {f= Model desc; location; in_call= []} in
let new_index = AbstractValue.mk_fresh () in let new_index = AbstractValue.mk_fresh () in
let* astate, (curr_index, curr_index_hist) = let<*> astate, (curr_index, curr_index_hist) =
GenericArrayBackedCollectionIterator.to_internal_pointer Read location iter astate GenericArrayBackedCollectionIterator.to_internal_pointer Read location iter astate
in in
let* astate = let<*> astate =
PulseOperations.write_field location ~ref:iter PulseOperations.write_field location ~ref:iter
GenericArrayBackedCollectionIterator.internal_pointer GenericArrayBackedCollectionIterator.internal_pointer
~obj:(new_index, event :: curr_index_hist) ~obj:(new_index, event :: curr_index_hist)
astate astate
in in
let new_elem = AbstractValue.mk_fresh () in let new_elem = AbstractValue.mk_fresh () in
let* astate, arr = GenericArrayBackedCollection.eval Read location iter astate in let<*> astate, arr = GenericArrayBackedCollection.eval Read location iter astate in
let+ astate = let<+> astate =
PulseOperations.write_arr_index location ~ref:arr ~index:curr_index PulseOperations.write_arr_index location ~ref:arr ~index:curr_index
~obj:(new_elem, event :: curr_index_hist) ~obj:(new_elem, event :: curr_index_hist)
astate astate
in in
[ExecutionDomain.ContinueProgram astate] astate
end end
module StdVector = struct module StdVector = struct
@ -802,15 +799,16 @@ module StdVector = struct
>>= PulseOperations.havoc_field location vector GenericArrayBackedCollection.field trace >>= PulseOperations.havoc_field location vector GenericArrayBackedCollection.field trace
let init_list_constructor this init_list _ ~callee_procname:_ location ~ret:_ astate = let init_list_constructor this init_list : model =
fun _ ~callee_procname:_ location ~ret:_ astate ->
let event = ValueHistory.Call {f= Model "std::vector::vector()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "std::vector::vector()"; location; in_call= []} in
let* astate, init_copy = PulseOperations.shallow_copy location init_list astate in let<*> astate, init_copy = PulseOperations.shallow_copy location init_list astate in
let+ astate = let<+> astate =
PulseOperations.write_field location ~ref:this GenericArrayBackedCollection.field PulseOperations.write_field location ~ref:this GenericArrayBackedCollection.field
~obj:(fst init_copy, event :: snd init_copy) ~obj:(fst init_copy, event :: snd init_copy)
astate astate
in in
[ExecutionDomain.ContinueProgram astate] astate
let invalidate_references vector_f vector : model = let invalidate_references vector_f vector : model =
@ -821,18 +819,17 @@ module StdVector = struct
; location ; location
; in_call= [] } ; in_call= [] }
in in
reallocate_internal_array [crumb] vector vector_f location astate let<+> astate = reallocate_internal_array [crumb] vector vector_f location astate in
>>| ExecutionDomain.continue >>| List.return astate
let at ~desc vector index : model = let at ~desc vector index : model =
fun _ ~callee_procname:_ location ~ret astate -> fun _ ~callee_procname:_ location ~ret astate ->
let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let event = ValueHistory.Call {f= Model desc; location; in_call= []} in
let+ astate, (addr, hist) = let<+> astate, (addr, hist) =
GenericArrayBackedCollection.element location vector (fst index) astate GenericArrayBackedCollection.element location vector (fst index) astate
in in
let astate = PulseOperations.write_id (fst ret) (addr, event :: hist) astate in PulseOperations.write_id (fst ret) (addr, event :: hist) astate
[ExecutionDomain.ContinueProgram astate]
let vector_begin vector iter : model = let vector_begin vector iter : model =
@ -842,44 +839,48 @@ module StdVector = struct
let pointer_val = (AbstractValue.mk_fresh (), pointer_hist) in let pointer_val = (AbstractValue.mk_fresh (), pointer_hist) in
let index_zero = AbstractValue.mk_fresh () in let index_zero = AbstractValue.mk_fresh () in
let astate = PulseArithmetic.and_eq_int index_zero IntLit.zero astate in let astate = PulseArithmetic.and_eq_int index_zero IntLit.zero astate in
let* astate, ((arr_addr, _) as arr) = let<*> astate, ((arr_addr, _) as arr) =
GenericArrayBackedCollection.eval Read location vector astate GenericArrayBackedCollection.eval Read location vector astate
in in
let* astate, _ = GenericArrayBackedCollection.eval_element location arr index_zero astate in let<*> astate, _ = GenericArrayBackedCollection.eval_element location arr index_zero astate in
let<+> astate =
PulseOperations.write_field location ~ref:iter GenericArrayBackedCollection.field PulseOperations.write_field location ~ref:iter GenericArrayBackedCollection.field
~obj:(arr_addr, pointer_hist) astate ~obj:(arr_addr, pointer_hist) astate
>>= PulseOperations.write_field location ~ref:iter >>= PulseOperations.write_field location ~ref:iter
GenericArrayBackedCollectionIterator.internal_pointer ~obj:pointer_val GenericArrayBackedCollectionIterator.internal_pointer ~obj:pointer_val
>>= PulseOperations.write_deref location ~ref:pointer_val ~obj:(index_zero, pointer_hist) >>= PulseOperations.write_deref location ~ref:pointer_val ~obj:(index_zero, pointer_hist)
>>| ExecutionDomain.continue >>| List.return in
astate
let vector_end vector iter : model = let vector_end vector iter : model =
fun _ ~callee_procname:_ location ~ret:_ astate -> fun _ ~callee_procname:_ location ~ret:_ astate ->
let event = ValueHistory.Call {f= Model "std::vector::end()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "std::vector::end()"; location; in_call= []} in
let* astate, (arr_addr, _) = GenericArrayBackedCollection.eval Read location vector astate in let<*> astate, (arr_addr, _) = GenericArrayBackedCollection.eval Read location vector astate in
let* astate, (pointer_addr, _) = let<*> astate, (pointer_addr, _) =
GenericArrayBackedCollection.eval_pointer_to_last_element location vector astate GenericArrayBackedCollection.eval_pointer_to_last_element location vector astate
in in
let pointer_hist = event :: snd iter in let pointer_hist = event :: snd iter in
let pointer_val = (pointer_addr, pointer_hist) in let pointer_val = (pointer_addr, pointer_hist) in
let* astate = let<*> astate =
PulseOperations.write_field location ~ref:iter GenericArrayBackedCollection.field PulseOperations.write_field location ~ref:iter GenericArrayBackedCollection.field
~obj:(arr_addr, pointer_hist) astate ~obj:(arr_addr, pointer_hist) astate
in in
let+ astate = let<+> astate =
PulseOperations.write_field location ~ref:iter PulseOperations.write_field location ~ref:iter
GenericArrayBackedCollectionIterator.internal_pointer ~obj:pointer_val astate GenericArrayBackedCollectionIterator.internal_pointer ~obj:pointer_val astate
in in
[ExecutionDomain.ContinueProgram astate] astate
let reserve vector : model = let reserve vector : model =
fun _ ~callee_procname:_ location ~ret:_ astate -> fun _ ~callee_procname:_ location ~ret:_ astate ->
let crumb = ValueHistory.Call {f= Model "std::vector::reserve()"; location; in_call= []} in let crumb = ValueHistory.Call {f= Model "std::vector::reserve()"; location; in_call= []} in
let<+> astate =
reallocate_internal_array [crumb] vector Reserve location astate reallocate_internal_array [crumb] vector Reserve location astate
>>| AddressAttributes.std_vector_reserve (fst vector) >>| AddressAttributes.std_vector_reserve (fst vector)
>>| ExecutionDomain.continue >>| List.return in
astate
let push_back vector : model = let push_back vector : model =
@ -891,18 +892,17 @@ module StdVector = struct
ok_continue astate ok_continue astate
else else
(* simulate a re-allocation of the underlying array every time an element is added *) (* simulate a re-allocation of the underlying array every time an element is added *)
reallocate_internal_array [crumb] vector PushBack location astate let<+> astate = reallocate_internal_array [crumb] vector PushBack location astate in
>>| ExecutionDomain.continue >>| List.return astate
let empty vector : model = let empty vector : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let crumb = ValueHistory.Call {f= Model "std::vector::empty()"; location; in_call= []} in let crumb = ValueHistory.Call {f= Model "std::vector::empty()"; location; in_call= []} in
let+ astate, (value_addr, value_hist) = let<+> astate, (value_addr, value_hist) =
GenericArrayBackedCollection.eval_is_empty location vector astate GenericArrayBackedCollection.eval_is_empty location vector astate
in in
let astate = PulseOperations.write_id ret_id (value_addr, crumb :: value_hist) astate in PulseOperations.write_id ret_id (value_addr, crumb :: value_hist) astate
[ExecutionDomain.ContinueProgram astate]
end end
module JavaCollection = struct module JavaCollection = struct
@ -910,30 +910,29 @@ module JavaCollection = struct
let set coll (index, _) (new_elem, new_elem_hist) : model = let set coll (index, _) (new_elem, new_elem_hist) : model =
fun _ ~callee_procname:_ location ~ret astate -> fun _ ~callee_procname:_ location ~ret astate ->
let event = ValueHistory.Call {f= Model "Collection.set"; location; in_call= []} in let event = ValueHistory.Call {f= Model "Collection.set"; location; in_call= []} in
let* astate, arr = GenericArrayBackedCollection.eval Read location coll astate in let<*> astate, arr = GenericArrayBackedCollection.eval Read location coll astate in
let* astate, (old_addr, old_hist) = let<*> astate, (old_addr, old_hist) =
GenericArrayBackedCollection.element location coll index astate GenericArrayBackedCollection.element location coll index astate
in in
let+ astate = let<+> astate =
PulseOperations.write_arr_index location ~ref:arr ~index PulseOperations.write_arr_index location ~ref:arr ~index
~obj:(new_elem, event :: new_elem_hist) ~obj:(new_elem, event :: new_elem_hist)
astate astate
in in
let astate = PulseOperations.write_id (fst ret) (old_addr, event :: old_hist) astate in PulseOperations.write_id (fst ret) (old_addr, event :: old_hist) astate
[ExecutionDomain.ContinueProgram astate]
(* writes to arr[index]-> new_elem *) (* writes to arr[index]-> new_elem *)
let add_at coll (index, _) (new_elem, new_elem_hist) : model = let add_at coll (index, _) (new_elem, new_elem_hist) : model =
fun _ ~callee_procname:_ location ~ret:_ astate -> fun _ ~callee_procname:_ location ~ret:_ astate ->
let event = ValueHistory.Call {f= Model "Collection.add"; location; in_call= []} in let event = ValueHistory.Call {f= Model "Collection.add"; location; in_call= []} in
let* astate, arr = GenericArrayBackedCollection.eval Read location coll astate in let<*> astate, arr = GenericArrayBackedCollection.eval Read location coll astate in
let+ astate = let<+> astate =
PulseOperations.write_arr_index location ~ref:arr ~index PulseOperations.write_arr_index location ~ref:arr ~index
~obj:(new_elem, event :: new_elem_hist) ~obj:(new_elem, event :: new_elem_hist)
astate astate
in in
[ExecutionDomain.ContinueProgram astate] astate
(* writes to arr[index]-> new_elem where index is a fresh address *) (* writes to arr[index]-> new_elem where index is a fresh address *)
@ -946,14 +945,14 @@ module JavaCollection = struct
let remove_at coll (index, _) : model = let remove_at coll (index, _) : model =
fun _ ~callee_procname:_ location ~ret:_ astate -> fun _ ~callee_procname:_ location ~ret:_ astate ->
let event = ValueHistory.Call {f= Model "Collection.add"; location; in_call= []} in let event = ValueHistory.Call {f= Model "Collection.add"; location; in_call= []} in
let* astate, arr = GenericArrayBackedCollection.eval Read location coll astate in let<*> astate, arr = GenericArrayBackedCollection.eval Read location coll astate in
let fresh_elem = AbstractValue.mk_fresh () in let fresh_elem = AbstractValue.mk_fresh () in
let+ astate = let<+> astate =
PulseOperations.write_arr_index location ~ref:arr ~index PulseOperations.write_arr_index location ~ref:arr ~index
~obj:(fresh_elem, event :: snd arr) ~obj:(fresh_elem, event :: snd arr)
astate astate
in in
[ExecutionDomain.ContinueProgram astate] astate
(* writes to arr[index]-> fresh_elem where index is fresh *) (* writes to arr[index]-> fresh_elem where index is fresh *)
@ -990,14 +989,14 @@ module JavaInteger = struct
let constructor this_address init_value : model = let constructor this_address init_value : model =
fun _ ~callee_procname:_ location ~ret:_ astate -> fun _ ~callee_procname:_ location ~ret:_ astate ->
let event = ValueHistory.Call {f= Model "Integer.init"; location; in_call= []} in let event = ValueHistory.Call {f= Model "Integer.init"; location; in_call= []} in
let+ state = construct this_address init_value event location astate in let<+> astate = construct this_address init_value event location astate in
[ExecutionDomain.continue state] astate
let equals this arg : model = let equals this arg : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let* astate, _int_addr1, (int1, hist) = load_backing_int location this astate in let<*> astate, _int_addr1, (int1, hist) = load_backing_int location this astate in
let* astate, _int_addr2, (int2, _) = load_backing_int location arg astate in let<*> astate, _int_addr2, (int2, _) = load_backing_int location arg astate in
let binop_addr = AbstractValue.mk_fresh () in let binop_addr = AbstractValue.mk_fresh () in
let astate = let astate =
PulseArithmetic.eval_binop binop_addr Binop.Eq (AbstractValueOperand int1) PulseArithmetic.eval_binop binop_addr Binop.Eq (AbstractValueOperand int1)
@ -1008,7 +1007,7 @@ module JavaInteger = struct
let int_val this : model = let int_val this : model =
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let* astate, _int_addr1, int_value_hist = load_backing_int location this astate in let<*> astate, _int_addr1, int_value_hist = load_backing_int location this astate in
PulseOperations.write_id ret_id int_value_hist astate |> ok_continue PulseOperations.write_id ret_id int_value_hist astate |> ok_continue
@ -1016,9 +1015,8 @@ module JavaInteger = struct
fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun _ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model "Integer.valueOf"; location; in_call= []} in let event = ValueHistory.Call {f= Model "Integer.valueOf"; location; in_call= []} in
let new_alloc = (AbstractValue.mk_fresh (), [event]) in let new_alloc = (AbstractValue.mk_fresh (), [event]) in
let+ astate = construct new_alloc init_value event location astate in let<+> astate = construct new_alloc init_value event location astate in
let astate = PulseOperations.write_id ret_id new_alloc astate in PulseOperations.write_id ret_id new_alloc astate
[ExecutionDomain.continue astate]
end end
module JavaPreconditions = struct module JavaPreconditions = struct

@ -14,7 +14,7 @@ type model =
-> Location.t -> Location.t
-> ret:Ident.t * Typ.t -> ret:Ident.t * Typ.t
-> AbductiveDomain.t -> AbductiveDomain.t
-> ExecutionDomain.t list PulseOperations.access_result -> ExecutionDomain.t PulseOperations.access_result list
val dispatch : val dispatch :
Tenv.t Tenv.t

@ -4,17 +4,35 @@
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*) *)
open! IStd open! IStd
module L = Logging module L = Logging
open IResult.Let_syntax
open PulseBasicInterface open PulseBasicInterface
open PulseDomainInterface open PulseDomainInterface
type t = AbductiveDomain.t type t = AbductiveDomain.t
type 'a access_result = 'a PulseReport.access_result module Import = struct
type access_mode = Read | Write | NoAccess
type 'abductive_domain_t base_t = 'abductive_domain_t ExecutionDomain.base_t =
| ContinueProgram of 'abductive_domain_t
| ExitProgram of AbductiveDomain.summary
| AbortProgram of AbductiveDomain.summary
| LatentAbortProgram of {astate: AbductiveDomain.summary; latent_issue: LatentIssue.t}
| ISLLatentMemoryError of 'abductive_domain_t
type 'a access_result = 'a PulseReport.access_result
type access_mode = Read | Write | NoAccess include IResult.Let_syntax
let ( let<*> ) x f = match x with Error _ as err -> [err] | Ok y -> f y
let ( let<+> ) x f =
match x with Error _ as err -> [err] | Ok y -> [Ok (ExecutionDomain.ContinueProgram (f y))]
end
include Import
let check_addr_access access_mode location (address, history) astate = let check_addr_access access_mode location (address, history) astate =
let access_trace = Trace.Immediate {location; history} in let access_trace = Trace.Immediate {location; history} in
@ -665,8 +683,8 @@ let conservatively_initialize_args arg_values ({AbductiveDomain.post} as astate)
AbstractValue.Set.fold AbductiveDomain.initialize reachable_values astate AbstractValue.Set.fold AbductiveDomain.initialize reachable_values astate
let call ~caller_proc_desc tenv err_log ~(callee_data : (Procdesc.t * PulseSummary.t) option) let call ~caller_proc_desc ~(callee_data : (Procdesc.t * PulseSummary.t) option) call_loc
call_loc callee_pname ~ret ~actuals ~formals_opt (astate : AbductiveDomain.t) = callee_pname ~ret ~actuals ~formals_opt (astate : AbductiveDomain.t) =
let get_arg_values () = List.map actuals ~f:(fun ((value, _), _) -> value) in let get_arg_values () = List.map actuals ~f:(fun ((value, _), _) -> value) in
match callee_data with match callee_data with
| Some (callee_proc_desc, exec_states) -> | Some (callee_proc_desc, exec_states) ->
@ -688,7 +706,7 @@ let call ~caller_proc_desc tenv err_log ~(callee_data : (Procdesc.t * PulseSumma
let pvar = Pvar.mk name callee_pname in let pvar = Pvar.mk name callee_pname in
(Var.of_pvar pvar, capture_mode) ) (Var.of_pvar pvar, capture_mode) )
in in
let+ astate, captured_vars_with_actuals = let<*> astate, captured_vars_with_actuals =
match actuals with match actuals with
| (actual_closure, _) :: _ | (actual_closure, _) :: _
when not (Procname.is_objc_block callee_pname || List.is_empty captured_vars) -> when not (Procname.is_objc_block callee_pname || List.is_empty captured_vars) ->
@ -718,15 +736,13 @@ let call ~caller_proc_desc tenv err_log ~(callee_data : (Procdesc.t * PulseSumma
| Unsat -> | Unsat ->
(* couldn't apply pre/post pair *) (* couldn't apply pre/post pair *)
posts posts
| Sat post -> ( | Sat post ->
match PulseReport.report_error caller_proc_desc tenv err_log post with post :: posts )
| Error Unsat ->
posts
| Error (Sat post) | Ok post ->
post :: posts ) )
| None -> | None ->
(* no spec found for some reason (unknown function, ...) *) (* no spec found for some reason (unknown function, ...) *)
L.d_printfln "No spec found for %a@\n" Procname.pp callee_pname ; L.d_printfln "No spec found for %a@\n" Procname.pp callee_pname ;
let astate = conservatively_initialize_args (get_arg_values ()) astate in let astate =
unknown_call call_loc (SkippedKnownCall callee_pname) ~ret ~actuals ~formals_opt astate conservatively_initialize_args (get_arg_values ()) astate
|> fun astate -> Ok [ExecutionDomain.ContinueProgram astate] |> unknown_call call_loc (SkippedKnownCall callee_pname) ~ret ~actuals ~formals_opt
in
[Ok (ContinueProgram astate)]

@ -9,11 +9,9 @@ open! IStd
open PulseBasicInterface open PulseBasicInterface
open PulseDomainInterface open PulseDomainInterface
type t = AbductiveDomain.t (** For [open]ing in other modules. *)
module Import : sig
type 'a access_result = 'a PulseReport.access_result type access_mode =
type access_mode =
| Read | Read
| Write | Write
| NoAccess | NoAccess
@ -21,6 +19,35 @@ type access_mode =
without actual memory access, for example, when evaluating [&x.f] we need to check without actual memory access, for example, when evaluating [&x.f] we need to check
initialized-ness of [x], not that of [x.f]. *) initialized-ness of [x], not that of [x.f]. *)
(** {2 Imported types for ease of use and so we can write variants without the corresponding
module prefix} *)
type 'abductive_domain_t base_t = 'abductive_domain_t ExecutionDomain.base_t =
| ContinueProgram of 'abductive_domain_t
| ExitProgram of AbductiveDomain.summary
| AbortProgram of AbductiveDomain.summary
| LatentAbortProgram of {astate: AbductiveDomain.summary; latent_issue: LatentIssue.t}
| ISLLatentMemoryError of 'abductive_domain_t
type 'a access_result = 'a PulseReport.access_result
(** {2 Monadic syntax} *)
include module type of IResult.Let_syntax
val ( let<*> ) : 'a access_result -> ('a -> 'b access_result list) -> 'b access_result list
(** monadic "bind" but not really that turns an [access_result] into a list of [access_result]s
(not really because the first type is not an [access_result list] but just an [access_result]) *)
val ( let<+> ) :
'a access_result -> ('a -> 'abductive_domain_t) -> 'abductive_domain_t base_t access_result list
(** monadic "map" but even less really that turns an [access_result] into an analysis result *)
end
include module type of Import
type t = AbductiveDomain.t
val check_addr_access : val check_addr_access :
access_mode -> Location.t -> AbstractValue.t * ValueHistory.t -> t -> t access_result access_mode -> Location.t -> AbstractValue.t * ValueHistory.t -> t -> t access_result
(** Check that the [address] is not known to be invalid *) (** Check that the [address] is not known to be invalid *)
@ -159,8 +186,6 @@ val check_address_escape :
val call : val call :
caller_proc_desc:Procdesc.t caller_proc_desc:Procdesc.t
-> Tenv.t
-> Errlog.t
-> callee_data:(Procdesc.t * PulseSummary.t) option -> callee_data:(Procdesc.t * PulseSummary.t) option
-> Location.t -> Location.t
-> Procname.t -> Procname.t
@ -168,7 +193,7 @@ val call :
-> actuals:((AbstractValue.t * ValueHistory.t) * Typ.t) list -> actuals:((AbstractValue.t * ValueHistory.t) * Typ.t) list
-> formals_opt:(Pvar.t * Typ.t) list option -> formals_opt:(Pvar.t * Typ.t) list option
-> t -> t
-> ExecutionDomain.t list access_result -> ExecutionDomain.t access_result list
(** perform an interprocedural call: apply the summary for the call proc name passed as argument if (** perform an interprocedural call: apply the summary for the call proc name passed as argument if
it exists *) it exists *)

Loading…
Cancel
Save