[pulse] Distinguish exit state at top level

Summary:
This diff lifts the `PulseAbductiveDomain.t` in `PulseExecutionState` by tracking whether the program continues the analysis normally or exits unusually (e.g. by calling `exit` or `throw`):

```
type exec_state =
  | ContinueProgram of PulseAbductiveDomain.t  (** represents the state at the program point *)
  | ExitProgram of PulseAbductiveDomain.t
      (** represents the state originating at exit/divergence. *)

```

Now, Pulse's actual domain is tracked by `PulseExecutionState` and as soon as we try to analyze an instruction at `ExitProgram`, we simply return its state.

The aim is to recover the state at the time of the exit, rather than simply ignoring them (i.e. returning  empty disjuncts). This allows us to get rid of some FNs that we were not able to detect before. Moreover, it also allows the impurity analysis to be more precise since we will know how the state changed up to exit.

TODO:
- Impurity analysis needs to be improved to consider functions that simply exit as impure.
- The next goal is to handle error state similarly so that when pulse finds an error, we recover the state at the error location (and potentially continue to analyze?).

Disclaimer: currently, we handle throw statements like exit (as was the case before). However, this is not correct. Ideally, control flow from throw nodes follows catch nodes rather than exiting the program entirely.

Reviewed By: jvillard

Differential Revision: D20791747

fbshipit-source-id: df9e5445a
master
Ezgi Çiçek 5 years ago committed by Facebook GitHub Bot
parent f0afa18cbf
commit 5a2b285fff

@ -138,21 +138,24 @@ let is_modeled_pure tenv pname =
(** Given Pulse summary, extract impurity info, i.e. parameters and global variables that are (** Given Pulse summary, extract impurity info, i.e. parameters and global variables that are
modified by the function and skipped functions. *) modified by the function and skipped functions. *)
let extract_impurity tenv pdesc pre_post : ImpurityDomain.t = let extract_impurity tenv pdesc (exec_state : PulseExecutionState.t) : ImpurityDomain.t =
let pre_heap = (AbductiveDomain.PrePost.get_pre pre_post).BaseDomain.heap in match exec_state with
let post = AbductiveDomain.PrePost.get_post pre_post in | ExitProgram astate | ContinueProgram astate ->
let post_stack = post.BaseDomain.stack in (* TODO: consider impure even though the program only exits with pre=post *)
let pname = Procdesc.get_proc_name pdesc in let pre_heap = (PulseAbductiveDomain.get_pre astate).BaseDomain.heap in
let modified_params = let post = PulseAbductiveDomain.get_post astate in
Procdesc.get_formals pdesc |> get_modified_params pname post_stack pre_heap post let post_stack = post.BaseDomain.stack in
in let pname = Procdesc.get_proc_name pdesc in
let modified_globals = get_modified_globals pre_heap post post_stack in let modified_params =
let skipped_calls = Procdesc.get_formals pdesc |> get_modified_params pname post_stack pre_heap post
AbductiveDomain.PrePost.get_skipped_calls pre_post in
|> PulseAbductiveDomain.SkippedCalls.filter (fun proc_name _ -> let modified_globals = get_modified_globals pre_heap post post_stack in
Purity.should_report proc_name && not (is_modeled_pure tenv proc_name) ) let skipped_calls =
in PulseAbductiveDomain.get_skipped_calls astate
{modified_globals; modified_params; skipped_calls} |> PulseAbductiveDomain.SkippedCalls.filter (fun proc_name _ ->
Purity.should_report proc_name && not (is_modeled_pure tenv proc_name) )
in
{modified_globals; modified_params; skipped_calls}
let checker {exe_env; Callbacks.summary} : Summary.t = let checker {exe_env; Callbacks.summary} : Summary.t =
@ -177,8 +180,8 @@ let checker {exe_env; Callbacks.summary} : Summary.t =
impure_fun_desc impure_fun_desc
| Some pre_posts -> | Some pre_posts ->
let (ImpurityDomain.{modified_globals; modified_params; skipped_calls} as impurity_astate) = let (ImpurityDomain.{modified_globals; modified_params; skipped_calls} as impurity_astate) =
List.fold pre_posts ~init:ImpurityDomain.pure ~f:(fun acc pre_post -> List.fold pre_posts ~init:ImpurityDomain.pure ~f:(fun acc exec_state ->
let modified = extract_impurity tenv pdesc pre_post in let modified = extract_impurity tenv pdesc exec_state in
ImpurityDomain.join acc modified ) ImpurityDomain.join acc modified )
in in
if Purity.should_report proc_name && not (ImpurityDomain.is_pure impurity_astate) then if Purity.should_report proc_name && not (ImpurityDomain.is_pure impurity_astate) then

@ -27,6 +27,10 @@ let check_error summary = function
raise_notrace AbstractDomain.Stop_analysis raise_notrace AbstractDomain.Stop_analysis
let check_error_continue summary result =
PulseExecutionState.ContinueProgram (check_error summary result)
let proc_name_of_call call_exp = let proc_name_of_call call_exp =
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} ->
@ -39,7 +43,7 @@ type get_formals = Procname.t -> (Pvar.t * Typ.t) list option
module PulseTransferFunctions = struct module PulseTransferFunctions = struct
module CFG = ProcCfg.Normal module CFG = ProcCfg.Normal
module Domain = PulseAbductiveDomain module Domain = PulseExecutionState
type extras = get_formals type extras = get_formals
@ -50,9 +54,9 @@ module PulseTransferFunctions = struct
PulseOperations.call ~caller_summary call_loc callee_pname ~ret ~actuals ~formals_opt astate PulseOperations.call ~caller_summary call_loc callee_pname ~ret ~actuals ~formals_opt astate
| _ -> | _ ->
L.d_printfln "Skipping indirect call %a@\n" Exp.pp call_exp ; L.d_printfln "Skipping indirect call %a@\n" Exp.pp call_exp ;
Ok PulseOperations.unknown_call call_loc (SkippedUnknownCall call_exp) ~ret ~actuals
[ PulseOperations.unknown_call call_loc (SkippedUnknownCall call_exp) ~ret ~actuals ~formals_opt:None astate
~formals_opt:None astate ] |> PulseOperations.ok_continue
(** has an object just gone out of scope? *) (** has an object just gone out of scope? *)
@ -70,11 +74,16 @@ 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) astate = let exec_object_out_of_scope call_loc (pvar, typ) exec_state =
let gone_out_of_scope = Invalidation.GoneOutOfScope (pvar, typ) in match exec_state with
(* invalidate [&x] *) | PulseExecutionState.ContinueProgram astate ->
let* astate, out_of_scope_base = PulseOperations.eval call_loc (Exp.Lvar pvar) astate in let gone_out_of_scope = Invalidation.GoneOutOfScope (pvar, typ) in
PulseOperations.invalidate call_loc gone_out_of_scope out_of_scope_base astate let* astate, out_of_scope_base = PulseOperations.eval call_loc (Exp.Lvar pvar) astate in
(* invalidate [&x] *)
PulseOperations.invalidate call_loc gone_out_of_scope out_of_scope_base astate
>>| PulseExecutionState.continue
| PulseExecutionState.ExitProgram _ ->
Ok exec_state
let dispatch_call tenv summary ret call_exp actuals call_loc flags get_formals astate = let dispatch_call tenv summary ret call_exp actuals call_loc flags get_formals astate =
@ -99,7 +108,7 @@ module PulseTransferFunctions = struct
None None
in in
(* do interprocedural call then destroy objects going out of scope *) (* do interprocedural call then destroy objects going out of scope *)
let posts = let exec_state_res =
match model with match model with
| Some (model, callee_procname) -> | Some (model, callee_procname) ->
L.d_printfln "Found model for call@\n" ; L.d_printfln "Found model for call@\n" ;
@ -121,59 +130,67 @@ module PulseTransferFunctions = struct
match get_out_of_scope_object call_exp actuals flags with match get_out_of_scope_object call_exp actuals flags with
| Some pvar_typ -> | Some pvar_typ ->
L.d_printfln "%a is going out of scope" Pvar.pp_value (fst pvar_typ) ; L.d_printfln "%a is going out of scope" Pvar.pp_value (fst pvar_typ) ;
let* posts = posts in let* exec_states = exec_state_res in
List.map posts ~f:(fun astate -> exec_object_out_of_scope call_loc pvar_typ astate) List.map exec_states ~f:(fun exec_state ->
exec_object_out_of_scope call_loc pvar_typ exec_state )
|> Result.all |> Result.all
| None -> | None ->
posts exec_state_res
let exec_instr (astate : Domain.t) {tenv; ProcData.summary; extras= get_formals} _cfg_node let exec_instr (astate : Domain.t) {tenv; ProcData.summary; extras= get_formals} _cfg_node
(instr : Sil.instr) = (instr : Sil.instr) : Domain.t list =
match instr with match astate with
| Load {id= lhs_id; e= rhs_exp; loc} -> | ExitProgram _ ->
(* [lhs_id := *rhs_exp] *) (* program already exited, simply propagate the exited state upwards *)
let result =
let+ astate, rhs_addr_hist = PulseOperations.eval_deref loc rhs_exp astate in
PulseOperations.write_id lhs_id rhs_addr_hist astate
in
[check_error summary result]
| Store {e1= lhs_exp; e2= rhs_exp; loc} ->
(* [*lhs_exp := rhs_exp] *)
let event = ValueHistory.Assignment loc in
let result =
let* astate, (rhs_addr, rhs_history) = PulseOperations.eval loc rhs_exp astate in
let* astate, lhs_addr_hist = PulseOperations.eval loc lhs_exp astate in
let* astate =
PulseOperations.write_deref loc ~ref:lhs_addr_hist
~obj:(rhs_addr, event :: rhs_history)
astate
in
match lhs_exp with
| Lvar pvar when Pvar.is_return pvar ->
PulseOperations.check_address_escape loc summary.Summary.proc_desc rhs_addr
rhs_history astate
| _ ->
Ok astate
in
[check_error summary result]
| Prune (condition, loc, is_then_branch, if_kind) ->
let post, cond_satisfiable =
PulseOperations.prune ~is_then_branch if_kind loc ~condition astate |> check_error summary
in
if cond_satisfiable then (* [condition] is true or unknown value: go into the branch *)
[post]
else (* [condition] is known to be unsatisfiable: prune path *) []
| Call (ret, call_exp, actuals, loc, call_flags) ->
dispatch_call tenv summary ret call_exp actuals loc call_flags get_formals astate
|> check_error summary
| Metadata (ExitScope (vars, location)) ->
let astate = PulseOperations.remove_vars vars location astate in
[check_error summary astate]
| Metadata (VariableLifetimeBegins (pvar, _, location)) ->
[PulseOperations.realloc_pvar pvar location astate]
| Metadata (Abstract _ | Nullify _ | Skip) ->
[astate] [astate]
| ContinueProgram astate -> (
match instr with
| Load {id= lhs_id; e= rhs_exp; loc} ->
(* [lhs_id := *rhs_exp] *)
let result =
let+ astate, rhs_addr_hist = PulseOperations.eval_deref loc rhs_exp astate in
PulseOperations.write_id lhs_id rhs_addr_hist astate
in
[check_error_continue summary result]
| Store {e1= lhs_exp; e2= rhs_exp; loc} ->
(* [*lhs_exp := rhs_exp] *)
let event = ValueHistory.Assignment loc in
let result =
let* astate, (rhs_addr, rhs_history) = PulseOperations.eval loc rhs_exp astate in
let* astate, lhs_addr_hist = PulseOperations.eval loc lhs_exp astate in
let* astate =
PulseOperations.write_deref loc ~ref:lhs_addr_hist
~obj:(rhs_addr, event :: rhs_history)
astate
in
match lhs_exp with
| Lvar pvar when Pvar.is_return pvar ->
PulseOperations.check_address_escape loc summary.Summary.proc_desc rhs_addr
rhs_history astate
| _ ->
Ok astate
in
[check_error_continue summary result]
| Prune (condition, loc, is_then_branch, if_kind) ->
let exec_state, cond_satisfiable =
PulseOperations.prune ~is_then_branch if_kind loc ~condition astate
|> check_error summary
in
if cond_satisfiable then
(* [condition] is true or unknown value: go into the branch *)
[Domain.continue exec_state]
else (* [condition] is known to be unsatisfiable: prune path *) []
| Call (ret, call_exp, actuals, loc, call_flags) ->
dispatch_call tenv summary ret call_exp actuals loc call_flags get_formals astate
|> check_error summary
| Metadata (ExitScope (vars, location)) ->
let astate = PulseOperations.remove_vars vars location astate in
[check_error_continue summary astate]
| Metadata (VariableLifetimeBegins (pvar, _, location)) ->
[PulseOperations.realloc_pvar pvar location astate |> Domain.continue]
| Metadata (Abstract _ | Nullify _ | Skip) ->
[Domain.ContinueProgram astate] )
let pp_session_name _node fmt = F.pp_print_string fmt "Pulse" let pp_session_name _node fmt = F.pp_print_string fmt "Pulse"
@ -197,7 +214,7 @@ let checker {Callbacks.exe_env; summary} =
AbstractValue.init () ; AbstractValue.init () ;
let pdesc = Summary.get_proc_desc summary in let pdesc = Summary.get_proc_desc summary in
let initial = let initial =
DisjunctiveTransferFunctions.Disjuncts.singleton (PulseAbductiveDomain.mk_initial pdesc) DisjunctiveTransferFunctions.Disjuncts.singleton (PulseExecutionState.mk_initial pdesc)
in in
let get_formals callee_pname = let get_formals callee_pname =
Ondemand.get_proc_desc callee_pname |> Option.map ~f:Procdesc.get_pvar_formals Ondemand.get_proc_desc callee_pname |> Option.map ~f:Procdesc.get_pvar_formals

File diff suppressed because it is too large Load Diff

@ -110,29 +110,25 @@ val discard_unreachable : t -> t * BaseAddressAttributes.t
val add_skipped_calls : Procname.t -> PulseTrace.t -> t -> t val add_skipped_calls : Procname.t -> PulseTrace.t -> t -> t
module PrePost : sig val leq : lhs:t -> rhs:t -> bool
type domain_t = t
type t = private domain_t val pp : Format.formatter -> t -> unit
val pp : Format.formatter -> t -> unit val of_post : Procdesc.t -> t -> t
val of_post : Procdesc.t -> domain_t -> t val get_pre : t -> BaseDomain.t
val apply : val get_post : t -> BaseDomain.t
Procname.t
-> Location.t
-> t
-> formals:Var.t list
-> actuals:((AbstractValue.t * ValueHistory.t) * Typ.t) list
-> domain_t
-> ((domain_t * (AbstractValue.t * ValueHistory.t) option) option, Diagnostic.t) result
(** return the abstract state after the call along with an optional return value, or [None] if the
precondition could not be satisfied (e.g. some aliasing constraints were not satisfied) *)
val get_pre : t -> BaseDomain.t val get_skipped_calls : t -> SkippedCalls.t
val get_post : t -> BaseDomain.t val apply :
Procname.t
val get_skipped_calls : t -> SkippedCalls.t -> Location.t
end -> t
-> formals:Var.t list
-> actuals:((AbstractValue.t * ValueHistory.t) * Typ.t) list
-> t
-> ((t * (AbstractValue.t * ValueHistory.t) option) option, Diagnostic.t) result
(** return the abstract state after the call along with an optional return value, or [None] if the
precondition could not be satisfied (e.g. some aliasing constraints were not satisfied) *)

@ -0,0 +1,43 @@
(*
* 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 exec_state =
| ContinueProgram of PulseAbductiveDomain.t
| ExitProgram of PulseAbductiveDomain.t
type t = exec_state
let continue astate = ContinueProgram astate
let mk_initial pdesc = ContinueProgram (PulseAbductiveDomain.mk_initial pdesc)
let leq ~lhs ~rhs =
match (lhs, rhs) with
| ContinueProgram astate1, ContinueProgram astate2 | ExitProgram astate1, ExitProgram astate2 ->
PulseAbductiveDomain.leq ~lhs:astate1 ~rhs:astate2
| ExitProgram _, ContinueProgram _ | ContinueProgram _, ExitProgram _ ->
false
let pp fmt = function
| ContinueProgram astate ->
PulseAbductiveDomain.pp fmt astate
| ExitProgram astate ->
F.fprintf fmt "{ExitProgram %a}" PulseAbductiveDomain.pp astate
let map ~f exec_state =
match exec_state with
| ContinueProgram astate ->
ContinueProgram (f astate)
| ExitProgram astate ->
ExitProgram (f astate)
let of_post pdesc = map ~f:(PulseAbductiveDomain.of_post pdesc)

@ -0,0 +1,20 @@
(*
* 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
type exec_state =
| ContinueProgram of PulseAbductiveDomain.t (** represents the state at the program point *)
| ExitProgram of PulseAbductiveDomain.t
(** represents the state originating at exit/divergence. *)
include AbstractDomain.NoJoin with type t = exec_state
val continue : PulseAbductiveDomain.t -> t
val of_post : Procdesc.t -> t -> t
val mk_initial : Procdesc.t -> t

@ -17,7 +17,7 @@ type model =
-> Location.t -> Location.t
-> ret:Ident.t * Typ.t -> ret:Ident.t * Typ.t
-> PulseAbductiveDomain.t -> PulseAbductiveDomain.t
-> PulseAbductiveDomain.t list PulseOperations.access_result -> PulseExecutionState.t list PulseOperations.access_result
module Misc = struct module Misc = struct
let shallow_copy model_desc dest_pointer_hist src_pointer_hist : model = let shallow_copy model_desc dest_pointer_hist src_pointer_hist : model =
@ -30,10 +30,14 @@ module Misc = struct
~obj:(fst obj_copy, event :: snd obj_copy) ~obj:(fst obj_copy, event :: snd obj_copy)
astate astate
in in
[PulseOperations.havoc_id ret_id [event] astate] let astate = PulseOperations.havoc_id ret_id [event] astate in
[PulseExecutionState.ContinueProgram astate]
let early_exit : model = fun ~caller_summary:_ ~callee_procname:_ _ ~ret:_ _ -> Ok [] let early_exit : model =
fun ~caller_summary:_ ~callee_procname:_ _ ~ret:_ astate ->
Ok [PulseExecutionState.ExitProgram astate]
let return_int : Int64.t -> model = let return_int : Int64.t -> model =
fun i64 ~caller_summary:_ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun i64 ~caller_summary:_ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
@ -44,7 +48,7 @@ module Misc = struct
|> AddressAttributes.add_one ret_addr |> AddressAttributes.add_one ret_addr
(CItv (CItv.equal_to i, Immediate {location; history= []})) (CItv (CItv.equal_to i, Immediate {location; history= []}))
in in
Ok [PulseOperations.write_id ret_id (ret_addr, []) astate] PulseOperations.write_id ret_id (ret_addr, []) astate |> PulseOperations.ok_continue
let return_unknown_size : model = let return_unknown_size : model =
@ -55,20 +59,22 @@ module Misc = struct
|> AddressAttributes.add_one ret_addr |> AddressAttributes.add_one ret_addr
(CItv (CItv.zero_inf, Immediate {location; history= []})) (CItv (CItv.zero_inf, Immediate {location; history= []}))
in in
Ok [PulseOperations.write_id ret_id (ret_addr, []) astate] PulseOperations.write_id ret_id (ret_addr, []) astate |> PulseOperations.ok_continue
let skip : model =
fun ~caller_summary:_ ~callee_procname:_ _ ~ret:_ astate -> PulseOperations.ok_continue astate
let skip : model = fun ~caller_summary:_ ~callee_procname:_ _ ~ret:_ astate -> Ok [astate]
let nondet ~fn_name : model = let nondet ~fn_name : model =
fun ~caller_summary:_ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun ~caller_summary:_ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model fn_name; location; in_call= []} in let event = ValueHistory.Call {f= Model fn_name; location; in_call= []} in
Ok [PulseOperations.havoc_id ret_id [event] astate] PulseOperations.havoc_id ret_id [event] astate |> PulseOperations.ok_continue
let id_first_arg arg_access_hist : model = let id_first_arg arg_access_hist : model =
fun ~caller_summary:_ ~callee_procname:_ _ ~ret astate -> fun ~caller_summary:_ ~callee_procname:_ _ ~ret astate ->
Ok [PulseOperations.write_id (fst ret) arg_access_hist astate] PulseOperations.write_id (fst ret) arg_access_hist astate |> PulseOperations.ok_continue
end end
module C = struct module C = struct
@ -82,10 +88,10 @@ module C = struct
|| Itv.ItvPure.is_zero (AddressAttributes.get_bo_itv (fst deleted_access) astate) || Itv.ItvPure.is_zero (AddressAttributes.get_bo_itv (fst deleted_access) astate)
in in
if is_known_zero then (* freeing 0 is a no-op *) if is_known_zero then (* freeing 0 is a no-op *)
Ok [astate] PulseOperations.ok_continue astate
else else
let+ astate = PulseOperations.invalidate location Invalidation.CFree deleted_access astate in let+ astate = PulseOperations.invalidate location Invalidation.CFree deleted_access astate in
[astate] [PulseExecutionState.ContinueProgram astate]
let malloc _ : model = let malloc _ : model =
@ -97,6 +103,7 @@ module C = struct
|> AddressAttributes.add_one ret_addr (BoItv Itv.ItvPure.pos) |> AddressAttributes.add_one ret_addr (BoItv Itv.ItvPure.pos)
|> AddressAttributes.add_one ret_addr |> AddressAttributes.add_one ret_addr
(CItv (CItv.ge_to IntLit.one, Immediate {location; history= []})) (CItv (CItv.ge_to IntLit.one, Immediate {location; history= []}))
|> PulseExecutionState.continue
in in
let+ astate_null = let+ astate_null =
AddressAttributes.add_one ret_addr (BoItv (Itv.ItvPure.of_int_lit IntLit.zero)) astate AddressAttributes.add_one ret_addr (BoItv (Itv.ItvPure.of_int_lit IntLit.zero)) astate
@ -106,23 +113,27 @@ module C = struct
|> PulseOperations.invalidate location (Invalidation.ConstantDereference IntLit.zero) |> PulseOperations.invalidate location (Invalidation.ConstantDereference IntLit.zero)
(ret_addr, []) (ret_addr, [])
in in
[astate_alloc; astate_null] [astate_alloc; PulseExecutionState.ContinueProgram astate_null]
end end
module Cplusplus = struct module Cplusplus = struct
let delete deleted_access : model = let delete deleted_access : model =
fun ~caller_summary:_ ~callee_procname:_ location ~ret:_ astate -> fun ~caller_summary:_ ~callee_procname:_ location ~ret:_ astate ->
PulseOperations.invalidate location Invalidation.CppDelete deleted_access astate >>| List.return let+ astate =
PulseOperations.invalidate location Invalidation.CppDelete deleted_access astate
in
[PulseExecutionState.ContinueProgram astate]
let placement_new actuals : model = let placement_new actuals : model =
fun ~caller_summary:_ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun ~caller_summary:_ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model "<placement new>()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "<placement new>()"; location; in_call= []} in
match List.rev actuals with ( match List.rev actuals with
| ProcnameDispatcher.Call.FuncArg.{arg_payload= address, hist} :: _ -> | ProcnameDispatcher.Call.FuncArg.{arg_payload= address, hist} :: _ ->
Ok [PulseOperations.write_id ret_id (address, event :: hist) astate] PulseOperations.write_id ret_id (address, event :: hist) astate
| _ -> | _ ->
Ok [PulseOperations.havoc_id ret_id [event] astate] PulseOperations.havoc_id ret_id [event] astate )
|> PulseOperations.ok_continue
end end
module StdAtomicInteger = struct module StdAtomicInteger = struct
@ -150,7 +161,7 @@ module StdAtomicInteger = struct
in in
let* astate = PulseOperations.write_deref location ~ref:int_field ~obj:init_value astate in let* astate = PulseOperations.write_deref location ~ref:int_field ~obj:init_value astate in
let+ astate = PulseOperations.write_deref location ~ref:this_address ~obj:this astate in let+ astate = PulseOperations.write_deref location ~ref:this_address ~obj:this astate in
[astate] [PulseExecutionState.ContinueProgram astate]
let arith_bop prepost location event ret_id bop this operand astate = let arith_bop prepost location event ret_id bop this operand astate =
@ -173,7 +184,7 @@ module StdAtomicInteger = struct
arith_bop `Post location event ret_id (PlusA None) this (AbstractValueOperand increment) arith_bop `Post location event ret_id (PlusA None) this (AbstractValueOperand increment)
astate astate
in in
[astate] [PulseExecutionState.ContinueProgram astate]
let fetch_sub this (increment, _) _memory_ordering : model = let fetch_sub this (increment, _) _memory_ordering : model =
@ -183,7 +194,7 @@ module StdAtomicInteger = struct
arith_bop `Post location event ret_id (MinusA None) this (AbstractValueOperand increment) arith_bop `Post location event ret_id (MinusA None) this (AbstractValueOperand increment)
astate astate
in in
[astate] [PulseExecutionState.ContinueProgram astate]
let operator_plus_plus_pre this : model = let operator_plus_plus_pre this : model =
@ -192,7 +203,7 @@ module StdAtomicInteger = struct
let+ astate = let+ astate =
arith_bop `Pre location event ret_id (PlusA None) this (LiteralOperand IntLit.one) astate arith_bop `Pre location event ret_id (PlusA None) this (LiteralOperand IntLit.one) astate
in in
[astate] [PulseExecutionState.ContinueProgram astate]
let operator_plus_plus_post this _int : model = let operator_plus_plus_post this _int : model =
@ -203,7 +214,7 @@ module StdAtomicInteger = struct
let+ astate = let+ astate =
arith_bop `Post location event ret_id (PlusA None) this (LiteralOperand IntLit.one) astate arith_bop `Post location event ret_id (PlusA None) this (LiteralOperand IntLit.one) astate
in in
[astate] [PulseExecutionState.ContinueProgram astate]
let operator_minus_minus_pre this : model = let operator_minus_minus_pre this : model =
@ -212,7 +223,7 @@ module StdAtomicInteger = struct
let+ astate = let+ astate =
arith_bop `Pre location event ret_id (MinusA None) this (LiteralOperand IntLit.one) astate arith_bop `Pre location event ret_id (MinusA None) this (LiteralOperand IntLit.one) astate
in in
[astate] [PulseExecutionState.ContinueProgram astate]
let operator_minus_minus_post this _int : model = let operator_minus_minus_post this _int : model =
@ -223,14 +234,15 @@ module StdAtomicInteger = struct
let+ astate = let+ astate =
arith_bop `Post location event ret_id (MinusA None) this (LiteralOperand IntLit.one) astate arith_bop `Post location event ret_id (MinusA None) this (LiteralOperand IntLit.one) astate
in in
[astate] [PulseExecutionState.ContinueProgram astate]
let load_instr model_desc this _memory_ordering_opt : model = let load_instr model_desc this _memory_ordering_opt : model =
fun ~caller_summary:_ ~callee_procname:_ location ~ret:(ret_id, _) astate -> fun ~caller_summary:_ ~callee_procname:_ location ~ret:(ret_id, _) astate ->
let event = ValueHistory.Call {f= Model model_desc; location; in_call= []} in let event = ValueHistory.Call {f= Model model_desc; location; in_call= []} in
let+ astate, _int_addr, (int, hist) = load_backing_int location this astate in let+ astate, _int_addr, (int, hist) = load_backing_int location this astate in
[PulseOperations.write_id ret_id (int, event :: hist) astate] let astate = PulseOperations.write_id ret_id (int, event :: hist) astate in
[PulseExecutionState.ContinueProgram astate]
let load = load_instr "std::atomic<T>::load()" let load = load_instr "std::atomic<T>::load()"
@ -249,7 +261,7 @@ module StdAtomicInteger = struct
fun ~caller_summary:_ ~callee_procname:_ location ~ret:_ astate -> fun ~caller_summary:_ ~callee_procname:_ location ~ret:_ astate ->
let event = ValueHistory.Call {f= Model "std::atomic::store()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "std::atomic::store()"; location; in_call= []} in
let+ astate = store_backing_int location this_address (new_value, event :: new_hist) astate in let+ astate = store_backing_int location this_address (new_value, event :: new_hist) astate in
[astate] [PulseExecutionState.ContinueProgram astate]
let exchange this_address (new_value, new_hist) _memory_ordering : model = let exchange this_address (new_value, new_hist) _memory_ordering : model =
@ -257,7 +269,8 @@ module StdAtomicInteger = struct
let event = ValueHistory.Call {f= Model "std::atomic::exchange()"; location; in_call= []} in let event = ValueHistory.Call {f= Model "std::atomic::exchange()"; location; in_call= []} in
let* astate, _int_addr, (old_int, old_hist) = load_backing_int location this_address astate in let* astate, _int_addr, (old_int, old_hist) = load_backing_int location this_address astate in
let+ astate = store_backing_int location this_address (new_value, event :: new_hist) astate in let+ astate = store_backing_int location this_address (new_value, event :: new_hist) astate in
[PulseOperations.write_id ret_id (old_int, event :: old_hist) astate] let astate = PulseOperations.write_id ret_id (old_int, event :: old_hist) astate in
[PulseExecutionState.ContinueProgram astate]
end end
module ObjectiveC = struct module ObjectiveC = struct
@ -268,7 +281,7 @@ module ObjectiveC = struct
in in
let ret_addr = AbstractValue.mk_fresh () in let ret_addr = AbstractValue.mk_fresh () in
let astate = PulseOperations.allocate callee_procname location (ret_addr, []) astate in let astate = PulseOperations.allocate callee_procname location (ret_addr, []) astate in
Ok [PulseOperations.write_id ret_id (ret_addr, hist) astate] PulseOperations.write_id ret_id (ret_addr, hist) astate |> PulseOperations.ok_continue
end end
module JavaObject = struct module JavaObject = struct
@ -278,7 +291,8 @@ module JavaObject = struct
let event = ValueHistory.Call {f= Model "Object.clone"; location; in_call= []} in let event = ValueHistory.Call {f= Model "Object.clone"; location; in_call= []} in
let* astate, obj = PulseOperations.eval_access location src_pointer_hist Dereference astate in let* astate, obj = PulseOperations.eval_access location src_pointer_hist Dereference astate in
let+ astate, obj_copy = PulseOperations.shallow_copy location obj astate in let+ astate, obj_copy = PulseOperations.shallow_copy location obj astate in
[PulseOperations.write_id ret_id (fst obj_copy, event :: snd obj_copy) astate] let astate = PulseOperations.write_id ret_id (fst obj_copy, event :: snd obj_copy) astate in
[PulseExecutionState.ContinueProgram astate]
end end
module StdBasicString = struct module StdBasicString = struct
@ -301,7 +315,8 @@ module StdBasicString = struct
let+ astate, (string, hist) = let+ astate, (string, hist) =
PulseOperations.eval_access location string_addr_hist Dereference astate PulseOperations.eval_access location string_addr_hist Dereference astate
in in
[PulseOperations.write_id ret_id (string, event :: hist) astate] let astate = PulseOperations.write_id ret_id (string, event :: hist) astate in
[PulseExecutionState.ContinueProgram astate]
let destructor this_hist : model = let destructor this_hist : model =
@ -312,7 +327,7 @@ module StdBasicString = struct
let string_addr_hist = (string_addr, call_event :: string_hist) in let string_addr_hist = (string_addr, call_event :: string_hist) in
let* astate = PulseOperations.invalidate_deref location CppDelete string_addr_hist astate in let* astate = PulseOperations.invalidate_deref location CppDelete string_addr_hist astate in
let+ astate = PulseOperations.invalidate location CppDelete string_addr_hist astate in let+ astate = PulseOperations.invalidate location CppDelete string_addr_hist astate in
[astate] [PulseExecutionState.ContinueProgram astate]
end end
module StdFunction = struct module StdFunction = struct
@ -328,7 +343,8 @@ module StdFunction = struct
let* astate = PulseOperations.Closures.check_captured_addresses location lambda astate in let* astate = PulseOperations.Closures.check_captured_addresses location lambda astate in
match AddressAttributes.get_closure_proc_name lambda astate with match AddressAttributes.get_closure_proc_name lambda astate with
| None -> | None ->
(* we don't know what proc name this lambda resolves to *) Ok (havoc_ret ret astate) (* we don't know what proc name this lambda resolves to *)
Ok (havoc_ret ret astate |> List.map ~f:PulseExecutionState.continue)
| Some callee_proc_name -> | Some callee_proc_name ->
let actuals = let actuals =
List.map actuals ~f:(fun ProcnameDispatcher.Call.FuncArg.{arg_payload; typ} -> List.map actuals ~f:(fun ProcnameDispatcher.Call.FuncArg.{arg_payload; typ} ->
@ -373,14 +389,16 @@ module StdVector = struct
; location ; location
; in_call= [] } ; in_call= [] }
in in
reallocate_internal_array [crumb] vector vector_f location astate >>| List.return reallocate_internal_array [crumb] vector vector_f location astate
>>| PulseExecutionState.continue >>| List.return
let at ~desc vector index : model = let at ~desc vector index : model =
fun ~caller_summary:_ ~callee_procname:_ location ~ret astate -> fun ~caller_summary:_ ~callee_procname:_ location ~ret astate ->
let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let event = ValueHistory.Call {f= Model desc; location; in_call= []} in
let+ astate, (addr, hist) = element_of_internal_array location vector (fst index) astate in let+ astate, (addr, hist) = element_of_internal_array location vector (fst index) astate in
[PulseOperations.write_id (fst ret) (addr, event :: hist) astate] let astate = PulseOperations.write_id (fst ret) (addr, event :: hist) astate in
[PulseExecutionState.ContinueProgram astate]
let reserve vector : model = let reserve vector : model =
@ -388,7 +406,7 @@ module StdVector = struct
let crumb = ValueHistory.Call {f= Model "std::vector::reserve()"; location; in_call= []} in let crumb = ValueHistory.Call {f= Model "std::vector::reserve()"; location; in_call= []} in
reallocate_internal_array [crumb] vector Reserve location astate reallocate_internal_array [crumb] vector Reserve location astate
>>| AddressAttributes.std_vector_reserve (fst vector) >>| AddressAttributes.std_vector_reserve (fst vector)
>>| List.return >>| PulseExecutionState.continue >>| List.return
let push_back vector : model = let push_back vector : model =
@ -397,10 +415,11 @@ module StdVector = struct
if AddressAttributes.is_std_vector_reserved (fst vector) astate then if AddressAttributes.is_std_vector_reserved (fst vector) astate then
(* assume that any call to [push_back] is ok after one called [reserve] on the same vector (* assume that any call to [push_back] is ok after one called [reserve] on the same vector
(a perfect analysis would also make sure we don't exceed the reserved size) *) (a perfect analysis would also make sure we don't exceed the reserved size) *)
Ok [astate] PulseOperations.ok_continue astate
else else
(* simulate a re-allocation of the underlying array every time an element is added *) (* simulate a re-allocation of the underlying array every time an element is added *)
reallocate_internal_array [crumb] vector PushBack location astate >>| List.return reallocate_internal_array [crumb] vector PushBack location astate
>>| PulseExecutionState.continue >>| List.return
end end
module JavaCollection = struct module JavaCollection = struct
@ -416,7 +435,8 @@ module JavaCollection = struct
astate astate
>>= PulseOperations.invalidate_deref location (StdVector Assign) old_elem >>= PulseOperations.invalidate_deref location (StdVector Assign) old_elem
in in
[PulseOperations.write_id (fst ret) (old_addr, event :: old_hist) astate] let astate = PulseOperations.write_id (fst ret) (old_addr, event :: old_hist) astate in
[PulseExecutionState.ContinueProgram astate]
end end
module StringSet = Caml.Set.Make (String) module StringSet = Caml.Set.Make (String)

@ -13,7 +13,7 @@ type model =
-> Location.t -> Location.t
-> ret:Ident.t * Typ.t -> ret:Ident.t * Typ.t
-> PulseAbductiveDomain.t -> PulseAbductiveDomain.t
-> PulseAbductiveDomain.t list PulseOperations.access_result -> PulseExecutionState.t list PulseOperations.access_result
val dispatch : val dispatch :
Tenv.t Tenv.t

@ -14,6 +14,8 @@ type t = AbductiveDomain.t
type 'a access_result = ('a, Diagnostic.t) result type 'a access_result = ('a, Diagnostic.t) result
let ok_continue post = Ok [PulseExecutionState.ContinueProgram post]
(** Check that the [address] is not known to be invalid *) (** Check that the [address] is not known to be invalid *)
let check_addr_access location (address, history) astate = let check_addr_access location (address, history) astate =
let access_trace = Trace.Immediate {location; history} in let access_trace = Trace.Immediate {location; history} in
@ -570,33 +572,47 @@ let unknown_call call_loc reason ~ret ~actuals ~formals_opt astate =
|> havoc_ret ret |> add_skipped_proc |> havoc_ret ret |> add_skipped_proc
let call ~caller_summary call_loc callee_pname ~ret ~actuals ~formals_opt astate = let apply_callee callee_pname call_loc callee_exec_state ~ret ~formals ~actuals astate =
let apply callee_prepost ~f =
PulseAbductiveDomain.apply callee_pname call_loc callee_prepost ~formals ~actuals astate
>>| function
| None ->
(* couldn't apply pre/post pair *) None
| Some (post, return_val_opt) ->
let event = ValueHistory.Call {f= Call callee_pname; location= call_loc; in_call= []} in
let post =
match return_val_opt with
| Some (return_val, return_hist) ->
write_id (fst ret) (return_val, event :: return_hist) post
| None ->
havoc_id (fst ret) [event] post
in
Some (f post)
in
let open PulseExecutionState in
match callee_exec_state with
| ContinueProgram astate ->
apply astate ~f:(fun astate -> ContinueProgram astate)
| ExitProgram astate ->
apply astate ~f:(fun astate -> ExitProgram astate)
let call ~caller_summary call_loc callee_pname ~ret ~actuals ~formals_opt
(astate : PulseAbductiveDomain.t) : (PulseExecutionState.t list, Diagnostic.t) result =
match PulsePayload.read_full ~caller_summary ~callee_pname with match PulsePayload.read_full ~caller_summary ~callee_pname with
| Some (callee_proc_desc, preposts) -> | Some (callee_proc_desc, exec_states) ->
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)
in in
(* call {!AbductiveDomain.PrePost.apply} on each pre/post pair in the summary. *) (* call {!AbductiveDomain.PrePost.apply} on each pre/post pair in the summary. *)
List.fold_result preposts ~init:[] ~f:(fun posts pre_post -> List.fold_result exec_states ~init:[] ~f:(fun posts callee_exec_state ->
(* apply all pre/post specs *) (* apply all pre/post specs *)
AbductiveDomain.PrePost.apply callee_pname call_loc pre_post ~formals ~actuals astate apply_callee callee_pname call_loc callee_exec_state ~formals ~actuals ~ret astate
>>| function >>| function
| None -> | None -> (* couldn't apply pre/post pair *) posts | Some post -> post :: posts )
(* couldn't apply pre/post pair *) posts
| Some (post, return_val_opt) ->
let event =
ValueHistory.Call {f= Call callee_pname; location= call_loc; in_call= []}
in
let post =
match return_val_opt with
| Some (return_val, return_hist) ->
write_id (fst ret) (return_val, event :: return_hist) post
| None ->
havoc_id (fst ret) [event] post
in
post :: posts )
| None -> | None ->
(* no spec found for some reason (unknown function, ...) *) (* no spec found for some reason (unknown function, ...) *)
L.d_printfln "No spec found for %a@\n" Procname.pp callee_pname ; L.d_printfln "No spec found for %a@\n" Procname.pp callee_pname ;
Ok [unknown_call call_loc (SkippedKnownCall callee_pname) ~ret ~actuals ~formals_opt astate] unknown_call call_loc (SkippedKnownCall callee_pname) ~ret ~actuals ~formals_opt astate
|> ok_continue

@ -13,6 +13,8 @@ type t = PulseAbductiveDomain.t
type 'a access_result = ('a, Diagnostic.t) result type 'a access_result = ('a, Diagnostic.t) result
val ok_continue : t -> (PulseExecutionState.exec_state list, 'a) result
module Closures : sig module Closures : sig
val check_captured_addresses : Location.t -> AbstractValue.t -> t -> (t, Diagnostic.t) result val check_captured_addresses : Location.t -> AbstractValue.t -> t -> (t, Diagnostic.t) result
(** assert the validity of the addresses captured by the lambda *) (** assert the validity of the addresses captured by the lambda *)
@ -112,7 +114,7 @@ val call :
-> actuals:((AbstractValue.t * ValueHistory.t) * Typ.t) list -> actuals:((AbstractValue.t * ValueHistory.t) * Typ.t) list
-> formals_opt:(Pvar.t * Typ.t) list option -> formals_opt:(Pvar.t * Typ.t) list option
-> t -> t
-> t list access_result -> PulseExecutionState.t list access_result
(** perform an interprocedural call: apply the summary for the call proc name passed as argument if (** perform an interprocedural call: apply the summary for the call proc name passed as argument if
it exists *) it exists *)

@ -6,15 +6,14 @@
*) *)
open! IStd open! IStd
module F = Format module F = Format
open PulseDomainInterface
type t = AbductiveDomain.PrePost.t list type t = PulseExecutionState.t list
let of_posts pdesc posts = List.map posts ~f:(PulseAbductiveDomain.PrePost.of_post pdesc) let of_posts pdesc posts = List.map posts ~f:(PulseExecutionState.of_post pdesc)
let pp fmt summary = let pp fmt summary =
F.open_vbox 0 ; F.open_vbox 0 ;
F.fprintf fmt "%d pre/post(s)@;" (List.length summary) ; F.fprintf fmt "%d pre/post(s)@;" (List.length summary) ;
List.iteri summary ~f:(fun i pre_post -> List.iteri summary ~f:(fun i pre_post ->
F.fprintf fmt "#%d: @[%a@]@;" i PulseAbductiveDomain.PrePost.pp pre_post ) ; F.fprintf fmt "#%d: @[%a@]@;" i PulseExecutionState.pp pre_post ) ;
F.close_box () F.close_box ()

@ -6,8 +6,8 @@
*) *)
open! IStd open! IStd
type t = PulseAbductiveDomain.PrePost.t list type t = PulseExecutionState.t list
val of_posts : Procdesc.t -> PulseAbductiveDomain.t list -> t val of_posts : Procdesc.t -> PulseExecutionState.t list -> t
val pp : Format.formatter -> t -> unit val pp : Format.formatter -> t -> unit

@ -0,0 +1,20 @@
/*
* 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.
*/
#include <stdlib.h>
int x;
void exit_positive_impure_FN(int a[10], int b) {
if (b > 0) {
exit(0);
}
}
void unreachable_impure_FN(int a[10], int b) {
exit_positive_impure_FN(a, 10);
x = 9;
}

@ -0,0 +1,29 @@
/*
* 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.
*/
#include <stdlib.h>
// we get two disjuncts one for each branch
void exit_positive(int a[10], int b) {
if (b < 1) {
exit(0);
}
}
void unreachable_double_free_ok(int a[10], int b) {
exit_positive(a, 0);
free(a);
free(a);
}
void store_exit(int* x, bool b) {
if (b) {
*x = 42;
exit(0);
}
}
void store_exit_null_bad(bool b) { store_exit(NULL, b); }

@ -12,6 +12,7 @@ codetoanalyze/cpp/pulse/deduplication.cpp, deduplication::SomeTemplatedClass<int
codetoanalyze/cpp/pulse/deduplication.cpp, deduplication::SomeTemplatedClass<int>::lifetime_error_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `a` of deduplication::SomeTemplatedClass<int>::lifetime_error_bad,when calling `deduplication::SomeTemplatedClass<int>::templated_wrapper_delete_ok` here,parameter `a` of deduplication::SomeTemplatedClass<int>::templated_wrapper_delete_ok,was invalidated by `delete`,use-after-lifetime part of the trace starts here,parameter `a` of deduplication::SomeTemplatedClass<int>::lifetime_error_bad,when calling `deduplication::SomeTemplatedClass<int>::templated_wrapper_access_ok` here,parameter `a` of deduplication::SomeTemplatedClass<int>::templated_wrapper_access_ok,invalid access occurs here] codetoanalyze/cpp/pulse/deduplication.cpp, deduplication::SomeTemplatedClass<int>::lifetime_error_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `a` of deduplication::SomeTemplatedClass<int>::lifetime_error_bad,when calling `deduplication::SomeTemplatedClass<int>::templated_wrapper_delete_ok` here,parameter `a` of deduplication::SomeTemplatedClass<int>::templated_wrapper_delete_ok,was invalidated by `delete`,use-after-lifetime part of the trace starts here,parameter `a` of deduplication::SomeTemplatedClass<int>::lifetime_error_bad,when calling `deduplication::SomeTemplatedClass<int>::templated_wrapper_access_ok` here,parameter `a` of deduplication::SomeTemplatedClass<int>::templated_wrapper_access_ok,invalid access occurs here]
codetoanalyze/cpp/pulse/deduplication.cpp, deduplication::templated_function_bad<_Bool>, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,when calling `deduplication::templated_delete_function<_Bool>` here,parameter `a` of deduplication::templated_delete_function<_Bool>,was invalidated by `delete`,use-after-lifetime part of the trace starts here,assigned,when calling `deduplication::templated_access_function<_Bool>` here,parameter `a` of deduplication::templated_access_function<_Bool>,invalid access occurs here] codetoanalyze/cpp/pulse/deduplication.cpp, deduplication::templated_function_bad<_Bool>, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,when calling `deduplication::templated_delete_function<_Bool>` here,parameter `a` of deduplication::templated_delete_function<_Bool>,was invalidated by `delete`,use-after-lifetime part of the trace starts here,assigned,when calling `deduplication::templated_access_function<_Bool>` here,parameter `a` of deduplication::templated_access_function<_Bool>,invalid access occurs here]
codetoanalyze/cpp/pulse/deduplication.cpp, deduplication::templated_function_bad<int>, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,when calling `deduplication::templated_delete_function<int>` here,parameter `a` of deduplication::templated_delete_function<int>,was invalidated by `delete`,use-after-lifetime part of the trace starts here,assigned,when calling `deduplication::templated_access_function<int>` here,parameter `a` of deduplication::templated_access_function<int>,invalid access occurs here] codetoanalyze/cpp/pulse/deduplication.cpp, deduplication::templated_function_bad<int>, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,when calling `deduplication::templated_delete_function<int>` here,parameter `a` of deduplication::templated_delete_function<int>,was invalidated by `delete`,use-after-lifetime part of the trace starts here,assigned,when calling `deduplication::templated_access_function<int>` here,parameter `a` of deduplication::templated_access_function<int>,invalid access occurs here]
codetoanalyze/cpp/pulse/exit_test.cpp, store_exit_null_bad, 0, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,when calling `store_exit` here,parameter `x` of store_exit,invalid access occurs here]
codetoanalyze/cpp/pulse/folly_DestructorGuard.cpp, UsingDelayedDestruction::double_delete_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `this` of UsingDelayedDestruction::double_delete_bad,was invalidated by `delete`,use-after-lifetime part of the trace starts here,parameter `this` of UsingDelayedDestruction::double_delete_bad,invalid access occurs here] codetoanalyze/cpp/pulse/folly_DestructorGuard.cpp, UsingDelayedDestruction::double_delete_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `this` of UsingDelayedDestruction::double_delete_bad,was invalidated by `delete`,use-after-lifetime part of the trace starts here,parameter `this` of UsingDelayedDestruction::double_delete_bad,invalid access occurs here]
codetoanalyze/cpp/pulse/frontend.cpp, deref_null_namespace_alias_ptr_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `some::thing::bad_ptr` here,assigned,was invalidated by `delete`,use-after-lifetime part of the trace starts here,passed as argument to `some::thing::bad_ptr`,return from call to `some::thing::bad_ptr`,assigned,invalid access occurs here] codetoanalyze/cpp/pulse/frontend.cpp, deref_null_namespace_alias_ptr_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `some::thing::bad_ptr` here,assigned,was invalidated by `delete`,use-after-lifetime part of the trace starts here,passed as argument to `some::thing::bad_ptr`,return from call to `some::thing::bad_ptr`,assigned,invalid access occurs here]
codetoanalyze/cpp/pulse/interprocedural.cpp, access_to_invalidated_alias2_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of access_to_invalidated_alias2_bad,assigned,when calling `invalidate_and_set_to_null` here,parameter `x_ptr` of invalidate_and_set_to_null,was invalidated by `delete`,use-after-lifetime part of the trace starts here,parameter `x` of access_to_invalidated_alias2_bad,when calling `wraps_read` here,parameter `x` of wraps_read,when calling `wraps_read_inner` here,parameter `x` of wraps_read_inner,invalid access occurs here] codetoanalyze/cpp/pulse/interprocedural.cpp, access_to_invalidated_alias2_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of access_to_invalidated_alias2_bad,assigned,when calling `invalidate_and_set_to_null` here,parameter `x_ptr` of invalidate_and_set_to_null,was invalidated by `delete`,use-after-lifetime part of the trace starts here,parameter `x` of access_to_invalidated_alias2_bad,when calling `wraps_read` here,parameter `x` of wraps_read,when calling `wraps_read_inner` here,parameter `x` of wraps_read_inner,invalid access occurs here]

@ -0,0 +1,35 @@
/*
* 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.
*/
#include <stdlib.h>
int double_free_in_catch_bad_FN(int a[10], int b) {
try {
if (b > 0) {
free(a);
throw(0);
}
} catch (...) {
free(a);
}
return 0;
}
void throw_positive(int a[10], int b) {
if (b > 0) {
free(a);
throw(1);
}
}
int double_free_interproc_bad_FN(int a[10], int b) {
try {
throw_positive(a, 2);
} catch (...) {
free(a);
}
return 0;
}

@ -89,8 +89,15 @@ class Test {
return System.nanoTime(); return System.nanoTime();
} }
// In pulse, we get 0 disjuncts as a summary, hence consider this as impure // In pulse, we get Exited summary where pre=post
void exit_impure() { // TODO: change impurity to track exit as impure
void exit_impure_FN() {
System.exit(1);
}
// In pulse, we get Exited summary where pre=post
void modify_exit_impure(int[] a) {
a[0] = 0;
System.exit(1); System.exit(1);
} }

@ -34,9 +34,9 @@ codetoanalyze/java/impurity/PurityModeled.java, PurityModeled.write_impure():voi
codetoanalyze/java/impurity/Test.java, Test.Test(int):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.Test(int),global variable `Test` modified here] codetoanalyze/java/impurity/Test.java, Test.Test(int):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.Test(int),global variable `Test` modified here]
codetoanalyze/java/impurity/Test.java, Test.alias_impure(int[],int,int):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.alias_impure(int[],int,int),parameter `array` modified here] codetoanalyze/java/impurity/Test.java, Test.alias_impure(int[],int,int):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.alias_impure(int[],int,int),parameter `array` modified here]
codetoanalyze/java/impurity/Test.java, Test.call_impure_impure(int):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.call_impure_impure(int),when calling `void Test.set_impure(int,int)` here,parameter `this` modified here] codetoanalyze/java/impurity/Test.java, Test.call_impure_impure(int):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.call_impure_impure(int),when calling `void Test.set_impure(int,int)` here,parameter `this` modified here]
codetoanalyze/java/impurity/Test.java, Test.exit_impure():void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.exit_impure() with empty pulse summary]
codetoanalyze/java/impurity/Test.java, Test.global_array_set_impure(int,int):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.global_array_set_impure(int,int),global variable `Test` modified here] codetoanalyze/java/impurity/Test.java, Test.global_array_set_impure(int,int):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.global_array_set_impure(int,int),global variable `Test` modified here]
codetoanalyze/java/impurity/Test.java, Test.local_field_write_impure(Test):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.local_field_write_impure(Test),parameter `x` modified here] codetoanalyze/java/impurity/Test.java, Test.local_field_write_impure(Test):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.local_field_write_impure(Test),parameter `x` modified here]
codetoanalyze/java/impurity/Test.java, Test.modify_exit_impure(int[]):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.modify_exit_impure(int[]),parameter `a` modified here]
codetoanalyze/java/impurity/Test.java, Test.parameter_field_write_impure(Test,boolean):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.parameter_field_write_impure(Test,boolean),parameter `test` modified here] codetoanalyze/java/impurity/Test.java, Test.parameter_field_write_impure(Test,boolean):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.parameter_field_write_impure(Test,boolean),parameter `test` modified here]
codetoanalyze/java/impurity/Test.java, Test.set_impure(int,int):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.set_impure(int,int),parameter `this` modified here] codetoanalyze/java/impurity/Test.java, Test.set_impure(int,int):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.set_impure(int,int),parameter `this` modified here]
codetoanalyze/java/impurity/Test.java, Test.swap_impure(int[],int,int):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.swap_impure(int[],int,int),parameter `array` modified here] codetoanalyze/java/impurity/Test.java, Test.swap_impure(int[],int,int):void, 0, IMPURE_FUNCTION, no_bucket, ERROR, [Impure function void Test.swap_impure(int[],int,int),parameter `array` modified here]

Loading…
Cancel
Save