[pulse][4/5] add a path context to record timestamps

Summary:
Add a new `PathContext.t` component to the abstract state. For now it
tracks only the current "timestamp" of symbolic execution inside the
procedure, i.e. which step of symbolic execution we are in (bumped by 1
each time we've executed one instruction). In the future this will also
hold, eg, which conditionals we've been through on the path (for
reporting traces with that information).

Most of the diff is about propagating the path context through many of
the APIs.

We use timestamps only in `MustBeValid` attributes to report the first
incorrect access in a function call for now.

Reviewed By: skcho

Differential Revision: D28674726

fbshipit-source-id: 2cd825e73
master
Jules Villard 4 years ago committed by Facebook GitHub Bot
parent 4bad4bf63c
commit df9a7bbc81

@ -175,7 +175,7 @@ module TopLifted (Domain : S) = struct
let pp = TopLiftedUtils.pp ~pp:Domain.pp let pp = TopLiftedUtils.pp ~pp:Domain.pp
end end
module Pair (Domain1 : S) (Domain2 : S) = struct module PairNoJoin (Domain1 : NoJoin) (Domain2 : NoJoin) = struct
type t = Domain1.t * Domain2.t type t = Domain1.t * Domain2.t
let leq ~lhs ~rhs = let leq ~lhs ~rhs =
@ -183,6 +183,12 @@ module Pair (Domain1 : S) (Domain2 : S) = struct
else Domain1.leq ~lhs:(fst lhs) ~rhs:(fst rhs) && Domain2.leq ~lhs:(snd lhs) ~rhs:(snd rhs) else Domain1.leq ~lhs:(fst lhs) ~rhs:(fst rhs) && Domain2.leq ~lhs:(snd lhs) ~rhs:(snd rhs)
let pp fmt astate = Pp.pair ~fst:Domain1.pp ~snd:Domain2.pp fmt astate
end
module Pair (Domain1 : S) (Domain2 : S) = struct
include PairNoJoin (Domain1) (Domain2)
let join astate1 astate2 = let join astate1 astate2 =
if phys_equal astate1 astate2 then astate1 if phys_equal astate1 astate2 then astate1
else else
@ -199,9 +205,6 @@ module Pair (Domain1 : S) (Domain2 : S) = struct
( Domain1.widen ~prev:(fst prev) ~next:(fst next) ~num_iters ( Domain1.widen ~prev:(fst prev) ~next:(fst next) ~num_iters
, Domain2.widen ~prev:(snd prev) ~next:(snd next) ~num_iters ) , Domain2.widen ~prev:(snd prev) ~next:(snd next) ~num_iters )
prev next prev next
let pp fmt astate = Pp.pair ~fst:Domain1.pp ~snd:Domain2.pp fmt astate
end end
module Flat (V : PrettyPrintable.PrintableEquatableType) = struct module Flat (V : PrettyPrintable.PrintableEquatableType) = struct

@ -83,6 +83,8 @@ end
(** Cartesian product of two domains. *) (** Cartesian product of two domains. *)
module Pair (Domain1 : S) (Domain2 : S) : S with type t = Domain1.t * Domain2.t module Pair (Domain1 : S) (Domain2 : S) : S with type t = Domain1.t * Domain2.t
module PairNoJoin (Domain1 : NoJoin) (Domain2 : NoJoin) : NoJoin with type t = Domain1.t * Domain2.t
(** Flat abstract domain: Bottom, Top, and non-comparable elements in between *) (** Flat abstract domain: Bottom, Top, and non-comparable elements in between *)
module Flat (V : PrettyPrintable.PrintableEquatableType) : sig module Flat (V : PrettyPrintable.PrintableEquatableType) : sig
include WithBottom include WithBottom

@ -25,7 +25,7 @@ let report_topl_errors proc_desc err_log summary =
module PulseTransferFunctions = struct module PulseTransferFunctions = struct
module CFG = ProcCfg.Normal module CFG = ProcCfg.Normal
module Domain = ExecutionDomain module Domain = AbstractDomain.PairNoJoin (PathContext) (ExecutionDomain)
type analysis_data = PulseSummary.t InterproceduralAnalysis.t type analysis_data = PulseSummary.t InterproceduralAnalysis.t
@ -33,18 +33,18 @@ module PulseTransferFunctions = struct
AnalysisCallbacks.proc_resolve_attributes pname |> Option.map ~f:Pvar.get_pvar_formals AnalysisCallbacks.proc_resolve_attributes pname |> Option.map ~f:Pvar.get_pvar_formals
let interprocedural_call {InterproceduralAnalysis.analyze_dependency; tenv; proc_desc} ret let interprocedural_call {InterproceduralAnalysis.analyze_dependency; tenv; proc_desc} path ret
callee_pname call_exp actuals call_loc astate = callee_pname 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
PulseCallOperations.call tenv ~caller_proc_desc:proc_desc ~callee_data call_loc callee_pname PulseCallOperations.call tenv path ~caller_proc_desc:proc_desc ~callee_data call_loc
~ret ~actuals ~formals_opt astate callee_pname ~ret ~actuals ~formals_opt astate
| _ -> | _ ->
(* dereference call expression to catch nil issues *) (* dereference call expression to catch nil issues *)
let<*> astate, _ = let<*> astate, _ =
PulseOperations.eval_deref ~must_be_valid_reason:BlockCall call_loc call_exp astate PulseOperations.eval_deref path ~must_be_valid_reason:BlockCall call_loc call_exp astate
in in
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 =
@ -72,15 +72,15 @@ module PulseTransferFunctions = struct
(** [out_of_scope_access_expr] has just gone out of scope and in now invalid *) (** [out_of_scope_access_expr] has just gone out of scope and in now invalid *)
let exec_object_out_of_scope call_loc (pvar, typ) exec_state = let exec_object_out_of_scope path call_loc (pvar, typ) exec_state =
match (exec_state : ExecutionDomain.t) with match (exec_state : ExecutionDomain.t) with
| ContinueProgram astate -> | ContinueProgram astate ->
let gone_out_of_scope = Invalidation.GoneOutOfScope (pvar, typ) in let gone_out_of_scope = Invalidation.GoneOutOfScope (pvar, typ) in
let* astate, out_of_scope_base = let* astate, out_of_scope_base =
PulseOperations.eval NoAccess call_loc (Exp.Lvar pvar) astate PulseOperations.eval path NoAccess call_loc (Exp.Lvar pvar) astate
in in
(* invalidate [&x] *) (* invalidate [&x] *)
PulseOperations.invalidate PulseOperations.invalidate path
(StackAddress (Var.of_pvar pvar, [])) (StackAddress (Var.of_pvar pvar, []))
call_loc gone_out_of_scope out_of_scope_base astate call_loc gone_out_of_scope out_of_scope_base astate
>>| ExecutionDomain.continue >>| ExecutionDomain.continue
@ -102,7 +102,7 @@ module PulseTransferFunctions = struct
let topl_event = PulseTopl.Call {return; arguments; procname} in let topl_event = PulseTopl.Call {return; arguments; procname} in
AbductiveDomain.Topl.small_step loc topl_event astate AbductiveDomain.Topl.small_step loc topl_event astate
in in
let do_one_exec_state (exec_state : Domain.t) : Domain.t = let do_one_exec_state (exec_state : ExecutionDomain.t) : ExecutionDomain.t =
match exec_state with match exec_state with
| ContinueProgram astate -> | ContinueProgram astate ->
ContinueProgram (do_astate astate) ContinueProgram (do_astate astate)
@ -116,12 +116,12 @@ module PulseTransferFunctions = struct
List.map ~f:(Result.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 path loc ~lhs ~rhs:_ astate =
match (lhs : Exp.t) with match (lhs : Exp.t) with
| Lindex (arr, index) -> | Lindex (arr, index) ->
(let open IResult.Let_syntax in (let open IResult.Let_syntax in
let* _astate, (aw_array, _history) = PulseOperations.eval Read loc arr astate in let* _astate, (aw_array, _history) = PulseOperations.eval path Read loc arr astate in
let+ _astate, (aw_index, _history) = PulseOperations.eval Read loc index astate in let+ _astate, (aw_index, _history) = PulseOperations.eval path Read loc index astate in
let topl_event = PulseTopl.ArrayWrite {aw_array; aw_index} in let topl_event = PulseTopl.ArrayWrite {aw_array; aw_index} in
AbductiveDomain.Topl.small_step loc topl_event astate) AbductiveDomain.Topl.small_step loc topl_event astate)
|> Result.ok (* don't emit Topl event if evals fail *) |> Option.value ~default:astate |> Result.ok (* don't emit Topl event if evals fail *) |> Option.value ~default:astate
@ -129,9 +129,9 @@ module PulseTransferFunctions = struct
astate astate
let dispatch_call ({InterproceduralAnalysis.tenv; proc_desc; err_log} as analysis_data) ret let dispatch_call ({InterproceduralAnalysis.tenv; proc_desc; err_log} as analysis_data) path ret
call_exp actuals call_loc flags astate = call_exp actuals call_loc flags astate =
let<*> astate, callee_pname = PulseOperations.eval_proc_name call_loc call_exp astate in let<*> astate, callee_pname = PulseOperations.eval_proc_name path call_loc call_exp astate in
(* special case for objc dispatch models *) (* special case for objc dispatch models *)
let callee_pname, actuals = let callee_pname, actuals =
match callee_pname with match callee_pname with
@ -148,7 +148,7 @@ module PulseTransferFunctions = struct
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 path Read call_loc actual_exp astate in
( astate ( astate
, ProcnameDispatcher.Call.FuncArg. , ProcnameDispatcher.Call.FuncArg.
{exp= actual_exp; arg_payload= actual_evaled; typ= actual_typ} {exp= actual_exp; arg_payload= actual_evaled; typ= actual_typ}
@ -176,7 +176,7 @@ module PulseTransferFunctions = struct
in in
PulseCallOperations.conservatively_initialize_args arg_values astate PulseCallOperations.conservatively_initialize_args arg_values astate
in in
model {analysis_data; callee_procname; location= call_loc; ret} astate model {analysis_data; path; callee_procname; location= call_loc; ret} astate
| None -> | None ->
PerfEvent.(log (fun logger -> log_begin_event logger ~name:"pulse interproc call" ())) ; PerfEvent.(log (fun logger -> log_begin_event logger ~name:"pulse interproc call" ())) ;
let only_actuals_evaled = let only_actuals_evaled =
@ -184,7 +184,7 @@ module PulseTransferFunctions = struct
(arg_payload, typ) ) (arg_payload, typ) )
in in
let r = let r =
interprocedural_call analysis_data ret callee_pname call_exp only_actuals_evaled interprocedural_call analysis_data path ret callee_pname call_exp only_actuals_evaled
call_loc astate call_loc astate
in in
PerfEvent.(log (fun logger -> log_end_event logger ())) ; PerfEvent.(log (fun logger -> log_end_event logger ())) ;
@ -204,7 +204,7 @@ module PulseTransferFunctions = struct
| 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) ;
List.map exec_states_res ~f:(fun exec_state -> List.map exec_states_res ~f:(fun exec_state ->
exec_state >>= exec_object_out_of_scope call_loc pvar_typ ) exec_state >>= exec_object_out_of_scope path call_loc pvar_typ )
| None -> | None ->
exec_states_res exec_states_res
in in
@ -244,10 +244,11 @@ module PulseTransferFunctions = struct
is that some memory could be freed in dealloc, and we would be reporting a leak on it if we is that some memory could be freed in dealloc, and we would be reporting a leak on it if we
didn't call it. *) didn't call it. *)
let execute_injected_dealloc_calls let execute_injected_dealloc_calls
({InterproceduralAnalysis.tenv; proc_desc; err_log} as analysis_data) vars astate location = ({InterproceduralAnalysis.tenv; proc_desc; err_log} as analysis_data) path vars astate
location =
let used_ids = Stack.keys astate |> List.filter_map ~f:(fun var -> Var.get_ident var) in let used_ids = Stack.keys astate |> List.filter_map ~f:(fun var -> Var.get_ident var) in
Ident.update_name_generator used_ids ; Ident.update_name_generator used_ids ;
let call_dealloc (astate_list : Domain.t list) (ret_id, id, typ, dealloc) = let call_dealloc (astate_list : ExecutionDomain.t list) (ret_id, id, typ, dealloc) =
let ret = (ret_id, StdTyp.void) in let ret = (ret_id, StdTyp.void) in
let call_flags = CallFlags.default in let call_flags = CallFlags.default in
let call_exp = Exp.Const (Cfun dealloc) in let call_exp = Exp.Const (Cfun dealloc) in
@ -256,7 +257,7 @@ module PulseTransferFunctions = struct
L.d_printfln ~color:Pp.Orange "@\nExecuting injected instr:%a@\n@." L.d_printfln ~color:Pp.Orange "@\nExecuting injected instr:%a@\n@."
(Sil.pp_instr Pp.text ~print_types:true) (Sil.pp_instr Pp.text ~print_types:true)
call_instr ; call_instr ;
List.concat_map astate_list ~f:(fun (astate : Domain.t) -> List.concat_map astate_list ~f:(fun (astate : ExecutionDomain.t) ->
match astate with match astate with
| ISLLatentMemoryError _ | ISLLatentMemoryError _
| AbortProgram _ | AbortProgram _
@ -265,7 +266,7 @@ module PulseTransferFunctions = struct
| LatentInvalidAccess _ -> | LatentInvalidAccess _ ->
[astate] [astate]
| ContinueProgram astate -> | ContinueProgram astate ->
dispatch_call analysis_data ret call_exp actuals location call_flags astate dispatch_call analysis_data path ret call_exp actuals location call_flags astate
|> PulseReport.report_exec_results tenv proc_desc err_log location ) |> PulseReport.report_exec_results tenv proc_desc err_log location )
in in
let dynamic_types_unreachable = let dynamic_types_unreachable =
@ -280,9 +281,9 @@ module PulseTransferFunctions = struct
(astates, ret_vars) (astates, ret_vars)
let exec_instr_aux (astate : Domain.t) let exec_instr_aux path (astate : ExecutionDomain.t)
({InterproceduralAnalysis.tenv; proc_desc; err_log} as analysis_data) _cfg_node ({InterproceduralAnalysis.tenv; proc_desc; err_log} as analysis_data) _cfg_node
(instr : Sil.instr) : Domain.t list = (instr : Sil.instr) : ExecutionDomain.t list =
match astate with match astate with
| AbortProgram _ | ISLLatentMemoryError _ | LatentAbortProgram _ | LatentInvalidAccess _ -> | AbortProgram _ | ISLLatentMemoryError _ | LatentAbortProgram _ | LatentInvalidAccess _ ->
[astate] [astate]
@ -294,17 +295,17 @@ module PulseTransferFunctions = struct
| Load {id= lhs_id; e= rhs_exp; loc} -> | Load {id= lhs_id; e= rhs_exp; loc} ->
(* [lhs_id := *rhs_exp] *) (* [lhs_id := *rhs_exp] *)
let deref_rhs astate = let deref_rhs astate =
let result = let results =
if Config.pulse_isl then if Config.pulse_isl then
PulseOperations.eval_deref_isl loc rhs_exp astate PulseOperations.eval_deref_isl path loc rhs_exp astate
|> List.map ~f:(fun result -> |> List.map ~f:(fun result ->
let+ astate, rhs_addr_hist = result in let+ astate, rhs_addr_hist = result in
PulseOperations.write_id lhs_id rhs_addr_hist astate ) PulseOperations.write_id lhs_id rhs_addr_hist astate )
else else
[ (let+ astate, rhs_addr_hist = PulseOperations.eval_deref loc rhs_exp astate in [ (let+ astate, rhs_addr_hist = PulseOperations.eval_deref path loc rhs_exp astate in
PulseOperations.write_id lhs_id rhs_addr_hist astate) ] PulseOperations.write_id lhs_id rhs_addr_hist astate) ]
in in
PulseReport.report_results tenv proc_desc err_log loc result PulseReport.report_results tenv proc_desc err_log loc results
in in
let set_global_astates = let set_global_astates =
match rhs_exp with match rhs_exp with
@ -320,7 +321,7 @@ module PulseTransferFunctions = struct
let call_flags = CallFlags.default in let call_flags = CallFlags.default in
let ret_id_void = (Ident.create_fresh Ident.knormal, StdTyp.void) in let ret_id_void = (Ident.create_fresh Ident.knormal, StdTyp.void) in
let no_error_states = let no_error_states =
dispatch_call analysis_data ret_id_void (Const (Cfun proc_name)) [] loc dispatch_call analysis_data path ret_id_void (Const (Cfun proc_name)) [] loc
call_flags astate call_flags astate
|> List.filter_map ~f:(function |> List.filter_map ~f:(function
| Ok (ContinueProgram astate) -> | Ok (ContinueProgram astate) ->
@ -347,21 +348,22 @@ module PulseTransferFunctions = struct
in in
let result = let result =
let<*> astate, (rhs_addr, rhs_history) = let<*> astate, (rhs_addr, rhs_history) =
PulseOperations.eval NoAccess loc rhs_exp astate PulseOperations.eval path NoAccess loc rhs_exp astate
in in
let<*> is_structured, ls_astate_lhs_addr_hist = let<*> is_structured, ls_astate_lhs_addr_hist =
if Config.pulse_isl then PulseOperations.eval_structure_isl Write loc lhs_exp astate if Config.pulse_isl then
PulseOperations.eval_structure_isl path Write loc lhs_exp astate
else else
let+ astate, lhs_addr_hist = PulseOperations.eval Write loc lhs_exp astate in let+ astate, lhs_addr_hist = PulseOperations.eval path Write loc lhs_exp astate in
(false, [Ok (astate, lhs_addr_hist)]) (false, [Ok (astate, lhs_addr_hist)])
in in
let write_function lhs_addr_hist astate = let write_function lhs_addr_hist astate =
if is_structured then if is_structured then
PulseOperations.write_deref_biad_isl loc ~ref:lhs_addr_hist Dereference PulseOperations.write_deref_biad_isl path loc ~ref:lhs_addr_hist Dereference
~obj:(rhs_addr, event :: rhs_history) ~obj:(rhs_addr, event :: rhs_history)
astate astate
else else
[ PulseOperations.write_deref loc ~ref:lhs_addr_hist [ PulseOperations.write_deref path loc ~ref:lhs_addr_hist
~obj:(rhs_addr, event :: rhs_history) ~obj:(rhs_addr, event :: rhs_history)
astate ] astate ]
in in
@ -374,7 +376,7 @@ module PulseTransferFunctions = struct
if Topl.is_active () then if Topl.is_active () then
List.map astates ~f:(fun result -> List.map astates ~f:(fun result ->
let+ astate = result in let+ astate = result in
topl_store_step loc ~lhs:lhs_exp ~rhs:rhs_exp astate ) topl_store_step path loc ~lhs:lhs_exp ~rhs:rhs_exp astate )
else astates else astates
in in
match lhs_exp with match lhs_exp with
@ -387,7 +389,7 @@ module PulseTransferFunctions = struct
in in
PulseReport.report_results tenv proc_desc err_log loc result PulseReport.report_results tenv proc_desc err_log loc 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 path 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 *)
[] []
@ -396,11 +398,11 @@ module PulseTransferFunctions = struct
[Ok (ContinueProgram astate)]) [Ok (ContinueProgram astate)])
|> PulseReport.report_exec_results tenv proc_desc err_log loc |> PulseReport.report_exec_results tenv proc_desc err_log loc
| 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 path ret call_exp actuals loc call_flags astate
|> PulseReport.report_exec_results tenv proc_desc err_log loc |> PulseReport.report_exec_results tenv proc_desc err_log loc
| Metadata (ExitScope (vars, location)) -> | Metadata (ExitScope (vars, location)) ->
let remove_vars vars astates = let remove_vars vars astates =
List.map astates ~f:(fun (exec_state : Domain.t) -> List.map astates ~f:(fun (exec_state : ExecutionDomain.t) ->
match exec_state with match exec_state with
| ISLLatentMemoryError _ | ISLLatentMemoryError _
| AbortProgram _ | AbortProgram _
@ -417,7 +419,7 @@ module PulseTransferFunctions = struct
(* 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 *)
let astates, ret_vars = let astates, ret_vars =
execute_injected_dealloc_calls analysis_data vars astate location execute_injected_dealloc_calls analysis_data path vars astate location
in in
(* OPTIM: avoid re-allocating [vars] when [ret_vars] is empty (* OPTIM: avoid re-allocating [vars] when [ret_vars] is empty
(in particular if no ObjC objects are involved), but otherwise (in particular if no ObjC objects are involved), but otherwise
@ -428,7 +430,7 @@ module PulseTransferFunctions = struct
in in
remove_vars vars_to_remove astates remove_vars vars_to_remove astates
| 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 tenv pvar typ location astate |> Domain.continue] [PulseOperations.realloc_pvar tenv pvar typ location astate |> ExecutionDomain.continue]
| Metadata | Metadata
( Abstract _ ( Abstract _
| CatchEntry _ | CatchEntry _
@ -440,11 +442,13 @@ module PulseTransferFunctions = struct
[ContinueProgram astate] ) [ContinueProgram astate] )
let exec_instr astate analysis_data cfg_node instr = let exec_instr (path, astate) analysis_data cfg_node instr : Domain.t list =
(* Sometimes instead of stopping on contradictions a false path condition is recorded (* Sometimes instead of stopping on contradictions a false path condition is recorded
instead. Prune these early here so they don't spuriously count towards the disjunct limit. *) instead. Prune these early here so they don't spuriously count towards the disjunct limit. *)
exec_instr_aux astate analysis_data cfg_node instr exec_instr_aux path astate analysis_data cfg_node instr
|> List.filter ~f:(fun exec_state -> not (Domain.is_unsat_cheap exec_state)) |> List.filter_map ~f:(fun exec_state ->
if ExecutionDomain.is_unsat_cheap exec_state then None
else Some (PathContext.post_exec_instr path, exec_state) )
let pp_session_name _node fmt = F.pp_print_string fmt "Pulse" let pp_session_name _node fmt = F.pp_print_string fmt "Pulse"
@ -469,9 +473,11 @@ let with_debug_exit_node proc_desc ~f =
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 let initial_astate = ExecutionDomain.mk_initial tenv proc_desc in
let initial = [initial_astate] in let initial = [(PathContext.initial, initial_astate)] in
match DisjunctiveAnalyzer.compute_post analysis_data ~initial proc_desc with match DisjunctiveAnalyzer.compute_post analysis_data ~initial proc_desc with
| Some posts -> | Some posts ->
(* forget path contexts, we don't propagate them across functions *)
let posts = List.map ~f:snd posts in
with_debug_exit_node proc_desc ~f:(fun () -> with_debug_exit_node proc_desc ~f:(fun () ->
let updated_posts = let updated_posts =
PulseObjectiveCSummary.update_objc_method_posts analysis_data ~initial_astate ~posts PulseObjectiveCSummary.update_objc_method_posts analysis_data ~initial_astate ~posts

@ -143,7 +143,7 @@ module Stack = struct
if phys_equal new_post astate.post then astate else {astate with post= new_post} if phys_equal new_post astate.post then astate else {astate with post= new_post}
let eval location origin var astate = let eval path location origin var astate =
match BaseStack.find_opt var (astate.post :> base_domain).stack with match BaseStack.find_opt var (astate.post :> base_domain).stack with
| Some addr_hist -> | Some addr_hist ->
(astate, addr_hist) (astate, addr_hist)
@ -160,7 +160,7 @@ module Stack = struct
if Config.pulse_isl then if Config.pulse_isl then
let access_trace = Trace.Immediate {location; history= []} in let access_trace = Trace.Immediate {location; history= []} in
BaseAddressAttributes.add_one addr BaseAddressAttributes.add_one addr
(MustBeValid (access_trace, None)) (MustBeValid (path.PathContext.timestamp, access_trace, None))
(astate.post :> base_domain).attrs (astate.post :> base_domain).attrs
else (astate.post :> base_domain).attrs else (astate.post :> base_domain).attrs
in in
@ -236,10 +236,12 @@ module AddressAttributes = struct
if phys_equal new_pre astate.pre then astate else {astate with pre= new_pre} if phys_equal new_pre astate.pre then astate else {astate with pre= new_pre}
let check_valid ?must_be_valid_reason access_trace addr astate = let check_valid path ?must_be_valid_reason access_trace addr astate =
let+ () = BaseAddressAttributes.check_valid addr (astate.post :> base_domain).attrs in let+ () = BaseAddressAttributes.check_valid addr (astate.post :> base_domain).attrs in
(* if [address] is in [pre] and it should be valid then that fact goes in the precondition *) (* if [address] is in [pre] and it should be valid then that fact goes in the precondition *)
abduce_attribute addr (MustBeValid (access_trace, must_be_valid_reason)) astate abduce_attribute addr
(MustBeValid (path.PathContext.timestamp, access_trace, must_be_valid_reason))
astate
let check_initialized access_trace addr astate = let check_initialized access_trace addr astate =
@ -323,11 +325,11 @@ module AddressAttributes = struct
map_post_attrs astate ~f:(BaseAddressAttributes.add_unreachable_at addr location) map_post_attrs astate ~f:(BaseAddressAttributes.add_unreachable_at addr location)
let replace_must_be_valid_reason reason addr astate = let replace_must_be_valid_reason path reason addr astate =
match BaseAddressAttributes.get_must_be_valid addr (astate.pre :> base_domain).attrs with match BaseAddressAttributes.get_must_be_valid addr (astate.pre :> base_domain).attrs with
| Some (trace, _reason) -> | Some (_timestamp, trace, _reason) ->
remove_must_be_valid_attr addr astate remove_must_be_valid_attr addr astate
|> abduce_attribute addr (MustBeValid (trace, Some reason)) |> abduce_attribute addr (MustBeValid (path.PathContext.timestamp, trace, Some reason))
| None -> | None ->
astate astate
@ -365,7 +367,7 @@ module AddressAttributes = struct
BaseAddressAttributes.find_opt address (astate.post :> base_domain).attrs BaseAddressAttributes.find_opt address (astate.post :> base_domain).attrs
let check_valid_isl access_trace addr ?(null_noop = false) astate = let check_valid_isl path access_trace addr ?(null_noop = false) astate =
L.d_printfln "*****check_valid_isl: addr*** %a@\n" AbstractValue.pp addr ; L.d_printfln "*****check_valid_isl: addr*** %a@\n" AbstractValue.pp addr ;
match BaseAddressAttributes.get_invalid addr (astate.post :> BaseDomain.t).attrs with match BaseAddressAttributes.get_invalid addr (astate.post :> BaseDomain.t).attrs with
| None -> | None ->
@ -389,7 +391,9 @@ module AddressAttributes = struct
else else
let valid_astate = let valid_astate =
let abdalloc = Attribute.ISLAbduced access_trace in let abdalloc = Attribute.ISLAbduced access_trace in
let valid_attr = Attribute.MustBeValid (access_trace, None) in let valid_attr =
Attribute.MustBeValid (path.PathContext.timestamp, access_trace, None)
in
add_one addr abdalloc astate |> abduce_attribute addr valid_attr add_one addr abdalloc astate |> abduce_attribute addr valid_attr
|> abduce_attribute addr abdalloc |> abduce_attribute addr abdalloc
in in
@ -476,7 +480,7 @@ let rec set_uninitialized_post tenv src typ location ?(fields_prefix = RevList.e
let attrs = let attrs =
if Config.pulse_isl then if Config.pulse_isl then
BaseAddressAttributes.add_one addr BaseAddressAttributes.add_one addr
(MustBeValid (Immediate {location; history= []}, None)) (MustBeValid (PathContext.t0, Immediate {location; history= []}, None))
attrs attrs
else attrs else attrs
in in
@ -562,7 +566,7 @@ let mk_initial tenv proc_desc =
List.fold formals_and_captured ~init:(PreDomain.empty :> base_domain).attrs List.fold formals_and_captured ~init:(PreDomain.empty :> base_domain).attrs
~f:(fun attrs (_, _, (addr, _)) -> ~f:(fun attrs (_, _, (addr, _)) ->
BaseAddressAttributes.add_one addr BaseAddressAttributes.add_one addr
(MustBeValid (Immediate {location; history= []}, None)) (MustBeValid (PathContext.t0, Immediate {location; history= []}, None))
attrs ) attrs )
else BaseDomain.empty.attrs else BaseDomain.empty.attrs
in in
@ -941,8 +945,8 @@ let incorporate_new_eqs astate new_eqs =
L.d_printfln "Not clear why %a should be allocated in the first place, giving up" L.d_printfln "Not clear why %a should be allocated in the first place, giving up"
AbstractValue.pp v ; AbstractValue.pp v ;
Stop Unsat Stop Unsat
| Some must_be_valid -> | Some (_, trace, reason_opt) ->
Stop (Sat (astate, Some (v, must_be_valid))) ) Stop (Sat (astate, Some (v, (trace, reason_opt)))) )
| EqZero _ (* [v] not allocated *) -> | EqZero _ (* [v] not allocated *) ->
Continue (astate, error) ) Continue (astate, error) )

@ -68,7 +68,13 @@ module Stack : sig
val find_opt : Var.t -> t -> BaseStack.value option val find_opt : Var.t -> t -> BaseStack.value option
val eval : Location.t -> ValueHistory.t -> Var.t -> t -> t * (AbstractValue.t * ValueHistory.t) val eval :
PathContext.t
-> Location.t
-> ValueHistory.t
-> Var.t
-> t
-> t * (AbstractValue.t * ValueHistory.t)
(** return the value of the variable in the stack or create a fresh one if needed *) (** return the value of the variable in the stack or create a fresh one if needed *)
val mem : Var.t -> t -> bool val mem : Var.t -> t -> bool
@ -114,7 +120,8 @@ module AddressAttributes : sig
val add_attrs : AbstractValue.t -> Attributes.t -> t -> t val add_attrs : AbstractValue.t -> Attributes.t -> t -> t
val check_valid : val check_valid :
?must_be_valid_reason:Invalidation.must_be_valid_reason PathContext.t
-> ?must_be_valid_reason:Invalidation.must_be_valid_reason
-> Trace.t -> Trace.t
-> AbstractValue.t -> AbstractValue.t
-> t -> t
@ -124,7 +131,8 @@ module AddressAttributes : sig
val invalidate : AbstractValue.t * ValueHistory.t -> Invalidation.t -> Location.t -> t -> t val invalidate : AbstractValue.t * ValueHistory.t -> Invalidation.t -> Location.t -> t -> t
val replace_must_be_valid_reason : Invalidation.must_be_valid_reason -> AbstractValue.t -> t -> t val replace_must_be_valid_reason :
PathContext.t -> Invalidation.must_be_valid_reason -> AbstractValue.t -> t -> t
val allocate : Procname.t -> AbstractValue.t * ValueHistory.t -> Location.t -> t -> t val allocate : Procname.t -> AbstractValue.t * ValueHistory.t -> Location.t -> t -> t
@ -149,7 +157,8 @@ module AddressAttributes : sig
val find_opt : AbstractValue.t -> t -> Attributes.t option val find_opt : AbstractValue.t -> t -> Attributes.t option
val check_valid_isl : val check_valid_isl :
Trace.t PathContext.t
-> Trace.t
-> AbstractValue.t -> AbstractValue.t
-> ?null_noop:bool -> ?null_noop:bool
-> t -> t

@ -7,6 +7,7 @@
open! IStd open! IStd
module F = Format module F = Format
module Invalidation = PulseInvalidation module Invalidation = PulseInvalidation
module PathContext = PulsePathContext
module Trace = PulseTrace module Trace = PulseTrace
module ValueHistory = PulseValueHistory module ValueHistory = PulseValueHistory
@ -34,7 +35,7 @@ module Attribute = struct
| Invalid of Invalidation.t * Trace.t | Invalid of Invalidation.t * Trace.t
| ISLAbduced of Trace.t | ISLAbduced of Trace.t
| MustBeInitialized of Trace.t | MustBeInitialized of Trace.t
| MustBeValid of Trace.t * Invalidation.must_be_valid_reason option | MustBeValid of PathContext.timestamp * Trace.t * Invalidation.must_be_valid_reason option
| StdVectorReserve | StdVectorReserve
| Uninitialized | Uninitialized
| UnreachableAt of Location.t | UnreachableAt of Location.t
@ -64,7 +65,7 @@ module Attribute = struct
Variants.to_rank (Invalid (Invalidation.ConstantDereference IntLit.zero, dummy_trace)) Variants.to_rank (Invalid (Invalidation.ConstantDereference IntLit.zero, dummy_trace))
let must_be_valid_rank = Variants.to_rank (MustBeValid (dummy_trace, None)) let must_be_valid_rank = Variants.to_rank (MustBeValid (PathContext.t0, dummy_trace, None))
let std_vector_reserve_rank = Variants.to_rank StdVectorReserve let std_vector_reserve_rank = Variants.to_rank StdVectorReserve
@ -128,10 +129,11 @@ module Attribute = struct
F.fprintf f "MustBeInitialized %a" F.fprintf f "MustBeInitialized %a"
(Trace.pp ~pp_immediate:(pp_string_if_debug "read")) (Trace.pp ~pp_immediate:(pp_string_if_debug "read"))
trace trace
| MustBeValid (trace, reason) -> | MustBeValid (timestamp, trace, reason) ->
F.fprintf f "MustBeValid %a (%a)" F.fprintf f "MustBeValid(%a, %a, t=%d)"
(Trace.pp ~pp_immediate:(pp_string_if_debug "access")) (Trace.pp ~pp_immediate:(pp_string_if_debug "access"))
trace Invalidation.pp_must_be_valid_reason reason trace Invalidation.pp_must_be_valid_reason reason
(timestamp :> int)
| StdVectorReserve -> | StdVectorReserve ->
F.pp_print_string f "std::vector::reserve()" F.pp_print_string f "std::vector::reserve()"
| Uninitialized -> | Uninitialized ->
@ -176,7 +178,7 @@ module Attribute = struct
true true
let add_call proc_name call_location caller_history attr = let add_call path proc_name call_location caller_history attr =
let add_call_to_trace in_call = let add_call_to_trace in_call =
Trace.ViaCall {f= Call proc_name; location= call_location; history= caller_history; in_call} Trace.ViaCall {f= Call proc_name; location= call_location; history= caller_history; in_call}
in in
@ -197,8 +199,8 @@ module Attribute = struct
Invalid (invalidation, add_call_to_trace trace) Invalid (invalidation, add_call_to_trace trace)
| ISLAbduced trace -> | ISLAbduced trace ->
ISLAbduced (add_call_to_trace trace) ISLAbduced (add_call_to_trace trace)
| MustBeValid (trace, reason) -> | MustBeValid (_timestamp, trace, reason) ->
MustBeValid (add_call_to_trace trace, reason) MustBeValid (path.PathContext.timestamp, add_call_to_trace trace, reason)
| MustBeInitialized trace -> | MustBeInitialized trace ->
MustBeInitialized (add_call_to_trace trace) MustBeInitialized (add_call_to_trace trace)
| WrittenTo trace -> | WrittenTo trace ->
@ -227,8 +229,8 @@ module Attributes = struct
let get_must_be_valid attrs = let get_must_be_valid attrs =
Set.find_rank attrs Attribute.must_be_valid_rank Set.find_rank attrs Attribute.must_be_valid_rank
|> Option.map ~f:(fun attr -> |> Option.map ~f:(fun attr ->
let[@warning "-8"] (Attribute.MustBeValid (trace, reason)) = attr in let[@warning "-8"] (Attribute.MustBeValid (timestamp, trace, reason)) = attr in
(trace, reason) ) (timestamp, trace, reason) )
let get_written_to attrs = let get_written_to attrs =
@ -335,8 +337,9 @@ module Attributes = struct
Set.add acc attr1 ) Set.add acc attr1 )
let add_call proc_name call_location caller_history attrs = let add_call path proc_name call_location caller_history attrs =
Set.map attrs ~f:(fun attr -> Attribute.add_call proc_name call_location caller_history attr) Set.map attrs ~f:(fun attr ->
Attribute.add_call path proc_name call_location caller_history attr )
include Set include Set

@ -7,6 +7,7 @@
open! IStd open! IStd
module F = Format module F = Format
module Invalidation = PulseInvalidation module Invalidation = PulseInvalidation
module PathContext = PulsePathContext
module Trace = PulseTrace module Trace = PulseTrace
module ValueHistory = PulseValueHistory module ValueHistory = PulseValueHistory
@ -21,7 +22,7 @@ type t =
| Invalid of Invalidation.t * Trace.t | Invalid of Invalidation.t * Trace.t
| ISLAbduced of Trace.t (** The allocation is abduced so as the analysis could run normally *) | ISLAbduced of Trace.t (** The allocation is abduced so as the analysis could run normally *)
| MustBeInitialized of Trace.t | MustBeInitialized of Trace.t
| MustBeValid of Trace.t * Invalidation.must_be_valid_reason option | MustBeValid of PathContext.timestamp * Trace.t * Invalidation.must_be_valid_reason option
| StdVectorReserve | StdVectorReserve
| Uninitialized | Uninitialized
| UnreachableAt of Location.t | UnreachableAt of Location.t
@ -53,7 +54,8 @@ module Attributes : sig
val get_isl_abduced : t -> Trace.t option val get_isl_abduced : t -> Trace.t option
val get_must_be_valid : t -> (Trace.t * Invalidation.must_be_valid_reason option) option val get_must_be_valid :
t -> (PathContext.timestamp * Trace.t * Invalidation.must_be_valid_reason option) option
val get_written_to : t -> Trace.t option val get_written_to : t -> Trace.t option
@ -75,5 +77,5 @@ module Attributes : sig
(** While applying a spec, replacing ISLAbduced by Allocated and Invalidation.Cfree by (** While applying a spec, replacing ISLAbduced by Allocated and Invalidation.Cfree by
Invalidation.delete, if applicable *) Invalidation.delete, if applicable *)
val add_call : Procname.t -> Location.t -> ValueHistory.t -> t -> t val add_call : PathContext.t -> Procname.t -> Location.t -> ValueHistory.t -> t -> t
end end

@ -118,8 +118,8 @@ let remove_isl_abduced_attr address memory =
let remove_must_be_valid_attr address memory = let remove_must_be_valid_attr address memory =
match get_attribute Attributes.get_must_be_valid address memory with match get_attribute Attributes.get_must_be_valid address memory with
| Some (trace, reason) -> | Some (timestamp, trace, reason) ->
remove_one address (Attribute.MustBeValid (trace, reason)) memory remove_one address (Attribute.MustBeValid (timestamp, trace, reason)) memory
| None -> | None ->
memory memory

@ -42,7 +42,9 @@ val get_closure_proc_name : AbstractValue.t -> t -> Procname.t option
val get_invalid : AbstractValue.t -> t -> (Invalidation.t * Trace.t) option val get_invalid : AbstractValue.t -> t -> (Invalidation.t * Trace.t) option
val get_must_be_valid : val get_must_be_valid :
AbstractValue.t -> t -> (Trace.t * Invalidation.must_be_valid_reason option) option AbstractValue.t
-> t
-> (PathContext.timestamp * Trace.t * Invalidation.must_be_valid_reason option) option
val is_must_be_valid_or_allocated_isl : AbstractValue.t -> t -> bool val is_must_be_valid_or_allocated_isl : AbstractValue.t -> t -> bool

@ -16,6 +16,7 @@ module CallEvent = PulseCallEvent
module Diagnostic = PulseDiagnostic module Diagnostic = PulseDiagnostic
module Invalidation = PulseInvalidation module Invalidation = PulseInvalidation
module PathCondition = PulsePathCondition module PathCondition = PulsePathCondition
module PathContext = PulsePathContext
module SatUnsat = PulseSatUnsat module SatUnsat = PulseSatUnsat
module SkippedCalls = PulseSkippedCalls module SkippedCalls = PulseSkippedCalls
module Trace = PulseTrace module Trace = PulseTrace
@ -36,6 +37,7 @@ include struct
[@@deprecated "use the short form Invalidation instead"] [@@deprecated "use the short form Invalidation instead"]
module PulsePathCondition = PulsePathCondition module PulsePathCondition = PulsePathCondition
[@@deprecated "use the short form PathCondition instead"] [@@deprecated "use the short form PathCondition instead"]
module PulsePathContext = PulsePathContext [@@deprecated "use the short form PathContext instead"]
module PulseSkippedCalls = PulseSkippedCalls module PulseSkippedCalls = PulseSkippedCalls
[@@deprecated "use the short form SkippedCalls instead"] [@@deprecated "use the short form SkippedCalls instead"]
module PulseTrace = PulseTrace [@@deprecated "use the short form Trace instead"] module PulseTrace = PulseTrace [@@deprecated "use the short form Trace instead"]

@ -83,11 +83,11 @@ let unknown_call tenv call_loc reason ~ret ~actuals ~formals_opt astate =
|> havoc_ret ret |> add_skipped_proc |> havoc_ret ret |> add_skipped_proc
let apply_callee tenv ~caller_proc_desc callee_pname call_loc callee_exec_state ~ret let apply_callee tenv path ~caller_proc_desc callee_pname call_loc callee_exec_state ~ret
~captured_vars_with_actuals ~formals ~actuals astate = ~captured_vars_with_actuals ~formals ~actuals astate =
let map_call_result ~is_isl_error_prepost callee_prepost ~f = let map_call_result ~is_isl_error_prepost callee_prepost ~f =
match match
PulseInterproc.apply_prepost ~is_isl_error_prepost callee_pname call_loc ~callee_prepost PulseInterproc.apply_prepost path ~is_isl_error_prepost callee_pname call_loc ~callee_prepost
~captured_vars_with_actuals ~formals ~actuals astate ~captured_vars_with_actuals ~formals ~actuals astate
with with
| (Sat (Error _) | Unsat) as path_result -> | (Sat (Error _) | Unsat) as path_result ->
@ -194,8 +194,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_aux tenv caller_proc_desc call_loc callee_pname ret actuals callee_proc_desc exec_states let call_aux tenv path caller_proc_desc call_loc callee_pname ret actuals callee_proc_desc
(astate : AbductiveDomain.t) = exec_states (astate : AbductiveDomain.t) =
let formals = let formals =
Procdesc.get_formals callee_proc_desc Procdesc.get_formals callee_proc_desc
|> List.map ~f:(fun (mangled, _) -> Pvar.mk mangled callee_pname |> Var.of_pvar) |> List.map ~f:(fun (mangled, _) -> Pvar.mk mangled callee_pname |> Var.of_pvar)
@ -211,7 +211,7 @@ let call_aux tenv caller_proc_desc call_loc callee_pname ret actuals callee_proc
| (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) ->
(* Assumption: the first parameter will be a closure *) (* Assumption: the first parameter will be a closure *)
PulseOperations.get_captured_actuals call_loc ~captured_vars ~actual_closure astate PulseOperations.get_captured_actuals path call_loc ~captured_vars ~actual_closure astate
| _ -> | _ ->
Ok (astate, []) Ok (astate, [])
in in
@ -228,7 +228,7 @@ let call_aux tenv caller_proc_desc call_loc callee_pname ret actuals callee_proc
else else
(* apply all pre/post specs *) (* apply all pre/post specs *)
match match
apply_callee tenv ~caller_proc_desc callee_pname call_loc callee_exec_state apply_callee tenv path ~caller_proc_desc callee_pname call_loc callee_exec_state
~captured_vars_with_actuals ~formals ~actuals ~ret astate ~captured_vars_with_actuals ~formals ~actuals ~ret astate
with with
| Unsat -> | Unsat ->
@ -238,7 +238,7 @@ let call_aux tenv caller_proc_desc call_loc callee_pname ret actuals callee_proc
post :: posts ) post :: posts )
let call tenv ~caller_proc_desc ~(callee_data : (Procdesc.t * PulseSummary.t) option) call_loc let call tenv path ~caller_proc_desc ~(callee_data : (Procdesc.t * PulseSummary.t) option) call_loc
callee_pname ~ret ~actuals ~formals_opt (astate : AbductiveDomain.t) = callee_pname ~ret ~actuals ~formals_opt (astate : AbductiveDomain.t) =
(* a special case for objc nil messaging *) (* a special case for objc nil messaging *)
let unknown_objc_nil_messaging astate_unknown procdesc = let unknown_objc_nil_messaging astate_unknown procdesc =
@ -254,7 +254,7 @@ let call tenv ~caller_proc_desc ~(callee_data : (Procdesc.t * PulseSummary.t) op
(ExecutionDomain.mk_initial tenv procdesc) (ExecutionDomain.mk_initial tenv procdesc)
|> Option.value_map ~default:[] ~f:(fun nil_summary -> |> Option.value_map ~default:[] ~f:(fun nil_summary ->
let<*> nil_astate = nil_summary in let<*> nil_astate = nil_summary in
call_aux tenv caller_proc_desc call_loc callee_pname ret actuals procdesc call_aux tenv path caller_proc_desc call_loc callee_pname ret actuals procdesc
([ContinueProgram nil_astate] :> ExecutionDomain.t list) ([ContinueProgram nil_astate] :> ExecutionDomain.t list)
astate ) astate )
in in
@ -262,7 +262,7 @@ let call tenv ~caller_proc_desc ~(callee_data : (Procdesc.t * PulseSummary.t) op
in in
match callee_data with match callee_data with
| Some (callee_proc_desc, exec_states) -> | Some (callee_proc_desc, exec_states) ->
call_aux tenv caller_proc_desc call_loc callee_pname ret actuals callee_proc_desc call_aux tenv path caller_proc_desc call_loc callee_pname ret actuals callee_proc_desc
(exec_states :> ExecutionDomain.t list) (exec_states :> ExecutionDomain.t list)
astate astate
| None -> | None ->

@ -13,6 +13,7 @@ type t = AbductiveDomain.t
val call : val call :
Tenv.t Tenv.t
-> PathContext.t
-> caller_proc_desc:Procdesc.t -> caller_proc_desc:Procdesc.t
-> callee_data:(Procdesc.t * PulseSummary.t) option -> callee_data:(Procdesc.t * PulseSummary.t) option
-> Location.t -> Location.t

@ -84,14 +84,14 @@ let pp_contradiction fmt = function
exception Contradiction of contradiction exception Contradiction of contradiction
let fold_globals_of_stack call_loc stack call_state ~f = let fold_globals_of_stack path call_loc stack call_state ~f =
Container.fold_result ~fold:(IContainer.fold_of_pervasives_map_fold BaseStack.fold) Container.fold_result ~fold:(IContainer.fold_of_pervasives_map_fold BaseStack.fold)
stack ~init:call_state ~f:(fun call_state (var, stack_value) -> stack ~init:call_state ~f:(fun call_state (var, stack_value) ->
match var with match var with
| Var.ProgramVar pvar when Pvar.is_global pvar -> | Var.ProgramVar pvar when Pvar.is_global pvar ->
let call_state, addr_hist_caller = let call_state, addr_hist_caller =
let astate, var_value = let astate, var_value =
Stack.eval call_loc Stack.eval path call_loc
[ValueHistory.VariableAccessed (pvar, call_loc)] [ValueHistory.VariableAccessed (pvar, call_loc)]
var call_state.astate var call_state.astate
in in
@ -252,8 +252,8 @@ let materialize_pre_for_parameters callee_proc_name call_location pre_post ~form
result result
let materialize_pre_for_globals callee_proc_name call_location pre_post call_state = let materialize_pre_for_globals path callee_proc_name call_location pre_post call_state =
fold_globals_of_stack call_location (pre_post.AbductiveDomain.pre :> BaseDomain.t).stack fold_globals_of_stack path call_location (pre_post.AbductiveDomain.pre :> BaseDomain.t).stack
call_state ~f:(fun _var ~stack_value:(addr_pre, _) ~addr_hist_caller call_state -> call_state ~f:(fun _var ~stack_value:(addr_pre, _) ~addr_hist_caller call_state ->
materialize_pre_from_address callee_proc_name call_location materialize_pre_from_address callee_proc_name call_location
~pre:(pre_post.AbductiveDomain.pre :> BaseDomain.t) ~pre:(pre_post.AbductiveDomain.pre :> BaseDomain.t)
@ -281,11 +281,11 @@ let conjoin_callee_arith pre_post call_state =
else {call_state with astate; subst} else {call_state with astate; subst}
let apply_arithmetic_constraints callee_proc_name call_location pre_post call_state = let apply_arithmetic_constraints path callee_proc_name call_location pre_post call_state =
let open IResult.Let_syntax in let open IResult.Let_syntax in
let one_address_sat callee_attrs (addr_caller, caller_history) call_state = let one_address_sat callee_attrs (addr_caller, caller_history) call_state =
let attrs_caller = let attrs_caller =
Attributes.add_call callee_proc_name call_location caller_history callee_attrs Attributes.add_call path callee_proc_name call_location caller_history callee_attrs
in in
let astate = AddressAttributes.abduce_and_add addr_caller attrs_caller call_state.astate in let astate = AddressAttributes.abduce_and_add addr_caller attrs_caller call_state.astate in
if phys_equal astate call_state.astate then call_state else {call_state with astate} if phys_equal astate call_state.astate then call_state else {call_state with astate}
@ -332,8 +332,8 @@ let apply_arithmetic_constraints callee_proc_name call_location pre_post call_st
call_state.subst call_state call_state.subst call_state
let materialize_pre callee_proc_name call_location pre_post ~captured_vars_with_actuals ~formals let materialize_pre path callee_proc_name call_location pre_post ~captured_vars_with_actuals
~actuals call_state = ~formals ~actuals call_state =
PerfEvent.(log (fun logger -> log_begin_event logger ~name:"pulse call pre" ())) ; PerfEvent.(log (fun logger -> log_begin_event logger ~name:"pulse call pre" ())) ;
let r = let r =
let open IResult.Let_syntax in let open IResult.Let_syntax in
@ -342,10 +342,10 @@ let materialize_pre callee_proc_name call_location pre_post ~captured_vars_with_
call_state call_state
>>= materialize_pre_for_captured_vars callee_proc_name call_location pre_post >>= materialize_pre_for_captured_vars callee_proc_name call_location pre_post
~captured_vars_with_actuals ~captured_vars_with_actuals
>>= materialize_pre_for_globals callee_proc_name call_location pre_post >>= materialize_pre_for_globals path callee_proc_name call_location pre_post
>>= (* ...then relational arithmetic constraints in the callee's attributes will make sense in >>= (* ...then relational arithmetic constraints in the callee's attributes will make sense in
terms of the caller's values *) terms of the caller's values *)
apply_arithmetic_constraints callee_proc_name call_location pre_post apply_arithmetic_constraints path callee_proc_name call_location pre_post
in in
PerfEvent.(log (fun logger -> log_end_event logger ())) ; PerfEvent.(log (fun logger -> log_end_event logger ())) ;
r r
@ -386,11 +386,11 @@ let delete_edges_in_callee_pre_from_caller ~edges_pre_opt addr_caller call_state
(subst, post_edges) ) (subst, post_edges) )
let record_post_cell callee_proc_name call_loc ~edges_pre_opt let record_post_cell path callee_proc_name call_loc ~edges_pre_opt
~cell_callee_post:(edges_callee_post, attrs_callee_post) (addr_caller, hist_caller) call_state = ~cell_callee_post:(edges_callee_post, attrs_callee_post) (addr_caller, hist_caller) call_state =
let call_state = let call_state =
let attrs_post_caller = let attrs_post_caller =
Attributes.add_call callee_proc_name call_loc hist_caller attrs_callee_post Attributes.add_call path callee_proc_name call_loc hist_caller attrs_callee_post
in in
let astate = let astate =
if Config.pulse_isl then if Config.pulse_isl then
@ -427,8 +427,8 @@ let record_post_cell callee_proc_name call_loc ~edges_pre_opt
; astate= AbductiveDomain.set_post_edges addr_caller edges_post_caller call_state.astate } ; astate= AbductiveDomain.set_post_edges addr_caller edges_post_caller call_state.astate }
let rec record_post_for_address callee_proc_name call_loc ({AbductiveDomain.pre; _} as pre_post) let rec record_post_for_address path callee_proc_name call_loc
~addr_callee ~addr_hist_caller call_state = ({AbductiveDomain.pre; _} as pre_post) ~addr_callee ~addr_hist_caller call_state =
L.d_printfln "%a<->%a" AbstractValue.pp addr_callee AbstractValue.pp (fst addr_hist_caller) ; L.d_printfln "%a<->%a" AbstractValue.pp addr_callee AbstractValue.pp (fst addr_hist_caller) ;
match visit call_state ~pre:(pre :> BaseDomain.t) ~addr_callee ~addr_hist_caller with match visit call_state ~pre:(pre :> BaseDomain.t) ~addr_callee ~addr_hist_caller with
| `AlreadyVisited, call_state -> | `AlreadyVisited, call_state ->
@ -454,7 +454,7 @@ let rec record_post_for_address callee_proc_name call_loc ({AbductiveDomain.pre;
Attributes.replace_isl_abduced attrs_post attrs_caller Attributes.replace_isl_abduced attrs_post attrs_caller
else attrs_post else attrs_post
in in
record_post_cell callee_proc_name call_loc ~edges_pre_opt addr_hist_caller record_post_cell path callee_proc_name call_loc ~edges_pre_opt addr_hist_caller
~cell_callee_post:(edges_post, attrs_post) call_state ~cell_callee_post:(edges_post, attrs_post) call_state
in in
Memory.Edges.fold ~init:call_state_after_post edges_post Memory.Edges.fold ~init:call_state_after_post edges_post
@ -463,11 +463,11 @@ let rec record_post_for_address callee_proc_name call_loc ({AbductiveDomain.pre;
call_state_subst_find_or_new call_state addr_callee_dest call_state_subst_find_or_new call_state addr_callee_dest
~default_hist_caller:(snd addr_hist_caller) ~default_hist_caller:(snd addr_hist_caller)
in in
record_post_for_address callee_proc_name call_loc pre_post ~addr_callee:addr_callee_dest record_post_for_address path callee_proc_name call_loc pre_post
~addr_hist_caller:addr_hist_curr_dest call_state ) ) ~addr_callee:addr_callee_dest ~addr_hist_caller:addr_hist_curr_dest call_state ) )
let record_post_for_actual callee_proc_name call_loc pre_post ~formal ~actual:(actual, typ) let record_post_for_actual path callee_proc_name call_loc pre_post ~formal ~actual:(actual, typ)
call_state = call_state =
L.d_printfln_escaped "Recording POST from [%a] <-> %a" Var.pp formal AbstractValue.pp (fst actual) ; L.d_printfln_escaped "Recording POST from [%a] <-> %a" Var.pp formal AbstractValue.pp (fst actual) ;
match match
@ -479,7 +479,7 @@ let record_post_for_actual callee_proc_name call_loc pre_post ~formal ~actual:(a
deref_non_c_struct addr_formal_pre typ deref_non_c_struct addr_formal_pre typ
(pre_post.AbductiveDomain.pre :> BaseDomain.t).BaseDomain.heap (pre_post.AbductiveDomain.pre :> BaseDomain.t).BaseDomain.heap
in in
record_post_for_address callee_proc_name call_loc pre_post ~addr_callee:formal_pre record_post_for_address path callee_proc_name call_loc pre_post ~addr_callee:formal_pre
~addr_hist_caller:actual call_state ~addr_hist_caller:actual call_state
with with
| Some call_state -> | Some call_state ->
@ -488,7 +488,7 @@ let record_post_for_actual callee_proc_name call_loc pre_post ~formal ~actual:(a
call_state call_state
let record_post_for_return callee_proc_name call_loc pre_post call_state = let record_post_for_return path callee_proc_name call_loc pre_post call_state =
let return_var = Var.of_pvar (Pvar.get_ret_pvar callee_proc_name) in let return_var = Var.of_pvar (Pvar.get_ret_pvar callee_proc_name) in
match BaseStack.find_opt return_var (pre_post.AbductiveDomain.post :> BaseDomain.t).stack with match BaseStack.find_opt return_var (pre_post.AbductiveDomain.post :> BaseDomain.t).stack with
| None -> | None ->
@ -517,13 +517,14 @@ let record_post_for_return callee_proc_name call_loc pre_post call_state =
L.d_printfln_escaped "Recording POST from [return] <-> %a" AbstractValue.pp L.d_printfln_escaped "Recording POST from [return] <-> %a" AbstractValue.pp
(fst return_caller_addr_hist) ; (fst return_caller_addr_hist) ;
let call_state = let call_state =
record_post_for_address callee_proc_name call_loc pre_post ~addr_callee:return_callee record_post_for_address path callee_proc_name call_loc pre_post ~addr_callee:return_callee
~addr_hist_caller:return_caller_addr_hist call_state ~addr_hist_caller:return_caller_addr_hist call_state
in in
(call_state, Some return_caller_addr_hist) ) (call_state, Some return_caller_addr_hist) )
let apply_post_for_parameters callee_proc_name call_location pre_post ~formals ~actuals call_state = let apply_post_for_parameters path callee_proc_name call_location pre_post ~formals ~actuals
call_state =
(* for each [(formal_i, actual_i)] pair, do [post_i = post union subst(graph reachable from (* for each [(formal_i, actual_i)] pair, do [post_i = post union subst(graph reachable from
formal_i in post)], deleting previous info when comparing pre and post shows a difference formal_i in post)], deleting previous info when comparing pre and post shows a difference
(TODO: record in the pre when a location is written to instead of just comparing values (TODO: record in the pre when a location is written to instead of just comparing values
@ -531,7 +532,8 @@ let apply_post_for_parameters callee_proc_name call_location pre_post ~formals ~
post but nuke other fields in the meantime? is that possible?). *) post but nuke other fields in the meantime? is that possible?). *)
match match
List.fold2 formals actuals ~init:call_state ~f:(fun call_state formal actual -> List.fold2 formals actuals ~init:call_state ~f:(fun call_state formal actual ->
record_post_for_actual callee_proc_name call_location pre_post ~formal ~actual call_state ) record_post_for_actual path callee_proc_name call_location pre_post ~formal ~actual
call_state )
with with
| Unequal_lengths -> | Unequal_lengths ->
(* should have been checked before by [materialize_pre] *) (* should have been checked before by [materialize_pre] *)
@ -540,18 +542,18 @@ let apply_post_for_parameters callee_proc_name call_location pre_post ~formals ~
call_state call_state
let apply_post_for_captured_vars callee_proc_name call_location pre_post ~captured_vars_with_actuals let apply_post_for_captured_vars path callee_proc_name call_location pre_post
call_state = ~captured_vars_with_actuals call_state =
List.fold captured_vars_with_actuals ~init:call_state ~f:(fun call_state (formal, actual) -> List.fold captured_vars_with_actuals ~init:call_state ~f:(fun call_state (formal, actual) ->
record_post_for_actual callee_proc_name call_location pre_post ~formal ~actual call_state ) record_post_for_actual path callee_proc_name call_location pre_post ~formal ~actual call_state )
let apply_post_for_globals callee_proc_name call_location pre_post call_state = let apply_post_for_globals path callee_proc_name call_location pre_post call_state =
match match
fold_globals_of_stack call_location (pre_post.AbductiveDomain.pre :> BaseDomain.t).stack fold_globals_of_stack path call_location (pre_post.AbductiveDomain.pre :> BaseDomain.t).stack
call_state ~f:(fun _var ~stack_value:(addr_callee, _) ~addr_hist_caller call_state -> call_state ~f:(fun _var ~stack_value:(addr_callee, _) ~addr_hist_caller call_state ->
Ok Ok
(record_post_for_address callee_proc_name call_location pre_post ~addr_callee (record_post_for_address path callee_proc_name call_location pre_post ~addr_callee
~addr_hist_caller call_state) ) ~addr_hist_caller call_state) )
with with
| Error _ -> | Error _ ->
@ -560,7 +562,7 @@ let apply_post_for_globals callee_proc_name call_location pre_post call_state =
call_state call_state
let record_post_remaining_attributes callee_proc_name call_loc pre_post call_state = let record_post_remaining_attributes path callee_proc_name call_loc pre_post call_state =
BaseAddressAttributes.fold BaseAddressAttributes.fold
(fun addr_callee attrs call_state -> (fun addr_callee attrs call_state ->
if AddressSet.mem addr_callee call_state.visited then if AddressSet.mem addr_callee call_state.visited then
@ -571,7 +573,7 @@ let record_post_remaining_attributes callee_proc_name call_loc pre_post call_sta
| None -> | None ->
(* callee address has no meaning for the caller *) call_state (* callee address has no meaning for the caller *) call_state
| Some (addr_caller, history) -> | Some (addr_caller, history) ->
let attrs' = Attributes.add_call callee_proc_name call_loc history attrs in let attrs' = Attributes.add_call path callee_proc_name call_loc history attrs in
let astate = AddressAttributes.abduce_and_add addr_caller attrs' call_state.astate in let astate = AddressAttributes.abduce_and_add addr_caller attrs' call_state.astate in
{call_state with astate} ) {call_state with astate} )
(pre_post.AbductiveDomain.post :> BaseDomain.t).attrs call_state (pre_post.AbductiveDomain.post :> BaseDomain.t).attrs call_state
@ -587,20 +589,21 @@ let record_skipped_calls callee_proc_name call_loc pre_post call_state =
{call_state with astate} {call_state with astate}
let apply_post callee_proc_name call_location pre_post ~captured_vars_with_actuals ~formals ~actuals let apply_post path callee_proc_name call_location pre_post ~captured_vars_with_actuals ~formals
call_state = ~actuals call_state =
let open IResult.Let_syntax in let open IResult.Let_syntax in
PerfEvent.(log (fun logger -> log_begin_event logger ~name:"pulse call post" ())) ; PerfEvent.(log (fun logger -> log_begin_event logger ~name:"pulse call post" ())) ;
let r = let r =
let call_state, return_caller = let call_state, return_caller =
apply_post_for_parameters callee_proc_name call_location pre_post ~formals ~actuals call_state apply_post_for_parameters path callee_proc_name call_location pre_post ~formals ~actuals
|> apply_post_for_captured_vars callee_proc_name call_location pre_post call_state
|> apply_post_for_captured_vars path callee_proc_name call_location pre_post
~captured_vars_with_actuals ~captured_vars_with_actuals
|> apply_post_for_globals callee_proc_name call_location pre_post |> apply_post_for_globals path callee_proc_name call_location pre_post
|> record_post_for_return callee_proc_name call_location pre_post |> record_post_for_return path callee_proc_name call_location pre_post
in in
let+ call_state = let+ call_state =
record_post_remaining_attributes callee_proc_name call_location pre_post call_state record_post_remaining_attributes path callee_proc_name call_location pre_post call_state
|> record_skipped_calls callee_proc_name call_location pre_post |> record_skipped_calls callee_proc_name call_location pre_post
|> conjoin_callee_arith pre_post |> conjoin_callee_arith pre_post
in in
@ -610,50 +613,78 @@ let apply_post callee_proc_name call_location pre_post ~captured_vars_with_actua
r r
let check_all_valid callee_proc_name call_location {AbductiveDomain.pre; _} call_state = let check_all_valid path callee_proc_name call_location {AbductiveDomain.pre; _} call_state =
AddressMap.fold (* collect all the checks to perform then do each check in timestamp order to make sure we report
(fun addr_pre (addr_caller, hist_caller) astate_result -> the first issue if any *)
let mk_access_trace callee_access_trace = let addresses_to_check =
Trace.ViaCall AddressMap.fold
{ in_call= callee_access_trace (fun addr_pre addr_hist_caller to_check ->
; f= Call callee_proc_name let to_check =
; location= call_location match BaseAddressAttributes.get_must_be_valid addr_pre (pre :> BaseDomain.t).attrs with
; history= hist_caller } | None ->
in to_check
let open IResult.Let_syntax in | Some must_be_valid_data ->
let* astate = astate_result in (addr_hist_caller, `MustBeValid must_be_valid_data) :: to_check
let* astate = in
match BaseAddressAttributes.get_must_be_valid addr_pre (pre :> BaseDomain.t).attrs with match
BaseAddressAttributes.get_must_be_initialized addr_pre (pre :> BaseDomain.t).attrs
with
| None -> | None ->
astate_result to_check
| Some (callee_access_trace, must_be_valid_reason) -> | Some must_be_init_data ->
let access_trace = mk_access_trace callee_access_trace in (addr_hist_caller, `MustBeInitialized must_be_init_data) :: to_check )
AddressAttributes.check_valid access_trace addr_caller astate call_state.subst []
|> Result.map_error ~f:(fun (invalidation, invalidation_trace) -> in
L.d_printfln "ERROR: caller's %a invalid!" AbstractValue.pp addr_caller ; let timestamp_of_check = function
AccessResult.ReportableError | `MustBeValid (timestamp, _, _) ->
{ diagnostic= Some timestamp
Diagnostic.AccessToInvalidAddress | `MustBeInitialized _ ->
{ calling_context= [] None
; invalidation in
; invalidation_trace List.sort addresses_to_check ~compare:(fun (_, check1) (_, check2) ->
; access_trace match (timestamp_of_check check1, timestamp_of_check check2) with
; must_be_valid_reason } | Some t1, Some t2 ->
; astate } ) (* smaller timestamp first *) PathContext.compare_timestamp t1 t2
in (* with a timestamp first (gone soon when MustBeInitialized will get a timestamp too), otherwise dummy comparison *)
match BaseAddressAttributes.get_must_be_initialized addr_pre (pre :> BaseDomain.t).attrs with | None, None ->
| None -> 0
astate_result | Some _, None ->
| Some callee_access_trace -> -1
let access_trace = mk_access_trace callee_access_trace in | None, Some _ ->
AddressAttributes.check_initialized access_trace addr_caller astate 1 )
|> Result.map_error ~f:(fun () -> |> List.fold_result ~init:call_state.astate ~f:(fun astate ((addr_caller, hist_caller), check) ->
L.d_printfln "ERROR: caller's %a is uninitialized!" AbstractValue.pp addr_caller ; let mk_access_trace callee_access_trace =
AccessResult.ReportableError Trace.ViaCall
{ diagnostic= { in_call= callee_access_trace
Diagnostic.ReadUninitializedValue {calling_context= []; trace= access_trace} ; f= Call callee_proc_name
; astate } ) ) ; location= call_location
call_state.subst (Ok call_state.astate) ; history= hist_caller }
in
match check with
| `MustBeValid (_timestamp, callee_access_trace, must_be_valid_reason) ->
let access_trace = mk_access_trace callee_access_trace in
AddressAttributes.check_valid path access_trace addr_caller astate
|> Result.map_error ~f:(fun (invalidation, invalidation_trace) ->
L.d_printfln "ERROR: caller's %a invalid!" AbstractValue.pp addr_caller ;
AccessResult.ReportableError
{ diagnostic=
Diagnostic.AccessToInvalidAddress
{ calling_context= []
; invalidation
; invalidation_trace
; access_trace
; must_be_valid_reason }
; astate } )
| `MustBeInitialized callee_access_trace ->
let access_trace = mk_access_trace callee_access_trace in
AddressAttributes.check_initialized access_trace addr_caller astate
|> Result.map_error ~f:(fun () ->
L.d_printfln "ERROR: caller's %a is uninitialized!" AbstractValue.pp addr_caller ;
AccessResult.ReportableError
{ diagnostic=
Diagnostic.ReadUninitializedValue
{calling_context= []; trace= access_trace}
; astate } ) )
let isl_check_all_invalid invalid_addr_callers callee_proc_name call_location let isl_check_all_invalid invalid_addr_callers callee_proc_name call_location
@ -704,7 +735,7 @@ let isl_check_all_invalid invalid_addr_callers callee_proc_name call_location
- for each actual, write the post for that actual - for each actual, write the post for that actual
- if aliasing is introduced at any time then give up *) - if aliasing is introduced at any time then give up *)
let apply_prepost ~is_isl_error_prepost callee_proc_name call_location ~callee_prepost:pre_post let apply_prepost path ~is_isl_error_prepost callee_proc_name call_location ~callee_prepost:pre_post
~captured_vars_with_actuals ~formals ~actuals astate = ~captured_vars_with_actuals ~formals ~actuals astate =
L.d_printfln "Applying pre/post for %a(%a):@\n%a" Procname.pp callee_proc_name L.d_printfln "Applying pre/post for %a(%a):@\n%a" Procname.pp callee_proc_name
(Pp.seq ~sep:"," Var.pp) formals AbductiveDomain.pp pre_post ; (Pp.seq ~sep:"," Var.pp) formals AbductiveDomain.pp pre_post ;
@ -717,8 +748,8 @@ let apply_prepost ~is_isl_error_prepost callee_proc_name call_location ~callee_p
in in
(* read the precondition *) (* read the precondition *)
match match
materialize_pre callee_proc_name call_location pre_post ~captured_vars_with_actuals ~formals materialize_pre path callee_proc_name call_location pre_post ~captured_vars_with_actuals
~actuals empty_call_state ~formals ~actuals empty_call_state
with with
| exception Contradiction reason -> | exception Contradiction reason ->
(* can't make sense of the pre-condition in the current context: give up on that particular (* can't make sense of the pre-condition in the current context: give up on that particular
@ -738,7 +769,7 @@ let apply_prepost ~is_isl_error_prepost callee_proc_name call_location ~callee_p
ContinueProgram ok specs *) ContinueProgram ok specs *)
let* astate = let* astate =
if Config.pulse_isl then Ok astate if Config.pulse_isl then Ok astate
else check_all_valid callee_proc_name call_location pre_post call_state else check_all_valid path callee_proc_name call_location pre_post call_state
in in
(* reset [visited] *) (* reset [visited] *)
let invalid_subst = call_state.invalid_subst in let invalid_subst = call_state.invalid_subst in
@ -746,8 +777,8 @@ let apply_prepost ~is_isl_error_prepost callee_proc_name call_location ~callee_p
let call_state = {call_state with astate; visited= AddressSet.empty} in let call_state = {call_state with astate; visited= AddressSet.empty} in
(* apply the postcondition *) (* apply the postcondition *)
let* call_state, return_caller = let* call_state, return_caller =
apply_post callee_proc_name call_location pre_post ~captured_vars_with_actuals ~formals apply_post path callee_proc_name call_location pre_post ~captured_vars_with_actuals
~actuals call_state ~formals ~actuals call_state
in in
let astate = let astate =
if Topl.is_active () then if Topl.is_active () then

@ -10,7 +10,8 @@ open PulseBasicInterface
open PulseDomainInterface open PulseDomainInterface
val apply_prepost : val apply_prepost :
is_isl_error_prepost:bool PathContext.t
-> is_isl_error_prepost:bool
-> Procname.t -> Procname.t
-> Location.t -> Location.t
-> callee_prepost:AbductiveDomain.t -> callee_prepost:AbductiveDomain.t

File diff suppressed because it is too large Load Diff

@ -10,6 +10,7 @@ open PulseDomainInterface
type model_data = type model_data =
{ analysis_data: PulseSummary.t InterproceduralAnalysis.t { analysis_data: PulseSummary.t InterproceduralAnalysis.t
; path: PathContext.t
; callee_procname: Procname.t ; callee_procname: Procname.t
; location: Location.t ; location: Location.t
; ret: Ident.t * Typ.t } ; ret: Ident.t * Typ.t }

@ -15,7 +15,7 @@ let mk_objc_self_pvar proc_desc =
Pvar.mk Mangled.self proc_name Pvar.mk Mangled.self proc_name
let init_fields_zero tenv location ~zero addr typ astate = let init_fields_zero tenv path location ~zero addr typ astate =
let get_fields typ = let get_fields typ =
match typ.Typ.desc with match typ.Typ.desc with
| Tstruct struct_name -> | Tstruct struct_name ->
@ -31,7 +31,7 @@ let init_fields_zero tenv location ~zero addr typ astate =
let acc, field_addr = Memory.eval_edge addr (FieldAccess field) acc in let acc, field_addr = Memory.eval_edge addr (FieldAccess field) acc in
init_fields_zero_helper field_addr field_typ acc ) init_fields_zero_helper field_addr field_typ acc )
| None -> | None ->
PulseOperations.write_deref location ~ref:addr ~obj:zero astate PulseOperations.write_deref path location ~ref:addr ~obj:zero astate
in in
init_fields_zero_helper addr typ astate init_fields_zero_helper addr typ astate
@ -39,10 +39,11 @@ let init_fields_zero tenv location ~zero addr typ astate =
let mk_objc_method_nil_summary_aux tenv proc_desc astate = let mk_objc_method_nil_summary_aux tenv proc_desc astate =
(* Constructs summary {self = 0} {return = self}. (* Constructs summary {self = 0} {return = self}.
This allows us to connect invalidation with invalid access in the trace *) This allows us to connect invalidation with invalid access in the trace *)
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, (self_value, self_history) = let* astate, (self_value, self_history) =
PulseOperations.eval_deref location (Lvar self) astate PulseOperations.eval_deref path location (Lvar self) astate
in in
let* astate = PulseArithmetic.prune_eq_zero self_value astate in let* astate = PulseArithmetic.prune_eq_zero self_value astate in
let event = ValueHistory.NilMessaging location in let event = ValueHistory.NilMessaging location in
@ -51,14 +52,16 @@ let mk_objc_method_nil_summary_aux tenv proc_desc astate =
| Some (last_formal, {desc= Tptr (typ, _)}) when Mangled.is_return_param last_formal -> | Some (last_formal, {desc= Tptr (typ, _)}) when Mangled.is_return_param last_formal ->
let ret_param_var = Procdesc.get_ret_param_var proc_desc in let ret_param_var = Procdesc.get_ret_param_var proc_desc in
let* astate, ret_param_var_addr_hist = let* astate, ret_param_var_addr_hist =
PulseOperations.eval_deref location (Lvar ret_param_var) astate PulseOperations.eval_deref path location (Lvar ret_param_var) astate
in in
init_fields_zero tenv location ~zero:updated_self_value_hist ret_param_var_addr_hist typ init_fields_zero tenv path location ~zero:updated_self_value_hist ret_param_var_addr_hist typ
astate astate
| _ -> | _ ->
let ret_var = Procdesc.get_ret_var proc_desc in let ret_var = Procdesc.get_ret_var proc_desc in
let* astate, ret_var_addr_hist = PulseOperations.eval Write location (Lvar ret_var) astate in let* astate, ret_var_addr_hist =
PulseOperations.write_deref location ~ref:ret_var_addr_hist ~obj:updated_self_value_hist PulseOperations.eval path Write location (Lvar ret_var) astate
in
PulseOperations.write_deref path location ~ref:ret_var_addr_hist ~obj:updated_self_value_hist
astate astate
@ -88,7 +91,9 @@ let append_objc_self_positive {InterproceduralAnalysis.tenv; proc_desc; err_log}
match astate with match astate with
| ContinueProgram astate -> | ContinueProgram astate ->
let result = let result =
let* astate, value = PulseOperations.eval_deref location (Lvar self) astate in let* astate, value =
PulseOperations.eval_deref PathContext.initial location (Lvar self) astate
in
PulseArithmetic.prune_positive (fst value) astate PulseArithmetic.prune_positive (fst value) astate
in in
PulseReport.report_result tenv proc_desc err_log location result PulseReport.report_result tenv proc_desc err_log location result
@ -111,6 +116,7 @@ let append_objc_actual_self_positive procdesc self_actual astate =
let update_must_be_valid_reason {InterproceduralAnalysis.tenv; proc_desc; err_log} astate = let update_must_be_valid_reason {InterproceduralAnalysis.tenv; proc_desc; err_log} astate =
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 proc_name = Procdesc.get_proc_name proc_desc in let proc_name = Procdesc.get_proc_name proc_desc in
@ -119,12 +125,9 @@ let update_must_be_valid_reason {InterproceduralAnalysis.tenv; proc_desc; err_lo
when not (Procdesc.is_ret_type_pod proc_desc) -> when not (Procdesc.is_ret_type_pod proc_desc) ->
let result = let result =
(* add reason for must be valid to be because the return type is non pod *) (* add reason for must be valid to be because the return type is non pod *)
let+ astate, value = PulseOperations.eval_deref location (Lvar self) astate in let+ astate, value = PulseOperations.eval_deref path location (Lvar self) astate in
let astate = AddressAttributes.replace_must_be_valid_reason path SelfOfNonPODReturnMethod (fst value)
AddressAttributes.replace_must_be_valid_reason Invalidation.SelfOfNonPODReturnMethod astate
(fst value) astate
in
astate
in in
PulseReport.report_result tenv proc_desc err_log location result PulseReport.report_result tenv proc_desc err_log location result
| ContinueProgram _, _ | ContinueProgram _, _

@ -50,10 +50,10 @@ end
include Import include Import
let check_addr_access ?must_be_valid_reason access_mode location (address, history) astate = let check_addr_access path ?must_be_valid_reason access_mode location (address, history) astate =
let access_trace = Trace.Immediate {location; history} in let access_trace = Trace.Immediate {location; history} in
let* astate = let* astate =
AddressAttributes.check_valid ?must_be_valid_reason access_trace address astate AddressAttributes.check_valid path ?must_be_valid_reason access_trace address astate
|> Result.map_error ~f:(fun (invalidation, invalidation_trace) -> |> Result.map_error ~f:(fun (invalidation, invalidation_trace) ->
ReportableError ReportableError
{ diagnostic= { diagnostic=
@ -79,10 +79,10 @@ let check_addr_access ?must_be_valid_reason access_mode location (address, histo
Ok astate Ok astate
let check_and_abduce_addr_access_isl access_mode location (address, history) ?(null_noop = false) let check_and_abduce_addr_access_isl path access_mode location (address, history)
astate = ?(null_noop = false) astate =
let access_trace = Trace.Immediate {location; history} in let access_trace = Trace.Immediate {location; history} in
AddressAttributes.check_valid_isl access_trace address ~null_noop astate AddressAttributes.check_valid_isl path access_trace address ~null_noop astate
|> List.map ~f:(fun access_result -> |> List.map ~f:(fun access_result ->
let* astate = AccessResult.of_abductive_access_result access_trace access_result in let* astate = AccessResult.of_abductive_access_result access_trace access_result in
match access_mode with match access_mode with
@ -137,7 +137,7 @@ module Closures = struct
Memory.Edges.add (HilExp.Access.FieldAccess (mk_fake_field ~id mode)) (addr, trace) edges ) Memory.Edges.add (HilExp.Access.FieldAccess (mk_fake_field ~id mode)) (addr, trace) edges )
let check_captured_addresses action lambda_addr (astate : t) = let check_captured_addresses path action lambda_addr (astate : t) =
match AbductiveDomain.find_post_cell_opt lambda_addr astate with match AbductiveDomain.find_post_cell_opt lambda_addr astate with
| None -> | None ->
Ok astate Ok astate
@ -147,7 +147,7 @@ module Closures = struct
| Attribute.Closure _ -> | Attribute.Closure _ ->
IContainer.iter_result ~fold:Memory.Edges.fold edges ~f:(fun (access, addr_trace) -> IContainer.iter_result ~fold:Memory.Edges.fold edges ~f:(fun (access, addr_trace) ->
if is_captured_by_ref_fake_access access then if is_captured_by_ref_fake_access access then
let+ _ = check_addr_access Read action addr_trace astate in let+ _ = check_addr_access path Read action addr_trace astate in
() ()
else Ok () ) else Ok () )
| _ -> | _ ->
@ -172,50 +172,52 @@ module Closures = struct
(astate, closure_addr_hist) (astate, closure_addr_hist)
end end
let eval_var location hist var astate = Stack.eval location hist var astate let eval_access path ?must_be_valid_reason mode location addr_hist access astate =
let+ astate = check_addr_access path ?must_be_valid_reason mode location addr_hist astate in
let eval_access ?must_be_valid_reason mode location addr_hist access astate =
let+ astate = check_addr_access ?must_be_valid_reason mode location addr_hist astate in
Memory.eval_edge addr_hist access astate Memory.eval_edge addr_hist access astate
let eval_deref_access ?must_be_valid_reason mode location addr_hist access astate = let eval_deref_access path ?must_be_valid_reason mode location addr_hist access astate =
let* astate, addr_hist = eval_access Read location addr_hist access astate in let* astate, addr_hist = eval_access path Read location addr_hist access astate in
eval_access ?must_be_valid_reason mode location addr_hist Dereference astate eval_access path ?must_be_valid_reason mode location addr_hist Dereference astate
let eval_access_biad_isl mode location addr_hist access astate = let eval_access_biad_isl path mode location addr_hist access astate =
let map_ok addr_hist access results = let map_ok addr_hist access results =
List.map results ~f:(fun result -> List.map results ~f:(fun result ->
let+ astate = result in let+ astate = result in
Memory.eval_edge addr_hist access astate ) Memory.eval_edge addr_hist access astate )
in in
let results = check_and_abduce_addr_access_isl mode location addr_hist astate in let results = check_and_abduce_addr_access_isl path mode location addr_hist astate in
map_ok addr_hist access results map_ok addr_hist access results
let eval mode location exp0 astate = let eval path mode location exp0 astate =
let rec eval mode exp astate = let rec eval path mode exp astate =
match (exp : Exp.t) with match (exp : Exp.t) with
| Var id -> | Var id ->
Ok (eval_var location (* error in case of missing history? *) [] (Var.of_id id) astate) Ok
(Stack.eval path location (* error in case of missing history? *) [] (Var.of_id id)
astate)
| Lvar pvar -> | Lvar pvar ->
Ok Ok
(eval_var location (Stack.eval path location
[ValueHistory.VariableAccessed (pvar, location)] [ValueHistory.VariableAccessed (pvar, location)]
(Var.of_pvar pvar) astate) (Var.of_pvar pvar) astate)
| Lfield (exp', field, _) -> | Lfield (exp', field, _) ->
let* astate, addr_hist = eval Read exp' astate in let* astate, addr_hist = eval path Read exp' astate in
eval_access mode location addr_hist (FieldAccess field) astate eval_access path mode location addr_hist (FieldAccess field) astate
| Lindex (exp', exp_index) -> | Lindex (exp', exp_index) ->
let* astate, addr_hist_index = eval Read exp_index astate in let* astate, addr_hist_index = eval path Read exp_index astate in
let* astate, addr_hist = eval Read exp' astate in let* astate, addr_hist = eval path Read exp' astate in
eval_access mode location addr_hist (ArrayAccess (StdTyp.void, fst addr_hist_index)) astate eval_access path mode location addr_hist
(ArrayAccess (StdTyp.void, fst addr_hist_index))
astate
| Closure {name; captured_vars} -> | Closure {name; captured_vars} ->
let+ astate, rev_captured = let+ astate, rev_captured =
List.fold_result captured_vars ~init:(astate, []) List.fold_result captured_vars ~init:(astate, [])
~f:(fun (astate, rev_captured) (capt_exp, captured_as, _, mode) -> ~f:(fun (astate, rev_captured) (capt_exp, captured_as, _, mode) ->
let+ astate, addr_trace = eval Read capt_exp astate in let+ astate, addr_trace = eval path Read capt_exp astate in
(astate, (captured_as, addr_trace, mode) :: rev_captured) ) (astate, (captured_as, addr_trace, mode) :: rev_captured) )
in in
Closures.record location name (List.rev rev_captured) astate Closures.record location name (List.rev rev_captured) astate
@ -223,7 +225,7 @@ let eval mode location exp0 astate =
(* function pointers are represented as closures with no captured variables *) (* function pointers are represented as closures with no captured variables *)
Ok (Closures.record location proc_name [] astate) Ok (Closures.record location proc_name [] astate)
| Cast (_, exp') -> | Cast (_, exp') ->
eval mode exp' astate eval path mode exp' astate
| Const (Cint i) -> | Const (Cint i) ->
let v = AbstractValue.Constants.get_int i in let v = AbstractValue.Constants.get_int i in
let invalidation = Invalidation.ConstantDereference i in let invalidation = Invalidation.ConstantDereference i in
@ -235,14 +237,14 @@ let eval mode location exp0 astate =
in in
(astate, (v, [ValueHistory.Invalidated (invalidation, location)])) (astate, (v, [ValueHistory.Invalidated (invalidation, location)]))
| UnOp (unop, exp, _typ) -> | UnOp (unop, exp, _typ) ->
let* astate, (addr, hist) = eval Read exp astate in let* astate, (addr, hist) = eval path Read exp astate in
let unop_addr = AbstractValue.mk_fresh () in let unop_addr = AbstractValue.mk_fresh () in
let+ astate = PulseArithmetic.eval_unop unop_addr unop addr astate in let+ astate = PulseArithmetic.eval_unop unop_addr unop addr astate in
(astate, (unop_addr, hist)) (astate, (unop_addr, hist))
| BinOp (bop, e_lhs, e_rhs) -> | BinOp (bop, e_lhs, e_rhs) ->
let* astate, (addr_lhs, hist_lhs) = eval Read e_lhs astate in let* astate, (addr_lhs, hist_lhs) = eval path Read e_lhs astate in
(* NOTE: keeping track of only [hist_lhs] into the binop is not the best *) (* NOTE: keeping track of only [hist_lhs] into the binop is not the best *)
let* astate, (addr_rhs, _hist_rhs) = eval Read e_rhs astate in let* astate, (addr_rhs, _hist_rhs) = eval path Read e_rhs 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 bop (AbstractValueOperand addr_lhs) PulseArithmetic.eval_binop binop_addr bop (AbstractValueOperand addr_lhs)
@ -252,24 +254,24 @@ let eval mode location exp0 astate =
| Const _ | Sizeof _ | Exn _ -> | Const _ | Sizeof _ | Exn _ ->
Ok (astate, (AbstractValue.mk_fresh (), (* TODO history *) [])) Ok (astate, (AbstractValue.mk_fresh (), (* TODO history *) []))
in in
eval mode exp0 astate eval path mode exp0 astate
let eval_to_operand mode location exp astate = let eval_to_operand path mode location exp astate =
match (exp : Exp.t) with match (exp : Exp.t) with
| Const (Cint i) -> | Const (Cint i) ->
Ok (astate, PulseArithmetic.LiteralOperand i) Ok (astate, PulseArithmetic.LiteralOperand i)
| exp -> | exp ->
let+ astate, (value, _) = eval mode location exp astate in let+ astate, (value, _) = eval path mode location exp astate in
(astate, PulseArithmetic.AbstractValueOperand value) (astate, PulseArithmetic.AbstractValueOperand value)
let prune location ~condition astate = let prune path location ~condition astate =
let rec prune_aux ~negated exp astate = let rec prune_aux ~negated exp astate =
match (exp : Exp.t) with match (exp : Exp.t) with
| BinOp (bop, exp_lhs, exp_rhs) -> | BinOp (bop, exp_lhs, exp_rhs) ->
let* astate, lhs_op = eval_to_operand Read location exp_lhs astate in let* astate, lhs_op = eval_to_operand path Read location exp_lhs astate in
let* astate, rhs_op = eval_to_operand Read location exp_rhs astate in let* astate, rhs_op = eval_to_operand path Read location exp_rhs astate in
PulseArithmetic.prune_binop ~negated bop lhs_op rhs_op astate PulseArithmetic.prune_binop ~negated bop lhs_op rhs_op astate
| UnOp (LNot, exp', _) -> | UnOp (LNot, exp', _) ->
prune_aux ~negated:(not negated) exp' astate prune_aux ~negated:(not negated) exp' astate
@ -279,53 +281,53 @@ let prune location ~condition astate =
prune_aux ~negated:false condition astate prune_aux ~negated:false condition astate
let eval_deref ?must_be_valid_reason location exp astate = let eval_deref path ?must_be_valid_reason location exp astate =
let* astate, addr_hist = eval Read location exp astate in let* astate, addr_hist = eval path Read location exp astate in
let+ astate = check_addr_access ?must_be_valid_reason Read location addr_hist astate in let+ astate = check_addr_access path ?must_be_valid_reason Read location addr_hist astate in
Memory.eval_edge addr_hist Dereference astate Memory.eval_edge addr_hist Dereference astate
let eval_proc_name location call_exp astate = let eval_proc_name path location call_exp astate =
match (call_exp : Exp.t) with match (call_exp : Exp.t) with
| Const (Cfun proc_name) | Closure {name= proc_name} -> | Const (Cfun proc_name) | Closure {name= proc_name} ->
Ok (astate, Some proc_name) Ok (astate, Some proc_name)
| _ -> | _ ->
let+ astate, (f, _) = eval Read location call_exp astate in let+ astate, (f, _) = eval path Read location call_exp astate in
(astate, AddressAttributes.get_closure_proc_name f astate) (astate, AddressAttributes.get_closure_proc_name f astate)
let eval_structure_isl mode loc exp astate = let eval_structure_isl path mode loc exp astate =
match (exp : Exp.t) with match (exp : Exp.t) with
| Lfield (exp', field, _) -> | Lfield (exp', field, _) ->
let+ astate, addr_hist = eval mode loc exp' astate in let+ astate, addr_hist = eval path mode loc exp' astate in
let astates = eval_access_biad_isl mode loc addr_hist (FieldAccess field) astate in let astates = eval_access_biad_isl path mode loc addr_hist (FieldAccess field) astate in
(false, astates) (false, astates)
| Lindex (exp', exp_index) -> | Lindex (exp', exp_index) ->
let* astate, addr_hist_index = eval mode loc exp_index astate in let* astate, addr_hist_index = eval path mode loc exp_index astate in
let+ astate, addr_hist = eval mode loc exp' astate in let+ astate, addr_hist = eval path mode loc exp' astate in
let astates = let astates =
eval_access_biad_isl mode loc addr_hist eval_access_biad_isl path mode loc addr_hist
(ArrayAccess (StdTyp.void, fst addr_hist_index)) (ArrayAccess (StdTyp.void, fst addr_hist_index))
astate astate
in in
(false, astates) (false, astates)
| _ -> | _ ->
let+ astate, (addr, history) = eval mode loc exp astate in let+ astate, (addr, history) = eval path mode loc exp astate in
(true, [Ok (astate, (addr, history))]) (true, [Ok (astate, (addr, history))])
let eval_deref_biad_isl location access addr_hist astate = let eval_deref_biad_isl path location access addr_hist astate =
check_and_abduce_addr_access_isl Read location addr_hist astate check_and_abduce_addr_access_isl path Read location addr_hist astate
|> List.map ~f:(fun result -> |> List.map ~f:(fun result ->
let+ astate = result in let+ astate = result in
Memory.eval_edge addr_hist access astate ) Memory.eval_edge addr_hist access astate )
let eval_deref_isl location exp astate = let eval_deref_isl path location exp astate =
let<*> is_structured, ls_astate_addr_hist = eval_structure_isl Read location exp astate in let<*> is_structured, ls_astate_addr_hist = eval_structure_isl path Read location exp astate in
let eval_deref_function (astate, addr_hist) = let eval_deref_function (astate, addr_hist) =
if is_structured then eval_deref_biad_isl location Dereference addr_hist astate if is_structured then eval_deref_biad_isl path location Dereference addr_hist astate
else [eval_deref location exp astate] else [eval_deref path location exp astate]
in in
List.concat_map ls_astate_addr_hist ~f:(fun result -> List.concat_map ls_astate_addr_hist ~f:(fun result ->
let<*> astate_addr = result in let<*> astate_addr = result in
@ -349,42 +351,44 @@ let havoc_id id loc_opt astate =
else astate else astate
let write_access location addr_trace_ref access addr_trace_obj astate = let write_access path location addr_trace_ref access addr_trace_obj astate =
check_addr_access Write location addr_trace_ref astate check_addr_access path Write location addr_trace_ref astate
>>| Memory.add_edge addr_trace_ref access addr_trace_obj location >>| Memory.add_edge addr_trace_ref access addr_trace_obj location
let write_access_biad_isl location addr_trace_ref access addr_trace_obj astate = let write_access_biad_isl path location addr_trace_ref access addr_trace_obj astate =
let astates = check_and_abduce_addr_access_isl Write location addr_trace_ref astate in let astates = check_and_abduce_addr_access_isl path Write location addr_trace_ref astate in
List.map astates ~f:(fun result -> List.map astates ~f:(fun result ->
let+ astate = result in let+ astate = result in
Memory.add_edge addr_trace_ref access addr_trace_obj location astate ) Memory.add_edge addr_trace_ref access addr_trace_obj location astate )
let write_deref location ~ref:addr_trace_ref ~obj:addr_trace_obj astate = let write_deref path location ~ref:addr_trace_ref ~obj:addr_trace_obj astate =
write_access location addr_trace_ref Dereference addr_trace_obj astate write_access path location addr_trace_ref Dereference addr_trace_obj astate
let write_deref_biad_isl location ~ref:(addr_ref, addr_ref_history) access ~obj:addr_trace_obj let write_deref_biad_isl path location ~ref:(addr_ref, addr_ref_history) access ~obj:addr_trace_obj
astate = astate =
write_access_biad_isl location (addr_ref, addr_ref_history) access addr_trace_obj astate write_access_biad_isl path location (addr_ref, addr_ref_history) access addr_trace_obj astate
let write_field location ~ref:addr_trace_ref field ~obj:addr_trace_obj astate = let write_field path location ~ref:addr_trace_ref field ~obj:addr_trace_obj astate =
write_access location addr_trace_ref (FieldAccess field) addr_trace_obj astate write_access path location addr_trace_ref (FieldAccess field) addr_trace_obj astate
let write_deref_field location ~ref:addr_trace_ref field ~obj:addr_trace_obj astate = let write_deref_field path location ~ref:addr_trace_ref field ~obj:addr_trace_obj astate =
let* astate, addr_hist = eval_access Read location addr_trace_ref (FieldAccess field) astate in let* astate, addr_hist =
write_deref location ~ref:addr_hist ~obj:addr_trace_obj astate eval_access path Read location addr_trace_ref (FieldAccess field) astate
in
write_deref path location ~ref:addr_hist ~obj:addr_trace_obj astate
let write_arr_index location ~ref:addr_trace_ref ~index ~obj:addr_trace_obj astate = let write_arr_index path location ~ref:addr_trace_ref ~index ~obj:addr_trace_obj astate =
write_access location addr_trace_ref (ArrayAccess (StdTyp.void, index)) addr_trace_obj astate write_access path location addr_trace_ref (ArrayAccess (StdTyp.void, index)) addr_trace_obj astate
let havoc_deref_field location addr_trace field trace_obj astate = let havoc_deref_field path location addr_trace field trace_obj astate =
write_deref_field location ~ref:addr_trace field write_deref_field path location ~ref:addr_trace field
~obj:(AbstractValue.mk_fresh (), trace_obj) ~obj:(AbstractValue.mk_fresh (), trace_obj)
astate astate
@ -405,10 +409,10 @@ type invalidation_access =
| StackAddress of Var.t * ValueHistory.t | StackAddress of Var.t * ValueHistory.t
| UntraceableAccess | UntraceableAccess
let record_invalidation access_path location cause astate = let record_invalidation path access_path location cause astate =
match access_path with match access_path with
| StackAddress (x, hist0) -> | StackAddress (x, hist0) ->
let astate, (addr, hist) = Stack.eval location hist0 x astate in let astate, (addr, hist) = Stack.eval path location hist0 x astate in
Stack.add x (addr, Invalidated (cause, location) :: hist) astate Stack.add x (addr, Invalidated (cause, location) :: hist) astate
| MemoryAccess {pointer; access; hist_obj_default} -> | MemoryAccess {pointer; access; hist_obj_default} ->
let addr_obj, hist_obj = let addr_obj, hist_obj =
@ -425,40 +429,40 @@ let record_invalidation access_path location cause astate =
astate astate
let invalidate access_path location cause addr_trace astate = let invalidate path access_path location cause addr_trace astate =
check_addr_access NoAccess location addr_trace astate check_addr_access path NoAccess location addr_trace astate
>>| AddressAttributes.invalidate addr_trace cause location >>| AddressAttributes.invalidate addr_trace cause location
>>| record_invalidation access_path location cause >>| record_invalidation path access_path location cause
let invalidate_biad_isl location cause (address, history) astate = let invalidate_biad_isl path location cause (address, history) astate =
check_and_abduce_addr_access_isl NoAccess location (address, history) ~null_noop:true astate check_and_abduce_addr_access_isl path NoAccess location (address, history) ~null_noop:true astate
|> List.map ~f:(fun result -> |> List.map ~f:(fun result ->
let+ astate = result in let+ astate = result in
AddressAttributes.invalidate (address, history) cause location astate ) AddressAttributes.invalidate (address, history) cause location astate )
let invalidate_access location cause ref_addr_hist access astate = let invalidate_access path location cause ref_addr_hist access astate =
let astate, (addr_obj, hist_obj) = Memory.eval_edge ref_addr_hist access astate in let astate, (addr_obj, hist_obj) = Memory.eval_edge ref_addr_hist access astate in
invalidate invalidate path
(MemoryAccess {pointer= ref_addr_hist; access; hist_obj_default= hist_obj}) (MemoryAccess {pointer= ref_addr_hist; access; hist_obj_default= hist_obj})
location cause location cause
(addr_obj, snd ref_addr_hist) (addr_obj, snd ref_addr_hist)
astate astate
let invalidate_deref_access location cause ref_addr_hist access astate = let invalidate_deref_access path location cause ref_addr_hist access astate =
let astate, addr_hist = Memory.eval_edge ref_addr_hist access astate in let astate, addr_hist = Memory.eval_edge ref_addr_hist access astate in
let astate, (addr_obj, hist_obj) = Memory.eval_edge addr_hist Dereference astate in let astate, (addr_obj, hist_obj) = Memory.eval_edge addr_hist Dereference astate in
invalidate invalidate path
(MemoryAccess {pointer= ref_addr_hist; access; hist_obj_default= hist_obj}) (MemoryAccess {pointer= ref_addr_hist; access; hist_obj_default= hist_obj})
location cause location cause
(addr_obj, snd ref_addr_hist) (addr_obj, snd ref_addr_hist)
astate astate
let invalidate_array_elements location cause addr_trace astate = let invalidate_array_elements path location cause addr_trace astate =
let+ astate = check_addr_access NoAccess location addr_trace astate in let+ astate = check_addr_access path NoAccess location addr_trace astate in
match Memory.find_opt (fst addr_trace) astate with match Memory.find_opt (fst addr_trace) astate with
| None -> | None ->
astate astate
@ -467,15 +471,15 @@ let invalidate_array_elements location cause addr_trace astate =
match (access : Memory.Access.t) with match (access : Memory.Access.t) with
| ArrayAccess _ as access -> | ArrayAccess _ as access ->
AddressAttributes.invalidate dest_addr_trace cause location astate AddressAttributes.invalidate dest_addr_trace cause location astate
|> record_invalidation |> record_invalidation path
(MemoryAccess {pointer= addr_trace; access; hist_obj_default= snd dest_addr_trace}) (MemoryAccess {pointer= addr_trace; access; hist_obj_default= snd dest_addr_trace})
location cause location cause
| _ -> | _ ->
astate ) astate )
let shallow_copy location addr_hist astate = let shallow_copy path location addr_hist astate =
let+ astate = check_addr_access Read location addr_hist astate in let+ astate = check_addr_access path Read location addr_hist astate in
let cell = let cell =
match AbductiveDomain.find_post_cell_opt (fst addr_hist) astate with match AbductiveDomain.find_post_cell_opt (fst addr_hist) astate with
| None -> | None ->
@ -668,13 +672,13 @@ let remove_vars vars location astate =
Stack.remove_vars vars astate Stack.remove_vars vars astate
let get_captured_actuals location ~captured_vars ~actual_closure astate = let get_captured_actuals path location ~captured_vars ~actual_closure astate =
let* astate, this_value_addr = eval_access Read location actual_closure Dereference astate in let* astate, this_value_addr = eval_access path Read location actual_closure Dereference astate in
let+ _, astate, captured_vars_with_actuals = let+ _, astate, captured_vars_with_actuals =
List.fold_result captured_vars ~init:(0, astate, []) List.fold_result captured_vars ~init:(0, astate, [])
~f:(fun (id, astate, captured) (var, mode, typ) -> ~f:(fun (id, astate, captured) (var, mode, typ) ->
let+ astate, captured_actual = let+ astate, captured_actual =
eval_access Read location this_value_addr eval_access path Read location this_value_addr
(FieldAccess (Closures.mk_fake_field ~id mode)) (FieldAccess (Closures.mk_fake_field ~id mode))
astate astate
in in

@ -68,7 +68,8 @@ include module type of Import
type t = AbductiveDomain.t type t = AbductiveDomain.t
val check_addr_access : val check_addr_access :
?must_be_valid_reason:Invalidation.must_be_valid_reason PathContext.t
-> ?must_be_valid_reason:Invalidation.must_be_valid_reason
-> access_mode -> access_mode
-> Location.t -> Location.t
-> AbstractValue.t * ValueHistory.t -> AbstractValue.t * ValueHistory.t
@ -77,12 +78,18 @@ val check_addr_access :
(** Check that the [address] is not known to be invalid *) (** Check that the [address] is not known to be invalid *)
module Closures : sig module Closures : sig
val check_captured_addresses : Location.t -> AbstractValue.t -> t -> t AccessResult.t val check_captured_addresses :
PathContext.t -> Location.t -> AbstractValue.t -> t -> t AccessResult.t
(** assert the validity of the addresses captured by the lambda *) (** assert the validity of the addresses captured by the lambda *)
end end
val eval : val eval :
access_mode -> Location.t -> Exp.t -> t -> (t * (AbstractValue.t * ValueHistory.t)) AccessResult.t PathContext.t
-> access_mode
-> Location.t
-> Exp.t
-> t
-> (t * (AbstractValue.t * ValueHistory.t)) AccessResult.t
(** Use the stack and heap to evaluate the given expression down to an abstract address representing (** Use the stack and heap to evaluate the given expression down to an abstract address representing
its value. its value.
@ -90,7 +97,8 @@ val eval :
known to be invalid. *) known to be invalid. *)
val eval_structure_isl : val eval_structure_isl :
access_mode PathContext.t
-> access_mode
-> Location.t -> Location.t
-> Exp.t -> Exp.t
-> t -> t
@ -98,10 +106,11 @@ val eval_structure_isl :
(** Similar to eval but apply to data structures and ISL abduction. Return a list of abduced states (** Similar to eval but apply to data structures and ISL abduction. Return a list of abduced states
(ISLOk and ISLErs); The boolean indicates whether it is data structures or not. *) (ISLOk and ISLErs); The boolean indicates whether it is data structures or not. *)
val prune : Location.t -> condition:Exp.t -> t -> t AccessResult.t val prune : PathContext.t -> Location.t -> condition:Exp.t -> t -> t AccessResult.t
val eval_deref : val eval_deref :
?must_be_valid_reason:Invalidation.must_be_valid_reason PathContext.t
-> ?must_be_valid_reason:Invalidation.must_be_valid_reason
-> Location.t -> Location.t
-> Exp.t -> Exp.t
-> t -> t
@ -109,10 +118,15 @@ val eval_deref :
(** Like [eval] but evaluates [*exp]. *) (** Like [eval] but evaluates [*exp]. *)
val eval_deref_isl : val eval_deref_isl :
Location.t -> Exp.t -> t -> (t * (AbstractValue.t * ValueHistory.t)) AccessResult.t list PathContext.t
-> Location.t
-> Exp.t
-> t
-> (t * (AbstractValue.t * ValueHistory.t)) AccessResult.t list
val eval_access : val eval_access :
?must_be_valid_reason:Invalidation.must_be_valid_reason PathContext.t
-> ?must_be_valid_reason:Invalidation.must_be_valid_reason
-> access_mode -> access_mode
-> Location.t -> Location.t
-> AbstractValue.t * ValueHistory.t -> AbstractValue.t * ValueHistory.t
@ -123,7 +137,8 @@ val eval_access :
so dereferences it according to the access. *) so dereferences it according to the access. *)
val eval_deref_access : val eval_deref_access :
?must_be_valid_reason:Invalidation.must_be_valid_reason PathContext.t
-> ?must_be_valid_reason:Invalidation.must_be_valid_reason
-> access_mode -> access_mode
-> Location.t -> Location.t
-> AbstractValue.t * ValueHistory.t -> AbstractValue.t * ValueHistory.t
@ -132,12 +147,14 @@ val eval_deref_access :
-> (t * (AbstractValue.t * ValueHistory.t)) AccessResult.t -> (t * (AbstractValue.t * ValueHistory.t)) AccessResult.t
(** Like [eval_access] but does additional dereference. *) (** Like [eval_access] but does additional dereference. *)
val eval_proc_name : Location.t -> Exp.t -> t -> (t * Procname.t option) AccessResult.t val eval_proc_name :
PathContext.t -> Location.t -> Exp.t -> t -> (t * Procname.t option) AccessResult.t
val havoc_id : Ident.t -> ValueHistory.t -> t -> t val havoc_id : Ident.t -> ValueHistory.t -> t -> t
val havoc_deref_field : val havoc_deref_field :
Location.t PathContext.t
-> Location.t
-> AbstractValue.t * ValueHistory.t -> AbstractValue.t * ValueHistory.t
-> Fieldname.t -> Fieldname.t
-> ValueHistory.t -> ValueHistory.t
@ -150,7 +167,8 @@ val realloc_pvar : Tenv.t -> Pvar.t -> Typ.t -> Location.t -> t -> t
val write_id : Ident.t -> AbstractValue.t * ValueHistory.t -> t -> t val write_id : Ident.t -> AbstractValue.t * ValueHistory.t -> t -> t
val write_field : val write_field :
Location.t PathContext.t
-> Location.t
-> ref:AbstractValue.t * ValueHistory.t -> ref:AbstractValue.t * ValueHistory.t
-> Fieldname.t -> Fieldname.t
-> obj:AbstractValue.t * ValueHistory.t -> obj:AbstractValue.t * ValueHistory.t
@ -159,7 +177,8 @@ val write_field :
(** write the edge [ref --.field--> obj] *) (** write the edge [ref --.field--> obj] *)
val write_deref_field : val write_deref_field :
Location.t PathContext.t
-> Location.t
-> ref:AbstractValue.t * ValueHistory.t -> ref:AbstractValue.t * ValueHistory.t
-> Fieldname.t -> Fieldname.t
-> obj:AbstractValue.t * ValueHistory.t -> obj:AbstractValue.t * ValueHistory.t
@ -168,7 +187,8 @@ val write_deref_field :
(** write the edge [ref --.field--> _ --*--> obj] *) (** write the edge [ref --.field--> _ --*--> obj] *)
val write_arr_index : val write_arr_index :
Location.t PathContext.t
-> Location.t
-> ref:AbstractValue.t * ValueHistory.t -> ref:AbstractValue.t * ValueHistory.t
-> index:AbstractValue.t -> index:AbstractValue.t
-> obj:AbstractValue.t * ValueHistory.t -> obj:AbstractValue.t * ValueHistory.t
@ -177,7 +197,8 @@ val write_arr_index :
(** write the edge [ref\[index\]--> obj] *) (** write the edge [ref\[index\]--> obj] *)
val write_deref : val write_deref :
Location.t PathContext.t
-> Location.t
-> ref:AbstractValue.t * ValueHistory.t -> ref:AbstractValue.t * ValueHistory.t
-> obj:AbstractValue.t * ValueHistory.t -> obj:AbstractValue.t * ValueHistory.t
-> t -> t
@ -185,7 +206,8 @@ val write_deref :
(** write the edge [ref --*--> obj] *) (** write the edge [ref --*--> obj] *)
val write_deref_biad_isl : val write_deref_biad_isl :
Location.t PathContext.t
-> Location.t
-> ref:AbstractValue.t * ValueHistory.t -> ref:AbstractValue.t * ValueHistory.t
-> AbstractValue.t HilExp.Access.t -> AbstractValue.t HilExp.Access.t
-> obj:AbstractValue.t * ValueHistory.t -> obj:AbstractValue.t * ValueHistory.t
@ -204,7 +226,8 @@ type invalidation_access =
| UntraceableAccess (** we don't know where the value came from; avoid using if possible *) | UntraceableAccess (** we don't know where the value came from; avoid using if possible *)
val invalidate : val invalidate :
invalidation_access PathContext.t
-> invalidation_access
-> Location.t -> Location.t
-> Invalidation.t -> Invalidation.t
-> AbstractValue.t * ValueHistory.t -> AbstractValue.t * ValueHistory.t
@ -213,7 +236,12 @@ val invalidate :
(** record that the address is invalid *) (** record that the address is invalid *)
val invalidate_biad_isl : val invalidate_biad_isl :
Location.t -> Invalidation.t -> AbstractValue.t * ValueHistory.t -> t -> t AccessResult.t list PathContext.t
-> Location.t
-> Invalidation.t
-> AbstractValue.t * ValueHistory.t
-> t
-> t AccessResult.t list
(** record that the address is invalid. If the address has not been allocated, abduce ISL specs for (** record that the address is invalid. If the address has not been allocated, abduce ISL specs for
both invalid (null, free, unint) and allocated heap. *) both invalid (null, free, unint) and allocated heap. *)
@ -224,7 +252,8 @@ val add_dynamic_type : Typ.t -> AbstractValue.t -> t -> t
val remove_allocation_attr : AbstractValue.t -> t -> t val remove_allocation_attr : AbstractValue.t -> t -> t
val invalidate_access : val invalidate_access :
Location.t PathContext.t
-> Location.t
-> Invalidation.t -> Invalidation.t
-> AbstractValue.t * ValueHistory.t -> AbstractValue.t * ValueHistory.t
-> BaseMemory.Access.t -> BaseMemory.Access.t
@ -233,7 +262,8 @@ val invalidate_access :
(** record that what the address points via the access to is invalid *) (** record that what the address points via the access to is invalid *)
val invalidate_deref_access : val invalidate_deref_access :
Location.t PathContext.t
-> Location.t
-> Invalidation.t -> Invalidation.t
-> AbstractValue.t * ValueHistory.t -> AbstractValue.t * ValueHistory.t
-> BaseMemory.Access.t -> BaseMemory.Access.t
@ -242,11 +272,17 @@ val invalidate_deref_access :
(** Like [invalidate_access] but invalidates dereferenced address. *) (** Like [invalidate_access] but invalidates dereferenced address. *)
val invalidate_array_elements : val invalidate_array_elements :
Location.t -> Invalidation.t -> AbstractValue.t * ValueHistory.t -> t -> t AccessResult.t PathContext.t
-> Location.t
-> Invalidation.t
-> AbstractValue.t * ValueHistory.t
-> t
-> t AccessResult.t
(** record that all the array elements that address points to is invalid *) (** record that all the array elements that address points to is invalid *)
val shallow_copy : val shallow_copy :
Location.t PathContext.t
-> Location.t
-> AbstractValue.t * ValueHistory.t -> AbstractValue.t * ValueHistory.t
-> t -> t
-> (t * (AbstractValue.t * ValueHistory.t)) AccessResult.t -> (t * (AbstractValue.t * ValueHistory.t)) AccessResult.t
@ -262,7 +298,8 @@ val check_address_escape :
Location.t -> Procdesc.t -> AbstractValue.t -> ValueHistory.t -> t -> t AccessResult.t Location.t -> Procdesc.t -> AbstractValue.t -> ValueHistory.t -> t -> t AccessResult.t
val get_captured_actuals : val get_captured_actuals :
Location.t PathContext.t
-> Location.t
-> captured_vars:(Var.t * CapturedVar.capture_mode * Typ.t) list -> captured_vars:(Var.t * CapturedVar.capture_mode * Typ.t) list
-> actual_closure:AbstractValue.t * ValueHistory.t -> actual_closure:AbstractValue.t * ValueHistory.t
-> t -> t

@ -0,0 +1,24 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
module F = Format
type timestamp = int [@@deriving compare]
let t0 = 0
type t = {timestamp: timestamp}
(** path contexts is metadata that do not contribute to the semantics *)
let leq ~lhs:_ ~rhs:_ = true
let pp fmt ({timestamp}[@warning "+9"]) = F.fprintf fmt "timestamp= %d" timestamp
let initial = {timestamp= 0}
let post_exec_instr {timestamp} = {timestamp= timestamp + 1}

@ -0,0 +1,24 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
module F = Format
type timestamp = private int [@@deriving compare]
val t0 : timestamp
type t = {timestamp: timestamp (** step number *)}
val leq : lhs:t -> rhs:t -> bool
val initial : t
val post_exec_instr : t -> t
(** call this after each step of the symbolic execution to update the path information *)
val pp : F.formatter -> t -> unit

@ -38,7 +38,7 @@ codetoanalyze/c/pulse/nullptr.c, malloc_then_call_create_null_path_then_deref_un
codetoanalyze/c/pulse/nullptr.c, null_alias_bad, 3, MEMORY_LEAK, no_bucket, ERROR, [allocation part of the trace starts here,allocated by `malloc` here,memory becomes unreachable here] codetoanalyze/c/pulse/nullptr.c, null_alias_bad, 3, MEMORY_LEAK, no_bucket, ERROR, [allocation part of the trace starts here,allocated by `malloc` here,memory becomes unreachable here]
codetoanalyze/c/pulse/nullptr.c, null_alias_bad, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [in call to `malloc` (modelled),is the null pointer,assigned,invalid access occurs here] codetoanalyze/c/pulse/nullptr.c, null_alias_bad, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [in call to `malloc` (modelled),is the null pointer,assigned,invalid access occurs here]
codetoanalyze/c/pulse/nullptr.c, nullptr_deref_young_bad, 5, NULLPTR_DEREFERENCE, no_bucket, ERROR, [is the null pointer,assigned,invalid access occurs here] codetoanalyze/c/pulse/nullptr.c, nullptr_deref_young_bad, 5, NULLPTR_DEREFERENCE, no_bucket, ERROR, [is the null pointer,assigned,invalid access occurs here]
codetoanalyze/c/pulse/nullptr.c, report_correct_error_among_multiple_bad, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [is the null pointer,assigned,when calling `several_dereferences_ok` here,parameter `x` of several_dereferences_ok,invalid access occurs here] codetoanalyze/c/pulse/nullptr.c, report_correct_error_among_multiple_bad, 3, NULLPTR_DEREFERENCE, no_bucket, ERROR, [is the null pointer,assigned,when calling `several_dereferences_ok` here,parameter `z` of several_dereferences_ok,invalid access occurs here]
codetoanalyze/c/pulse/traces.c, access_null_deref_bad, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [is the null pointer,assigned,invalid access occurs here] codetoanalyze/c/pulse/traces.c, access_null_deref_bad, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [is the null pointer,assigned,invalid access occurs here]
codetoanalyze/c/pulse/traces.c, access_use_after_free_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `l` of access_use_after_free_bad,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `l` of access_use_after_free_bad,invalid access occurs here] codetoanalyze/c/pulse/traces.c, access_use_after_free_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `l` of access_use_after_free_bad,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `l` of access_use_after_free_bad,invalid access occurs here]
codetoanalyze/c/pulse/traces.c, call_makes_null_deref_manifest_bad, 4, NULLPTR_DEREFERENCE, no_bucket, ERROR, [calling context starts here,in call to `something_about_strings_latent`,null pointer dereference part of the trace starts here,in call to `malloc` (modelled),is the null pointer,assigned,invalid access occurs here] codetoanalyze/c/pulse/traces.c, call_makes_null_deref_manifest_bad, 4, NULLPTR_DEREFERENCE, no_bucket, ERROR, [calling context starts here,in call to `something_about_strings_latent`,null pointer dereference part of the trace starts here,in call to `malloc` (modelled),is the null pointer,assigned,invalid access occurs here]

@ -122,9 +122,12 @@ void null_alias_bad(int* x) {
*x = 42; *x = 42;
} }
void dereference(int* p) { *p; }
void several_dereferences_ok(int* x, int* y, int* z) { void several_dereferences_ok(int* x, int* y, int* z) {
int* p = x; int* p = x;
*z = 52; *z = 52;
dereference(y);
*y = 42; *y = 42;
*x = 32; *x = 32;
*x = 777; *x = 777;

@ -13,6 +13,6 @@ codetoanalyze/objc/pulse/NPENilBlocks.m, BlockA.assignNilBad, 5, NIL_BLOCK_CALL,
codetoanalyze/objc/pulse/NPENilBlocks.m, BlockA.paramAssignNilBad:, 3, NIL_BLOCK_CALL, no_bucket, ERROR, [is the null pointer,assigned,invalid access occurs here] codetoanalyze/objc/pulse/NPENilBlocks.m, BlockA.paramAssignNilBad:, 3, NIL_BLOCK_CALL, no_bucket, ERROR, [is the null pointer,assigned,invalid access occurs here]
codetoanalyze/objc/pulse/NPENilBlocks.m, BlockA.paramReassignNilBad:, 6, NIL_BLOCK_CALL, no_bucket, ERROR, [is the null pointer,assigned,assigned,invalid access occurs here] codetoanalyze/objc/pulse/NPENilBlocks.m, BlockA.paramReassignNilBad:, 6, NIL_BLOCK_CALL, no_bucket, ERROR, [is the null pointer,assigned,assigned,invalid access occurs here]
codetoanalyze/objc/pulse/NPENilBlocks.m, calldoSomethingThenCallbackWithNilBad, 2, NIL_BLOCK_CALL, no_bucket, ERROR, [is the null pointer,when calling `BlockA.doSomethingThenCallback:` here,parameter `my_block` of BlockA.doSomethingThenCallback:,invalid access occurs here] codetoanalyze/objc/pulse/NPENilBlocks.m, calldoSomethingThenCallbackWithNilBad, 2, NIL_BLOCK_CALL, no_bucket, ERROR, [is the null pointer,when calling `BlockA.doSomethingThenCallback:` here,parameter `my_block` of BlockA.doSomethingThenCallback:,invalid access occurs here]
codetoanalyze/objc/pulse/uninit.m, Uninit.call_setter_c_struct_bad, 3, PULSE_UNINITIALIZED_VALUE, no_bucket, ERROR, [struct field address `x` created,when calling `Uninit.setS:` here,parameter `s` of Uninit.setS:,read to uninitialized value occurs here] codetoanalyze/objc/pulse/uninit.m, Uninit.call_setter_c_struct_bad, 3, PULSE_UNINITIALIZED_VALUE, no_bucket, ERROR, [struct field address `y` created,when calling `Uninit.setS:` here,parameter `s` of Uninit.setS:,read to uninitialized value occurs here]
codetoanalyze/objc/pulse/use_after_free.m, PulseTest.use_after_free_simple_in_objc_method_bad:, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of PulseTest.use_after_free_simple_in_objc_method_bad:,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of PulseTest.use_after_free_simple_in_objc_method_bad:,invalid access occurs here] codetoanalyze/objc/pulse/use_after_free.m, PulseTest.use_after_free_simple_in_objc_method_bad:, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of PulseTest.use_after_free_simple_in_objc_method_bad:,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of PulseTest.use_after_free_simple_in_objc_method_bad:,invalid access occurs here]
codetoanalyze/objc/pulse/use_after_free.m, use_after_free_simple_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of use_after_free_simple_bad,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of use_after_free_simple_bad,invalid access occurs here] codetoanalyze/objc/pulse/use_after_free.m, use_after_free_simple_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of use_after_free_simple_bad,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of use_after_free_simple_bad,invalid access occurs here]

Loading…
Cancel
Save