[pulse] improve error messages and traces

Summary:
Feedback from peterogithub:
- mention which access path is being invalidated and accessed in the message
- mention the line at which it was invalidated (the line at which it's accessed is already the line at which we report)
- traces for stack variable/C++ temporary address escapes
- delete double implementation of the same functionality in
`PulseTrace`: `location_of_action_start` is the same as
`outer_location_of_action`...

Reviewed By: jberdine

Differential Revision: D14800294

fbshipit-source-id: 3d9ab9b3d
master
Jules Villard 6 years ago committed by Facebook Github Bot
parent 9dbbd68472
commit b5589661ce

@ -233,6 +233,18 @@ module AccessExpression = struct
aux init [] access_expr aux init [] access_expr
let add_access access access_expr =
match (access : _ Access.t) with
| FieldAccess fld ->
Some (field_offset access_expr fld)
| ArrayAccess (typ, _index) ->
Some (array_offset access_expr typ None)
| Dereference ->
Some (dereference access_expr)
| TakeAddress ->
address_of access_expr
(** convert to an AccessPath.t, ignoring AddressOf and Dereference for now *) (** convert to an AccessPath.t, ignoring AddressOf and Dereference for now *)
let rec to_access_path t = let rec to_access_path t =
let rec to_access_path_ t = let rec to_access_path_ t =

@ -88,6 +88,8 @@ module AccessExpression : sig
[@@deriving compare] [@@deriving compare]
val fold_vars : (t, Var.t, 'accum) Container.fold val fold_vars : (t, Var.t, 'accum) Container.fold
val add_access : _ Access.t -> t -> t option
end end
val pp : F.formatter -> t -> unit val pp : F.formatter -> t -> unit

@ -64,7 +64,7 @@ module PulseTransferFunctions = struct
>>= fun astate -> >>= fun astate ->
match lhs_access with match lhs_access with
| Base (var, _) when Var.is_return var -> | Base (var, _) when Var.is_return var ->
PulseOperations.check_address_of_local_variable summary.Summary.proc_desc rhs_addr PulseOperations.check_address_escape loc summary.Summary.proc_desc rhs_addr rhs_trace
astate astate
| _ -> | _ ->
Ok astate ) Ok astate )
@ -145,25 +145,26 @@ module PulseTransferFunctions = struct
if flags.cf_injected_destructor then if flags.cf_injected_destructor then
match (call, actuals) with match (call, actuals) with
| ( Direct (Typ.Procname.ObjC_Cpp pname) | ( Direct (Typ.Procname.ObjC_Cpp pname)
, [AccessExpression (AddressOf (Base (ProgramVar pvar, _)) as destroyed_access)] ) , [AccessExpression (AddressOf (Base (ProgramVar pvar, typ)))] )
when Pvar.is_local pvar && not (Typ.Procname.ObjC_Cpp.is_inner_destructor pname) -> when Pvar.is_local pvar && not (Typ.Procname.ObjC_Cpp.is_inner_destructor pname) ->
(* ignore inner destructors, only trigger out of scope on the final destructor call *) (* ignore inner destructors, only trigger out of scope on the final destructor call *)
Some destroyed_access Some (pvar, typ)
| _ -> | _ ->
None None
else None else None
(** [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 out_of_scope_access_expr astate = let exec_object_out_of_scope call_loc (pvar, typ) astate =
(* invalidate both [&x] and [x]: reading either is now forbidden *) (* invalidate both [&x] and [x]: reading either is now forbidden *)
let invalidate access_expr = let invalidate pvar typ access astate =
PulseOperations.invalidate PulseOperations.invalidate
(PulseTrace.Immediate {imm= GoneOutOfScope access_expr; location= call_loc}) (PulseTrace.Immediate {imm= GoneOutOfScope (pvar, typ); location= call_loc})
call_loc access_expr call_loc access astate
in in
invalidate (HilExp.AccessExpression.dereference out_of_scope_access_expr) astate let out_of_scope_base = HilExp.AccessExpression.base (Var.of_pvar pvar, typ) in
>>= invalidate out_of_scope_access_expr invalidate pvar typ (HilExp.AccessExpression.dereference out_of_scope_base) astate
>>= invalidate pvar typ out_of_scope_base
let dispatch_call summary ret (call : HilInstr.call) (actuals : HilExp.t list) flags call_loc let dispatch_call summary ret (call : HilInstr.call) (actuals : HilExp.t list) flags call_loc
@ -185,11 +186,11 @@ module PulseTransferFunctions = struct
let posts = interprocedural_call summary ret call actuals flags call_loc astate in let posts = interprocedural_call summary ret call actuals flags call_loc astate in
PerfEvent.(log (fun logger -> log_end_event logger ())) ; PerfEvent.(log (fun logger -> log_end_event logger ())) ;
match get_out_of_scope_object call actuals flags with match get_out_of_scope_object call actuals flags with
| Some access_expr -> | Some pvar_typ ->
L.d_printfln "%a is going out of scope" HilExp.AccessExpression.pp access_expr ; L.d_printfln "%a is going out of scope" Pvar.pp_value (fst pvar_typ) ;
posts posts
>>= fun posts -> >>= fun posts ->
List.map posts ~f:(fun astate -> exec_object_out_of_scope call_loc access_expr astate) List.map posts ~f:(fun astate -> exec_object_out_of_scope call_loc pvar_typ astate)
|> Result.all |> Result.all
| None -> | None ->
posts ) posts )

@ -323,7 +323,7 @@ module PrePost = struct
if phys_equal astate call_state.astate then (call_state, var_value) if phys_equal astate call_state.astate then (call_state, var_value)
else ({call_state with astate}, var_value) else ({call_state with astate}, var_value)
in in
f ~stack_value ~addr_caller call_state f var ~stack_value ~addr_caller call_state
else Ok call_state ) else Ok call_state )
@ -355,8 +355,8 @@ module PrePost = struct
(** Materialize the (abstract memory) subgraph of [pre] reachable from [addr_pre] in (** Materialize the (abstract memory) subgraph of [pre] reachable from [addr_pre] in
[call_state.astate] starting from address [addr_caller]. Report an error if some invalid [call_state.astate] starting from address [addr_caller]. Report an error if some invalid
addresses are traversed in the process. *) addresses are traversed in the process. *)
let rec materialize_pre_from_address callee_proc_name call_location ~pre ~addr_pre ~addr_caller let rec materialize_pre_from_address callee_proc_name call_location access_expr ~pre ~addr_pre
trace call_state = ~addr_caller trace call_state =
let mk_action action = let mk_action action =
PulseTrace.ViaCall {action; proc_name= callee_proc_name; location= call_location} PulseTrace.ViaCall {action; proc_name= callee_proc_name; location= call_location}
in in
@ -373,7 +373,8 @@ module PrePost = struct
match Memory.check_valid action addr_caller call_state.astate with match Memory.check_valid action addr_caller call_state.astate with
| Error invalidated_by -> | Error invalidated_by ->
Error Error
(PulseDiagnostic.AccessToInvalidAddress {invalidated_by; accessed_by= action; trace}) (PulseDiagnostic.AccessToInvalidAddress
{access= access_expr; invalidated_by; accessed_by= action; trace})
| Ok astate -> | Ok astate ->
let call_state = {call_state with astate} in let call_state = {call_state with astate} in
Container.fold_result Container.fold_result
@ -383,7 +384,13 @@ module PrePost = struct
Memory.materialize_edge addr_caller access call_state.astate Memory.materialize_edge addr_caller access call_state.astate
in in
let call_state = {call_state with astate} in let call_state = {call_state with astate} in
materialize_pre_from_address callee_proc_name call_location ~pre let access_expr =
(* if the new access expression doesn't make sense then keep the old one; should
be fine since this is used only for reporting *)
HilExp.AccessExpression.add_access access access_expr
|> Option.value ~default:access_expr
in
materialize_pre_from_address callee_proc_name call_location access_expr ~pre
~addr_pre:addr_pre_dest ~addr_caller:addr_caller_dest trace call_state )) ~addr_pre:addr_pre_dest ~addr_caller:addr_caller_dest trace call_state ))
|> function Some result -> result | None -> Ok call_state ) |> function Some result -> result | None -> Ok call_state )
@ -393,20 +400,20 @@ module PrePost = struct
[call_state.astate] *) [call_state.astate] *)
let materialize_pre_from_actual callee_proc_name call_location ~pre ~formal ~actual call_state = let materialize_pre_from_actual callee_proc_name call_location ~pre ~formal ~actual call_state =
L.d_printfln "Materializing PRE from [%a <- %a]" Var.pp formal (Pp.option AbstractAddress.pp) L.d_printfln "Materializing PRE from [%a <- %a]" Var.pp formal (Pp.option AbstractAddress.pp)
(Option.map ~f:fst actual) ; (Option.map ~f:fst3 actual) ;
match actual with match actual with
| None -> | None ->
(* the expression representing the actual couldn't be evaluated down to an abstract address (* the expression representing the actual couldn't be evaluated down to an abstract address
*) *)
Ok call_state Ok call_state
| Some (addr_caller, trace) -> ( | Some (addr_caller, access_expr, trace) -> (
(let open Option.Monad_infix in (let open Option.Monad_infix in
PulseDomain.Stack.find_opt formal pre.PulseDomain.stack PulseDomain.Stack.find_opt formal pre.PulseDomain.stack
>>= fun (addr_formal_pre, _) -> >>= fun (addr_formal_pre, _) ->
PulseDomain.Memory.find_edge_opt addr_formal_pre Dereference pre.PulseDomain.heap PulseDomain.Memory.find_edge_opt addr_formal_pre Dereference pre.PulseDomain.heap
>>| fun (formal_pre, _) -> >>| fun (formal_pre, _) ->
materialize_pre_from_address callee_proc_name call_location ~pre ~addr_pre:formal_pre materialize_pre_from_address callee_proc_name call_location access_expr ~pre
~addr_caller trace call_state) ~addr_pre:formal_pre ~addr_caller trace call_state)
|> function Some result -> result | None -> Ok call_state ) |> function Some result -> result | None -> Ok call_state )
@ -447,11 +454,12 @@ module PrePost = struct
let materialize_pre_for_globals callee_proc_name call_location pre_post call_state = let materialize_pre_for_globals callee_proc_name call_location pre_post call_state =
fold_globals_of_stack (pre_post.pre :> PulseDomain.t).stack call_state fold_globals_of_stack (pre_post.pre :> PulseDomain.t).stack call_state
~f:(fun ~stack_value:(addr_pre, loc_opt) ~addr_caller call_state -> ~f:(fun var ~stack_value:(addr_pre, loc_opt) ~addr_caller call_state ->
let trace = let trace =
Option.map loc_opt ~f:(fun loc -> PulseTrace.VariableDeclaration loc) |> Option.to_list Option.map loc_opt ~f:(fun loc -> PulseTrace.VariableDeclaration loc) |> Option.to_list
in in
materialize_pre_from_address callee_proc_name call_location let access_expr = HilExp.AccessExpression.base (var, Typ.void) in
materialize_pre_from_address callee_proc_name call_location access_expr
~pre:(pre_post.pre :> PulseDomain.t) ~pre:(pre_post.pre :> PulseDomain.t)
~addr_pre ~addr_caller trace call_state ) ~addr_pre ~addr_caller trace call_state )
@ -589,11 +597,11 @@ module PrePost = struct
let record_post_for_actual callee_proc_name call_loc pre_post ~formal ~actual call_state = let record_post_for_actual callee_proc_name call_loc pre_post ~formal ~actual call_state =
L.d_printfln_escaped "Recording POST from [%a] <-> %a" Var.pp formal L.d_printfln_escaped "Recording POST from [%a] <-> %a" Var.pp formal
(Pp.option AbstractAddress.pp) (Option.map ~f:fst actual) ; (Pp.option AbstractAddress.pp) (Option.map ~f:fst3 actual) ;
match actual with match actual with
| None -> | None ->
call_state call_state
| Some (addr_caller, _trace) -> ( | Some (addr_caller, _access_expr, _trace) -> (
let open Option.Monad_infix in let open Option.Monad_infix in
match match
PulseDomain.Stack.find_opt formal (pre_post.pre :> PulseDomain.t).PulseDomain.stack PulseDomain.Stack.find_opt formal (pre_post.pre :> PulseDomain.t).PulseDomain.stack
@ -642,7 +650,7 @@ module PrePost = struct
let apply_post_for_globals callee_proc_name call_location pre_post call_state = let apply_post_for_globals callee_proc_name call_location pre_post call_state =
match match
fold_globals_of_stack (pre_post.pre :> PulseDomain.t).stack call_state fold_globals_of_stack (pre_post.pre :> PulseDomain.t).stack call_state
~f:(fun ~stack_value:(addr_callee, _) ~addr_caller call_state -> ~f:(fun _var ~stack_value:(addr_callee, _) ~addr_caller call_state ->
Ok Ok
(record_post_for_address callee_proc_name call_location pre_post ~addr_callee (record_post_for_address callee_proc_name call_location pre_post ~addr_callee
~addr_caller call_state) ) ~addr_caller call_state) )

@ -76,7 +76,7 @@ module PrePost : sig
-> t -> t
-> formals:Var.t list -> formals:Var.t list
-> ret:AbstractAddress.t * PulseTrace.t -> ret:AbstractAddress.t * PulseTrace.t
-> actuals:(AbstractAddress.t * PulseTrace.t) option list -> actuals:(AbstractAddress.t * HilExp.AccessExpression.t * PulseTrace.t) option list
-> domain_t -> domain_t
-> (domain_t, PulseDiagnostic.t) result -> (domain_t, PulseDiagnostic.t) result
end end

@ -10,12 +10,13 @@ module F = Format
type t = type t =
| AccessToInvalidAddress of | AccessToInvalidAddress of
{ invalidated_by: PulseInvalidation.t PulseTrace.action { access: HilExp.AccessExpression.t
; invalidated_by: PulseInvalidation.t PulseTrace.action
; accessed_by: HilExp.AccessExpression.t PulseTrace.action ; accessed_by: HilExp.AccessExpression.t PulseTrace.action
; trace: PulseTrace.t } ; trace: PulseTrace.t }
| StackVariableAddressEscape of {variable: Var.t; location: Location.t} | StackVariableAddressEscape of {variable: Var.t; trace: PulseTrace.t; location: Location.t}
let describe_access = PulseTrace.pp_action HilExp.AccessExpression.pp let describe_access = PulseTrace.pp_action (Pp.in_backticks HilExp.AccessExpression.pp)
let describe_invalidation = PulseTrace.pp_action PulseInvalidation.describe let describe_invalidation = PulseTrace.pp_action PulseInvalidation.describe
@ -27,10 +28,24 @@ let get_location = function
let get_message = function let get_message = function
| AccessToInvalidAddress {accessed_by; invalidated_by; _} -> | AccessToInvalidAddress {access; accessed_by; invalidated_by; _} ->
F.asprintf "%a accesses address invalidated by %a past its lifetime" describe_access (* TODO: [access] might be something irrelevant to the user, shouldn't print it in that case
accessed_by describe_invalidation invalidated_by *)
| StackVariableAddressEscape {variable} -> let line_of_action action =
let {Location.line; _} = PulseTrace.outer_location_of_action action in
line
in
let invalidation_line = line_of_action invalidated_by in
let access_line = line_of_action accessed_by in
let pp_indirect_access f =
let erroneous_access = PulseTrace.immediate_of_action accessed_by in
if not (HilExp.AccessExpression.equal erroneous_access access) then
F.fprintf f " via %a" describe_access accessed_by
in
F.asprintf "access to `%a`%t at line %d is to %a on line %d" HilExp.AccessExpression.pp
access pp_indirect_access access_line describe_invalidation invalidated_by
invalidation_line
| StackVariableAddressEscape {variable; _} ->
let pp_var f var = let pp_var f var =
if Var.is_cpp_temporary var then F.pp_print_string f "C++ temporary" if Var.is_cpp_temporary var then F.pp_print_string f "C++ temporary"
else F.fprintf f "stack variable `%a`" Var.pp var else F.fprintf f "stack variable `%a`" Var.pp var
@ -53,20 +68,24 @@ let get_trace = function
| Some location -> | Some location ->
add_errlog_header ~title location errlog add_errlog_header ~title location errlog
in in
add_errlog_header ~title:"start of end of lifetime trace" let pp_invalid_access f access =
(PulseTrace.location_of_action_start invalidated_by) F.fprintf f "invalid access to `%a`" HilExp.AccessExpression.pp access
@@ PulseTrace.add_errlog_of_action ~nesting:1 ~action_name:"invalidated by" in
PulseInvalidation.describe invalidated_by add_errlog_header ~title:"invalidation part of the trace starts here"
@@ add_errlog_header ~title:"start of use after lifetime trace" (PulseTrace.outer_location_of_action invalidated_by)
(PulseTrace.location_of_action_start accessed_by) @@ PulseTrace.add_errlog_of_action ~nesting:1 PulseInvalidation.describe invalidated_by
@@ PulseTrace.add_errlog_of_action ~nesting:1 ~action_name:"accessed" @@ add_errlog_header ~title:"use-after-lifetime part of the trace starts here"
(Pp.in_backticks HilExp.AccessExpression.pp) (PulseTrace.outer_location_of_action accessed_by)
accessed_by @@ PulseTrace.add_errlog_of_action ~nesting:1 pp_invalid_access accessed_by
@@ add_header_if_some ~title:"start of value trace" (PulseTrace.get_start_location trace) @@ add_header_if_some ~title:"trace of how the access expression was constructed starts here"
(PulseTrace.get_start_location trace)
@@ PulseTrace.add_errlog_of_trace ~nesting:1 trace @@ PulseTrace.add_errlog_of_trace ~nesting:1 trace
@@ [] @@ []
| StackVariableAddressEscape _ -> | StackVariableAddressEscape {trace; location; _} ->
[] PulseTrace.add_errlog_of_trace ~nesting:0 trace
@@
let nesting = 0 in
[Errlog.make_trace_element nesting location "returned here" []]
let get_issue_type = function let get_issue_type = function

@ -7,12 +7,14 @@
open! IStd open! IStd
(** an error to report to the user *)
type t = type t =
| AccessToInvalidAddress of | AccessToInvalidAddress of
{ invalidated_by: PulseInvalidation.t PulseTrace.action { access: HilExp.AccessExpression.t
; invalidated_by: PulseInvalidation.t PulseTrace.action
; accessed_by: HilExp.AccessExpression.t PulseTrace.action ; accessed_by: HilExp.AccessExpression.t PulseTrace.action
; trace: PulseTrace.t } ; trace: PulseTrace.t }
| StackVariableAddressEscape of {variable: Var.t; location: Location.t} | StackVariableAddressEscape of {variable: Var.t; trace: PulseTrace.t; location: Location.t}
val get_message : t -> string val get_message : t -> string

@ -40,7 +40,7 @@ let pp_std_vector_function f = function
type t = type t =
| CFree of HilExp.AccessExpression.t | CFree of HilExp.AccessExpression.t
| CppDelete of HilExp.AccessExpression.t | CppDelete of HilExp.AccessExpression.t
| GoneOutOfScope of HilExp.AccessExpression.t | GoneOutOfScope of Pvar.t * Typ.t
| Nullptr | Nullptr
| StdVector of std_vector_function * HilExp.AccessExpression.t | StdVector of std_vector_function * HilExp.AccessExpression.t
[@@deriving compare] [@@deriving compare]
@ -60,15 +60,21 @@ let issue_type_of_cause = function
let describe f = function let describe f = function
| CFree access_expr -> | CFree access_expr ->
F.fprintf f "call to `free()` on `%a`" HilExp.AccessExpression.pp access_expr F.fprintf f "memory invalidated by call to `free()` on `%a`" HilExp.AccessExpression.pp
access_expr
| CppDelete access_expr -> | CppDelete access_expr ->
F.fprintf f "`delete` on `%a`" HilExp.AccessExpression.pp access_expr F.fprintf f "memory invalidated by `delete` on `%a`" HilExp.AccessExpression.pp access_expr
| GoneOutOfScope access_expr -> | GoneOutOfScope (pvar, typ) ->
F.fprintf f "`%a` gone out of scope" HilExp.AccessExpression.pp access_expr let pp_var f pvar =
if Pvar.is_cpp_temporary pvar then
F.fprintf f "C++ temporary of type `%a`" (Typ.pp_full Pp.text) typ
else F.fprintf f "stack variable `%a`" Pvar.pp_value pvar
in
F.fprintf f "address of %a whose lifetime has ended" pp_var pvar
| Nullptr -> | Nullptr ->
F.fprintf f "null pointer" F.fprintf f "the null pointer"
| StdVector (std_vector_f, access_expr) -> | StdVector (std_vector_f, access_expr) ->
F.fprintf f "potentially invalidated by call to `%a()` on `%a`" pp_std_vector_function F.fprintf f "memory potentially invalidated by call to `%a()` on `%a`" pp_std_vector_function
std_vector_f HilExp.AccessExpression.pp access_expr std_vector_f HilExp.AccessExpression.pp access_expr

@ -22,7 +22,7 @@ val pp_std_vector_function : Format.formatter -> std_vector_function -> unit
type t = type t =
| CFree of HilExp.AccessExpression.t | CFree of HilExp.AccessExpression.t
| CppDelete of HilExp.AccessExpression.t | CppDelete of HilExp.AccessExpression.t
| GoneOutOfScope of HilExp.AccessExpression.t | GoneOutOfScope of Pvar.t * Typ.t
| Nullptr | Nullptr
| StdVector of std_vector_function * HilExp.AccessExpression.t | StdVector of std_vector_function * HilExp.AccessExpression.t
[@@deriving compare] [@@deriving compare]

@ -26,23 +26,24 @@ type t = PulseAbductiveDomain.t
type 'a access_result = ('a, PulseDiagnostic.t) result type 'a access_result = ('a, PulseDiagnostic.t) result
(** Check that the address is not known to be invalid *) (** Check that the address is not known to be invalid *)
let check_addr_access action (address, trace) astate = let check_addr_access access action (address, trace) astate =
Memory.check_valid action address astate Memory.check_valid action address astate
|> Result.map_error ~f:(fun invalidated_by -> |> Result.map_error ~f:(fun invalidated_by ->
PulseDiagnostic.AccessToInvalidAddress {invalidated_by; accessed_by= action; trace} ) PulseDiagnostic.AccessToInvalidAddress {access; invalidated_by; accessed_by= action; trace}
)
(** Walk the heap starting from [addr] and following [path]. Stop either at the element before last (** Walk the heap starting from [addr] and following [path]. Stop either at the element before last
and return [new_addr] if [overwrite_last] is [Some new_addr], or go until the end of the path if it and return [new_addr] if [overwrite_last] is [Some new_addr], or go until the end of the path if it
is [None]. Create more addresses into the heap as needed to follow the [path]. Check that each is [None]. Create more addresses into the heap as needed to follow the [path]. Check that each
address reached is valid. *) address reached is valid. *)
let rec walk ~dereference_to_ignore action ~on_last addr_trace path astate = let rec walk ~dereference_to_ignore access_expr action ~on_last addr_trace path astate =
let check_addr_access_optional action addr_trace astate = let check_addr_access_optional action addr_trace astate =
match dereference_to_ignore with match dereference_to_ignore with
| Some 0 -> | Some 0 ->
Ok astate Ok astate
| _ -> | _ ->
check_addr_access action addr_trace astate check_addr_access access_expr action addr_trace astate
in in
match (path, on_last) with match (path, on_last) with
| [], `Access -> | [], `Access ->
@ -61,7 +62,7 @@ let rec walk ~dereference_to_ignore action ~on_last addr_trace path astate =
Option.map ~f:(fun index -> max 0 (index - 1)) dereference_to_ignore Option.map ~f:(fun index -> max 0 (index - 1)) dereference_to_ignore
in in
let astate, addr_trace' = Memory.materialize_edge (fst addr_trace) a astate in let astate, addr_trace' = Memory.materialize_edge (fst addr_trace) a astate in
walk ~dereference_to_ignore action ~on_last addr_trace' path astate walk access_expr ~dereference_to_ignore action ~on_last addr_trace' path astate
let write_var var new_addr_trace astate = let write_var var new_addr_trace astate =
@ -121,8 +122,10 @@ and walk_access_expr ~on_last astate access_expr location =
let astate, base_addr_trace = let astate, base_addr_trace =
let astate, (addr, init_loc_opt) = Stack.materialize access_var astate in let astate, (addr, init_loc_opt) = Stack.materialize access_var astate in
let trace = let trace =
Option.value_map init_loc_opt ~default:[] ~f:(fun init_loc -> Option.map init_loc_opt ~f:(fun init_loc ->
[PulseTrace.VariableDeclaration init_loc] ) if Var.is_cpp_temporary access_var then PulseTrace.CppTemporaryCreated init_loc
else PulseTrace.VariableDeclaration init_loc )
|> Option.to_list
in in
(astate, (addr, trace)) (astate, (addr, trace))
in in
@ -131,7 +134,7 @@ and walk_access_expr ~on_last astate access_expr location =
Ok (astate, base_addr_trace) Ok (astate, base_addr_trace)
| _ -> | _ ->
let action = PulseTrace.Immediate {imm= access_expr; location} in let action = PulseTrace.Immediate {imm= access_expr; location} in
walk ~dereference_to_ignore action ~on_last base_addr_trace walk access_expr ~dereference_to_ignore action ~on_last base_addr_trace
(HilExp.Access.Dereference :: access_list) (HilExp.Access.Dereference :: access_list)
astate ) astate )
@ -188,14 +191,14 @@ let write location access_expr addr astate =
let invalidate cause location access_expr astate = let invalidate cause location access_expr astate =
materialize_address astate access_expr location materialize_address astate access_expr location
>>= fun (astate, addr_trace) -> >>= fun (astate, addr_trace) ->
check_addr_access (Immediate {imm= access_expr; location}) addr_trace astate check_addr_access access_expr (Immediate {imm= access_expr; location}) addr_trace astate
>>| mark_invalid cause (fst addr_trace) >>| mark_invalid cause (fst addr_trace)
let invalidate_array_elements cause location access_expr astate = let invalidate_array_elements cause location access_expr astate =
materialize_address astate access_expr location materialize_address astate access_expr location
>>= fun (astate, addr_trace) -> >>= fun (astate, addr_trace) ->
check_addr_access (Immediate {imm= access_expr; location}) addr_trace astate check_addr_access access_expr (Immediate {imm= access_expr; location}) addr_trace astate
>>| fun astate -> >>| fun astate ->
match Memory.find_opt (fst addr_trace) astate with match Memory.find_opt (fst addr_trace) astate with
| None -> | None ->
@ -211,33 +214,33 @@ let invalidate_array_elements cause location access_expr astate =
edges astate edges astate
let check_address_of_local_variable proc_desc address astate = let check_address_escape escape_location proc_desc address trace astate =
let proc_location = Procdesc.get_loc proc_desc in
let proc_name = Procdesc.get_proc_name proc_desc in
let check_address_of_cpp_temporary () = let check_address_of_cpp_temporary () =
Memory.find_opt address astate Memory.find_opt address astate
|> Option.fold_result ~init:() ~f:(fun () (_, attrs) -> |> Option.fold_result ~init:() ~f:(fun () (_, attrs) ->
IContainer.iter_result ~fold:Attributes.fold attrs ~f:(fun attr -> IContainer.iter_result ~fold:Attributes.fold attrs ~f:(fun attr ->
match attr with match attr with
| Attribute.AddressOfCppTemporary (variable, location_opt) -> | Attribute.AddressOfCppTemporary (variable, _) ->
let location = Option.value ~default:proc_location location_opt in Error
Error (PulseDiagnostic.StackVariableAddressEscape {variable; location}) (PulseDiagnostic.StackVariableAddressEscape
{variable; location= escape_location; trace})
| _ -> | _ ->
Ok () ) ) Ok () ) )
in in
let check_address_of_stack_variable () = let check_address_of_stack_variable () =
let proc_name = Procdesc.get_proc_name proc_desc in
IContainer.iter_result ~fold:(IContainer.fold_of_pervasives_map_fold ~fold:Stack.fold) astate IContainer.iter_result ~fold:(IContainer.fold_of_pervasives_map_fold ~fold:Stack.fold) astate
~f:(fun (variable, (var_address, init_location)) -> ~f:(fun (variable, (var_address, _)) ->
if if
AbstractAddress.equal var_address address AbstractAddress.equal var_address address
&& ( Var.is_cpp_temporary variable && ( Var.is_cpp_temporary variable
|| Var.is_local_to_procedure proc_name variable || Var.is_local_to_procedure proc_name variable
&& not (Procdesc.is_captured_var proc_desc variable) ) && not (Procdesc.is_captured_var proc_desc variable) )
then ( then (
let location = Option.value ~default:proc_location init_location in L.d_printfln_escaped "Stack variable address &%a detected at address %a" Var.pp variable
L.d_printfln_escaped "Stack Variable &%a detected at address %a" Var.pp variable
AbstractAddress.pp address ; AbstractAddress.pp address ;
Error (PulseDiagnostic.StackVariableAddressEscape {variable; location}) ) Error
(PulseDiagnostic.StackVariableAddressEscape {variable; location= escape_location; trace}) )
else Ok () ) else Ok () )
in in
check_address_of_cpp_temporary () >>= check_address_of_stack_variable >>| fun () -> astate check_address_of_cpp_temporary () >>= check_address_of_stack_variable >>| fun () -> astate
@ -254,8 +257,6 @@ let remove_vars vars astate =
List.fold vars ~init:astate ~f:(fun heap var -> List.fold vars ~init:astate ~f:(fun heap var ->
match Stack.find_opt var astate with match Stack.find_opt var astate with
| Some (address, location) when Var.is_cpp_temporary var -> | Some (address, location) when Var.is_cpp_temporary var ->
(* TODO: it would be good to record the location of the temporary creation in the
stack and save it here in the attribute for reporting *)
mark_address_of_cpp_temporary location var address astate mark_address_of_cpp_temporary location var address astate
| _ -> | _ ->
heap ) heap )
@ -305,7 +306,7 @@ module Closures = struct
~fold:(IContainer.fold_of_pervasives_map_fold ~fold:Memory.Edges.fold) edges ~fold:(IContainer.fold_of_pervasives_map_fold ~fold:Memory.Edges.fold) edges
~f:(fun (access, addr_trace) -> ~f:(fun (access, addr_trace) ->
if is_captured_fake_access access then if is_captured_fake_access access then
check_addr_access (Immediate {imm= lambda; location}) addr_trace astate check_addr_access lambda (Immediate {imm= lambda; location}) addr_trace astate
>>| fun _ -> () >>| fun _ -> ()
else Ok () ) else Ok () )
| _ -> | _ ->
@ -365,7 +366,8 @@ module Interproc = struct
match actual with match actual with
| HilExp.AccessExpression access_expr -> | HilExp.AccessExpression access_expr ->
read call_loc access_expr astate read call_loc access_expr astate
>>| fun (astate, (addr, trace)) -> (astate, Some (addr, trace) :: rev_actual_addresses) >>| fun (astate, (addr, trace)) ->
(astate, Some (addr, access_expr, trace) :: rev_actual_addresses)
| _ -> | _ ->
Ok (astate, None :: rev_actual_addresses) ) Ok (astate, None :: rev_actual_addresses) )
>>= fun (astate, rev_actual_addresses) -> >>= fun (astate, rev_actual_addresses) ->

@ -76,8 +76,8 @@ val invalidate_array_elements :
val remove_vars : Var.t list -> t -> t val remove_vars : Var.t list -> t -> t
(* TODO: better name and pass location to report where we returned *) val check_address_escape :
val check_address_of_local_variable : Procdesc.t -> AbstractAddress.t -> t -> t access_result Location.t -> Procdesc.t -> AbstractAddress.t -> PulseTrace.t -> t -> t access_result
module Interproc : sig module Interproc : sig
val call : val call :

@ -9,6 +9,7 @@ module F = Format
type breadcrumb = type breadcrumb =
| VariableDeclaration of Location.t | VariableDeclaration of Location.t
| CppTemporaryCreated of Location.t
| Assignment of {lhs: HilExp.AccessExpression.t; location: Location.t} | Assignment of {lhs: HilExp.AccessExpression.t; location: Location.t}
| Capture of | Capture of
{ captured_as: AccessPath.base { captured_as: AccessPath.base
@ -22,7 +23,9 @@ type breadcrumb =
let pp_breadcrumb_no_location fmt = function let pp_breadcrumb_no_location fmt = function
| VariableDeclaration _ -> | VariableDeclaration _ ->
F.fprintf fmt "variable declared" F.pp_print_string fmt "variable declared"
| CppTemporaryCreated _ ->
F.pp_print_string fmt "C++ temporary created"
| Capture {captured_as; captured; location= _} -> | Capture {captured_as; captured; location= _} ->
F.fprintf fmt "`%a` captured as `%a`" HilExp.AccessExpression.pp captured AccessPath.pp_base F.fprintf fmt "`%a` captured as `%a`" HilExp.AccessExpression.pp captured AccessPath.pp_base
captured_as captured_as
@ -39,7 +42,11 @@ let pp_breadcrumb_no_location fmt = function
let location_of_breadcrumb = function let location_of_breadcrumb = function
| VariableDeclaration location | Assignment {location} | Capture {location} | Call {location} -> | VariableDeclaration location
| CppTemporaryCreated location
| Assignment {location}
| Capture {location}
| Call {location} ->
location location
@ -77,11 +84,6 @@ let pp_action pp_immediate fmt = function
F.fprintf fmt "call to `%a`" Typ.Procname.describe proc_name F.fprintf fmt "call to `%a`" Typ.Procname.describe proc_name
let location_of_action_start = function
| Immediate {location; _} | ViaCall {location; _} ->
location
let rec immediate_of_action = function let rec immediate_of_action = function
| Immediate {imm; _} -> | Immediate {imm; _} ->
imm imm
@ -89,21 +91,19 @@ let rec immediate_of_action = function
immediate_of_action action immediate_of_action action
let add_errlog_of_action ~nesting ~action_name pp_immediate action errlog = let add_errlog_of_action ~nesting pp_immediate action errlog =
let rec aux ~nesting rev_errlog action = let rec aux ~nesting rev_errlog action =
match action with match action with
| Immediate {imm; location} -> | Immediate {imm; location} ->
let rev_errlog = let rev_errlog =
Errlog.make_trace_element nesting location Errlog.make_trace_element nesting location (F.asprintf "%a here" pp_immediate imm) []
(F.asprintf "%s %a here" action_name pp_immediate imm)
[]
:: rev_errlog :: rev_errlog
in in
List.rev_append rev_errlog errlog List.rev_append rev_errlog errlog
| ViaCall {action; proc_name; location} -> | ViaCall {action; proc_name; location} ->
aux ~nesting:(nesting + 1) aux ~nesting:(nesting + 1)
( Errlog.make_trace_element nesting location ( Errlog.make_trace_element nesting location
(F.asprintf "%s during call to `%a` here" action_name Typ.Procname.describe proc_name) (F.asprintf "when calling `%a` here" Typ.Procname.describe proc_name)
[] []
:: rev_errlog ) :: rev_errlog )
action action

@ -10,6 +10,7 @@ module F = Format
type breadcrumb = type breadcrumb =
| VariableDeclaration of Location.t | VariableDeclaration of Location.t
| CppTemporaryCreated of Location.t
| Assignment of {lhs: HilExp.AccessExpression.t; location: Location.t} | Assignment of {lhs: HilExp.AccessExpression.t; location: Location.t}
| Capture of | Capture of
{ captured_as: AccessPath.base { captured_as: AccessPath.base
@ -36,15 +37,12 @@ type 'a action =
val pp_action : (F.formatter -> 'a -> unit) -> F.formatter -> 'a action -> unit val pp_action : (F.formatter -> 'a -> unit) -> F.formatter -> 'a action -> unit
val location_of_action_start : 'a action -> Location.t
val immediate_of_action : 'a action -> 'a val immediate_of_action : 'a action -> 'a
val outer_location_of_action : 'a action -> Location.t val outer_location_of_action : 'a action -> Location.t
val add_errlog_of_action : val add_errlog_of_action :
nesting:int nesting:int
-> action_name:string
-> (F.formatter -> 'a -> unit) -> (F.formatter -> 'a -> unit)
-> 'a action -> 'a action
-> Errlog.loc_trace_elem sexp_list -> Errlog.loc_trace_elem sexp_list

@ -1,46 +1,46 @@
codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_branch_bad, 6, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `ptr` here,start of use after lifetime trace,accessed `*(ptr)` here] codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_branch_bad, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `ptr` here,use-after-lifetime part of the trace starts here,invalid access to `*(ptr)` here]
codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_loop_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `ptr` here,start of use after lifetime trace,accessed `ptr` here] codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_loop_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `ptr` here,use-after-lifetime part of the trace starts here,invalid access to `ptr` here]
codetoanalyze/cpp/pulse/closures.cpp, implicit_ref_capture_destroy_invoke_bad, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `s` gone out of scope here,start of use after lifetime trace,accessed `&(f)` here,start of value trace,`&(s)` captured as `s`] codetoanalyze/cpp/pulse/closures.cpp, implicit_ref_capture_destroy_invoke_bad, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidation part of the trace starts here,address of stack variable `s` whose lifetime has ended here,use-after-lifetime part of the trace starts here,invalid access to `&(f)` here,trace of how the access expression was constructed starts here,`&(s)` captured as `s`]
codetoanalyze/cpp/pulse/closures.cpp, ref_capture_destroy_invoke_bad, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `s` gone out of scope here,start of use after lifetime trace,accessed `&(f)` here,start of value trace,`&(s)` captured as `s`] codetoanalyze/cpp/pulse/closures.cpp, ref_capture_destroy_invoke_bad, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidation part of the trace starts here,address of stack variable `s` whose lifetime has ended here,use-after-lifetime part of the trace starts here,invalid access to `&(f)` here,trace of how the access expression was constructed starts here,`&(s)` captured as `s`]
codetoanalyze/cpp/pulse/deduplication.cpp, deduplication::SomeTemplatedClass<int*>::lifetime_error_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by during call to `templated_wrapper_delete_ok` here,invalidated by `delete` on `a` here,start of use after lifetime trace,accessed during call to `templated_wrapper_access_ok` here,accessed `a->f` 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,when calling `templated_wrapper_delete_ok` here,memory invalidated by `delete` on `a` here,use-after-lifetime part of the trace starts here,when calling `templated_wrapper_access_ok` here,invalid access to `a->f` here]
codetoanalyze/cpp/pulse/deduplication.cpp, deduplication::SomeTemplatedClass<int>::lifetime_error_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by during call to `templated_wrapper_delete_ok` here,invalidated by `delete` on `a` here,start of use after lifetime trace,accessed during call to `templated_wrapper_access_ok` here,accessed `a->f` 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,when calling `templated_wrapper_delete_ok` here,memory invalidated by `delete` on `a` here,use-after-lifetime part of the trace starts here,when calling `templated_wrapper_access_ok` here,invalid access to `a->f` here]
codetoanalyze/cpp/pulse/interprocedural.cpp, delete_aliased_then_read_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `y` here,start of use after lifetime trace,accessed during call to `wraps_read()` here,accessed during call to `wraps_read_inner()` here,accessed `x->f` here,start of value trace,assigned to `z`] codetoanalyze/cpp/pulse/interprocedural.cpp, delete_aliased_then_read_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `y` here,use-after-lifetime part of the trace starts here,when calling `wraps_read()` here,when calling `wraps_read_inner()` here,invalid access to `x->f` here,trace of how the access expression was constructed starts here,assigned to `z`]
codetoanalyze/cpp/pulse/interprocedural.cpp, delete_inner_then_write_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by during call to `wraps_delete_inner()` here,invalidated by `delete` on `x` here,start of use after lifetime trace,accessed during call to `wraps_read()` here,accessed during call to `wraps_read_inner()` here,accessed `x->f` here] codetoanalyze/cpp/pulse/interprocedural.cpp, delete_inner_then_write_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `wraps_delete_inner()` here,memory invalidated by `delete` on `x` here,use-after-lifetime part of the trace starts here,when calling `wraps_read()` here,when calling `wraps_read_inner()` here,invalid access to `x->f` here]
codetoanalyze/cpp/pulse/interprocedural.cpp, delete_then_read_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `x` here,start of use after lifetime trace,accessed during call to `wraps_read()` here,accessed during call to `wraps_read_inner()` here,accessed `x->f` here] codetoanalyze/cpp/pulse/interprocedural.cpp, delete_then_read_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `x` here,use-after-lifetime part of the trace starts here,when calling `wraps_read()` here,when calling `wraps_read_inner()` here,invalid access to `x->f` here]
codetoanalyze/cpp/pulse/interprocedural.cpp, delete_then_write_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by during call to `wraps_delete()` here,invalidated by during call to `wraps_delete_inner()` here,invalidated by `delete` on `x` here,start of use after lifetime trace,accessed during call to `wraps_read()` here,accessed during call to `wraps_read_inner()` here,accessed `x->f` here] codetoanalyze/cpp/pulse/interprocedural.cpp, delete_then_write_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `wraps_delete()` here,when calling `wraps_delete_inner()` here,memory invalidated by `delete` on `x` here,use-after-lifetime part of the trace starts here,when calling `wraps_read()` here,when calling `wraps_read_inner()` here,invalid access to `x->f` here]
codetoanalyze/cpp/pulse/interprocedural.cpp, feed_invalid_into_access_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by during call to `may_return_invalid_ptr_ok()` here,invalidated by `delete` on `y` here,start of use after lifetime trace,accessed during call to `call_store()` here,accessed during call to `store()` here,accessed `y->p` here,start of value trace,assigned to `y`,assigned to `return`,returned from call to `may_return_invalid_ptr_ok()`,assigned to `y`] codetoanalyze/cpp/pulse/interprocedural.cpp, feed_invalid_into_access_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `may_return_invalid_ptr_ok()` here,memory invalidated by `delete` on `y` here,use-after-lifetime part of the trace starts here,when calling `call_store()` here,when calling `store()` here,invalid access to `y->p` here,trace of how the access expression was constructed starts here,assigned to `y`,assigned to `return`,returned from call to `may_return_invalid_ptr_ok()`,assigned to `y`]
codetoanalyze/cpp/pulse/join.cpp, invalidate_node_alias_bad, 12, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `result` here,start of use after lifetime trace,accessed `*(result)` here,start of value trace,assigned to `result`] codetoanalyze/cpp/pulse/join.cpp, invalidate_node_alias_bad, 12, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `result` here,use-after-lifetime part of the trace starts here,invalid access to `*(result)` here,trace of how the access expression was constructed starts here,assigned to `result`]
codetoanalyze/cpp/pulse/reference_wrapper.cpp, reference_wrapper_heap_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by during call to `getwrapperHeap()` here,invalidated by during call to `~WrapsB` here,invalidated by during call to `__infer_inner_destructor_~WrapsB` here,invalidated by `delete` on `this->b` here,start of use after lifetime trace,accessed `rw.b->f` here,start of value trace,assigned to `this->b`,returned from call to `ReferenceWrapperHeap::ReferenceWrapperHeap()`] codetoanalyze/cpp/pulse/reference_wrapper.cpp, reference_wrapper_heap_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `getwrapperHeap()` here,when calling `~WrapsB` here,when calling `__infer_inner_destructor_~WrapsB` here,memory invalidated by `delete` on `this->b` here,use-after-lifetime part of the trace starts here,invalid access to `rw.b->f` here,trace of how the access expression was constructed starts here,assigned to `this->b`,returned from call to `ReferenceWrapperHeap::ReferenceWrapperHeap()`]
codetoanalyze/cpp/pulse/reference_wrapper.cpp, reference_wrapper_stack_bad, 2, USE_AFTER_LIFETIME, no_bucket, ERROR, [start of end of lifetime trace,invalidated by during call to `getwrapperStack()` here,invalidated by `&(b)` gone out of scope here,start of use after lifetime trace,accessed `rw.b->f` here,start of value trace,assigned to `this->b`,returned from call to `ReferenceWrapperStack::ReferenceWrapperStack()`] codetoanalyze/cpp/pulse/reference_wrapper.cpp, reference_wrapper_stack_bad, 2, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `getwrapperStack()` here,address of stack variable `b` whose lifetime has ended here,use-after-lifetime part of the trace starts here,invalid access to `rw.b->f` here,trace of how the access expression was constructed starts here,returned from call to `getwrapperStack()`]
codetoanalyze/cpp/pulse/returns.cpp, returns::return_literal_stack_reference_bad, 0, STACK_VARIABLE_ADDRESS_ESCAPE, no_bucket, ERROR, [] codetoanalyze/cpp/pulse/returns.cpp, returns::return_literal_stack_reference_bad, 0, STACK_VARIABLE_ADDRESS_ESCAPE, no_bucket, ERROR, [C++ temporary created,returned here]
codetoanalyze/cpp/pulse/returns.cpp, returns::return_stack_pointer_bad, 1, STACK_VARIABLE_ADDRESS_ESCAPE, no_bucket, ERROR, [] codetoanalyze/cpp/pulse/returns.cpp, returns::return_stack_pointer_bad, 2, STACK_VARIABLE_ADDRESS_ESCAPE, no_bucket, ERROR, [variable declared,returned here]
codetoanalyze/cpp/pulse/returns.cpp, returns::return_variable_stack_reference1_bad, 1, STACK_VARIABLE_ADDRESS_ESCAPE, no_bucket, ERROR, [] codetoanalyze/cpp/pulse/returns.cpp, returns::return_variable_stack_reference1_bad, 2, STACK_VARIABLE_ADDRESS_ESCAPE, no_bucket, ERROR, [C++ temporary created,assigned to `x`,returned here]
codetoanalyze/cpp/pulse/returns.cpp, returns::return_variable_stack_reference2_bad, 1, STACK_VARIABLE_ADDRESS_ESCAPE, no_bucket, ERROR, [] codetoanalyze/cpp/pulse/returns.cpp, returns::return_variable_stack_reference2_bad, 3, STACK_VARIABLE_ADDRESS_ESCAPE, no_bucket, ERROR, [C++ temporary created,assigned to `x`,assigned to `y`,returned here]
codetoanalyze/cpp/pulse/use_after_delete.cpp, delete_in_branch_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `s` here,start of use after lifetime trace,accessed `s->f` here,start of value trace,assigned to `s`] codetoanalyze/cpp/pulse/use_after_delete.cpp, delete_in_branch_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `s` here,use-after-lifetime part of the trace starts here,invalid access to `s->f` here,trace of how the access expression was constructed starts here,assigned to `s`]
codetoanalyze/cpp/pulse/use_after_delete.cpp, delete_in_loop_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `s` here,start of use after lifetime trace,accessed `s` here,start of value trace,assigned to `s`] codetoanalyze/cpp/pulse/use_after_delete.cpp, delete_in_loop_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `s` here,use-after-lifetime part of the trace starts here,invalid access to `s` here,trace of how the access expression was constructed starts here,assigned to `s`]
codetoanalyze/cpp/pulse/use_after_delete.cpp, deref_deleted_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `s` here,start of use after lifetime trace,accessed during call to `Simple` here,accessed `__param_0->f` here,start of value trace,assigned to `s`] codetoanalyze/cpp/pulse/use_after_delete.cpp, deref_deleted_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `s` here,use-after-lifetime part of the trace starts here,when calling `Simple` here,invalid access to `__param_0->f` here,trace of how the access expression was constructed starts here,assigned to `s`]
codetoanalyze/cpp/pulse/use_after_delete.cpp, double_delete_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `s` here,start of use after lifetime trace,accessed `s` here,start of value trace,assigned to `s`] codetoanalyze/cpp/pulse/use_after_delete.cpp, double_delete_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `s` here,use-after-lifetime part of the trace starts here,invalid access to `s` here,trace of how the access expression was constructed starts here,assigned to `s`]
codetoanalyze/cpp/pulse/use_after_delete.cpp, reassign_field_of_deleted_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `s` here,start of use after lifetime trace,accessed `s->f` here,start of value trace,assigned to `s`] codetoanalyze/cpp/pulse/use_after_delete.cpp, reassign_field_of_deleted_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `s` here,use-after-lifetime part of the trace starts here,invalid access to `s->f` here,trace of how the access expression was constructed starts here,assigned to `s`]
codetoanalyze/cpp/pulse/use_after_delete.cpp, use_in_branch_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `s` here,start of use after lifetime trace,accessed during call to `Simple` here,accessed `__param_0->f` here,start of value trace,assigned to `s`] codetoanalyze/cpp/pulse/use_after_delete.cpp, use_in_branch_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `s` here,use-after-lifetime part of the trace starts here,when calling `Simple` here,invalid access to `__param_0->f` here,trace of how the access expression was constructed starts here,assigned to `s`]
codetoanalyze/cpp/pulse/use_after_delete.cpp, use_in_loop_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `s` here,start of use after lifetime trace,accessed `s->f` here,start of value trace,assigned to `s`] codetoanalyze/cpp/pulse/use_after_delete.cpp, use_in_loop_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `s` here,use-after-lifetime part of the trace starts here,invalid access to `s->f` here,trace of how the access expression was constructed starts here,assigned to `s`]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::double_destructor_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by during call to `~S` here,invalidated by during call to `__infer_inner_destructor_~S` here,invalidated by `delete` on `this->f` here,start of use after lifetime trace,accessed during call to `~S` here,accessed during call to `__infer_inner_destructor_~S` here,accessed `this->f` here,start of value trace,variable declared] codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::double_destructor_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `~S` here,when calling `__infer_inner_destructor_~S` here,memory invalidated by `delete` on `this->f` here,use-after-lifetime part of the trace starts here,when calling `~S` here,when calling `__infer_inner_destructor_~S` here,invalid access to `this->f` here,trace of how the access expression was constructed starts here,variable declared]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::placement_new_aliasing1_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `alias` here,start of use after lifetime trace,accessed `s->f` here,start of value trace,assigned to `s`] codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::placement_new_aliasing1_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `alias` here,use-after-lifetime part of the trace starts here,invalid access to `s->f` here,trace of how the access expression was constructed starts here,assigned to `s`]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::placement_new_aliasing2_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `s` here,start of use after lifetime trace,accessed `alias->f` here,start of value trace,assigned to `s`,returned from call to `<placement new>(sizeof(use_after_destructor::S),s)`,assigned to `alias`] codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::placement_new_aliasing2_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `s` here,use-after-lifetime part of the trace starts here,invalid access to `alias->f` here,trace of how the access expression was constructed starts here,assigned to `s`,returned from call to `<placement new>(sizeof(use_after_destructor::S),s)`,assigned to `alias`]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::placement_new_aliasing3_bad, 6, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `delete` on `s` here,start of use after lifetime trace,accessed `alias->f` here,start of value trace,assigned to `s`,assigned to `alias`] codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::placement_new_aliasing3_bad, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by `delete` on `s` here,use-after-lifetime part of the trace starts here,invalid access to `alias->f` here,trace of how the access expression was constructed starts here,assigned to `s`,assigned to `alias`]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::reinit_after_explicit_destructor2_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by during call to `~S` here,invalidated by during call to `__infer_inner_destructor_~S` here,invalidated by `delete` on `this->f` here,start of use after lifetime trace,accessed during call to `~S` here,accessed during call to `__infer_inner_destructor_~S` here,accessed `this->f` here,start of value trace,variable declared] codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::reinit_after_explicit_destructor2_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `~S` here,when calling `__infer_inner_destructor_~S` here,memory invalidated by `delete` on `this->f` here,use-after-lifetime part of the trace starts here,when calling `~S` here,when calling `__infer_inner_destructor_~S` here,invalid access to `this->f` here,trace of how the access expression was constructed starts here,variable declared]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::use_after_destructor_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by during call to `~S` here,invalidated by during call to `__infer_inner_destructor_~S` here,invalidated by `delete` on `this->f` here,start of use after lifetime trace,accessed `*(s.f)` here,start of value trace,assigned to `this->f`,returned from call to `use_after_destructor::S::S()`] codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::use_after_destructor_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `~S` here,when calling `__infer_inner_destructor_~S` here,memory invalidated by `delete` on `this->f` here,use-after-lifetime part of the trace starts here,invalid access to `*(s.f)` here,trace of how the access expression was constructed starts here,assigned to `this->f`,returned from call to `use_after_destructor::S::S()`]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::use_after_scope1_bad, 7, USE_AFTER_DELETE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by during call to `~S` here,invalidated by during call to `__infer_inner_destructor_~S` here,invalidated by `delete` on `this->f` here,start of use after lifetime trace,accessed during call to `~S` here,accessed during call to `__infer_inner_destructor_~S` here,accessed `this->f` here,start of value trace,variable declared] codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::use_after_scope1_bad, 7, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `~S` here,when calling `__infer_inner_destructor_~S` here,memory invalidated by `delete` on `this->f` here,use-after-lifetime part of the trace starts here,when calling `~S` here,when calling `__infer_inner_destructor_~S` here,invalid access to `this->f` here,trace of how the access expression was constructed starts here,variable declared]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::use_after_scope4_bad, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [start of end of lifetime trace,invalidated by `&(c)` gone out of scope here,start of use after lifetime trace,accessed `pc->f` here,start of value trace,variable declared,assigned to `pc`] codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor::use_after_scope4_bad, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidation part of the trace starts here,address of stack variable `c` whose lifetime has ended here,use-after-lifetime part of the trace starts here,invalid access to `pc->f` here]
codetoanalyze/cpp/pulse/use_after_free.cpp, double_free_global_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by during call to `free_global_pointer_ok()` here,invalidated by call to `free()` on `global_pointer` here,start of use after lifetime trace,accessed during call to `free_global_pointer_ok()` here,accessed `global_pointer` here] codetoanalyze/cpp/pulse/use_after_free.cpp, double_free_global_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `free_global_pointer_ok()` here,memory invalidated by call to `free()` on `global_pointer` here,use-after-lifetime part of the trace starts here,when calling `free_global_pointer_ok()` here,invalid access to `global_pointer` here]
codetoanalyze/cpp/pulse/use_after_free.cpp, double_free_simple_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by call to `free()` on `x` here,start of use after lifetime trace,accessed `x` here] codetoanalyze/cpp/pulse/use_after_free.cpp, double_free_simple_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by call to `free()` on `x` here,use-after-lifetime part of the trace starts here,invalid access to `x` here]
codetoanalyze/cpp/pulse/use_after_free.cpp, use_after_free_simple_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [start of end of lifetime trace,invalidated by call to `free()` on `x` here,start of use after lifetime trace,accessed `*(x)` here] codetoanalyze/cpp/pulse/use_after_free.cpp, use_after_free_simple_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,memory invalidated by call to `free()` on `x` here,use-after-lifetime part of the trace starts here,invalid access to `*(x)` here]
codetoanalyze/cpp/pulse/vector.cpp, assign_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [start of end of lifetime trace,invalidated by potentially invalidated by call to `std::vector::assign()` on `vec` here,start of use after lifetime trace,accessed `*(elt)` here,start of value trace,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`] codetoanalyze/cpp/pulse/vector.cpp, assign_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [invalidation part of the trace starts here,memory potentially invalidated by call to `std::vector::assign()` on `vec` here,use-after-lifetime part of the trace starts here,invalid access to `*(elt)` here,trace of how the access expression was constructed starts here,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`]
codetoanalyze/cpp/pulse/vector.cpp, clear_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [start of end of lifetime trace,invalidated by potentially invalidated by call to `std::vector::clear()` on `vec` here,start of use after lifetime trace,accessed `*(elt)` here,start of value trace,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`] codetoanalyze/cpp/pulse/vector.cpp, clear_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [invalidation part of the trace starts here,memory potentially invalidated by call to `std::vector::clear()` on `vec` here,use-after-lifetime part of the trace starts here,invalid access to `*(elt)` here,trace of how the access expression was constructed starts here,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`]
codetoanalyze/cpp/pulse/vector.cpp, deref_local_vector_element_after_push_back_bad, 4, VECTOR_INVALIDATION, no_bucket, ERROR, [start of end of lifetime trace,invalidated by potentially invalidated by call to `std::vector::push_back()` on `&(vec)` here,start of use after lifetime trace,accessed `*(elt)` here,start of value trace,returned from call to `std::vector::at(&(vec),(unsigned long) 1)`,assigned to `elt`] codetoanalyze/cpp/pulse/vector.cpp, deref_local_vector_element_after_push_back_bad, 4, VECTOR_INVALIDATION, no_bucket, ERROR, [invalidation part of the trace starts here,memory potentially invalidated by call to `std::vector::push_back()` on `&(vec)` here,use-after-lifetime part of the trace starts here,invalid access to `*(elt)` here,trace of how the access expression was constructed starts here,returned from call to `std::vector::at(&(vec),(unsigned long) 1)`,assigned to `elt`]
codetoanalyze/cpp/pulse/vector.cpp, deref_vector_element_after_push_back_bad, 4, VECTOR_INVALIDATION, no_bucket, ERROR, [start of end of lifetime trace,invalidated by potentially invalidated by call to `std::vector::push_back()` on `vec` here,start of use after lifetime trace,accessed `*(y)` here,start of value trace,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`,assigned to `y`] codetoanalyze/cpp/pulse/vector.cpp, deref_vector_element_after_push_back_bad, 4, VECTOR_INVALIDATION, no_bucket, ERROR, [invalidation part of the trace starts here,memory potentially invalidated by call to `std::vector::push_back()` on `vec` here,use-after-lifetime part of the trace starts here,invalid access to `*(y)` here,trace of how the access expression was constructed starts here,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`,assigned to `y`]
codetoanalyze/cpp/pulse/vector.cpp, emplace_back_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [start of end of lifetime trace,invalidated by potentially invalidated by call to `std::vector::emplace_back()` on `vec` here,start of use after lifetime trace,accessed `*(elt)` here,start of value trace,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`] codetoanalyze/cpp/pulse/vector.cpp, emplace_back_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [invalidation part of the trace starts here,memory potentially invalidated by call to `std::vector::emplace_back()` on `vec` here,use-after-lifetime part of the trace starts here,invalid access to `*(elt)` here,trace of how the access expression was constructed starts here,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`]
codetoanalyze/cpp/pulse/vector.cpp, emplace_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [start of end of lifetime trace,invalidated by potentially invalidated by call to `std::vector::emplace()` on `vec` here,start of use after lifetime trace,accessed `*(elt)` here,start of value trace,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`] codetoanalyze/cpp/pulse/vector.cpp, emplace_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [invalidation part of the trace starts here,memory potentially invalidated by call to `std::vector::emplace()` on `vec` here,use-after-lifetime part of the trace starts here,invalid access to `*(elt)` here,trace of how the access expression was constructed starts here,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`]
codetoanalyze/cpp/pulse/vector.cpp, insert_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [start of end of lifetime trace,invalidated by potentially invalidated by call to `std::vector::insert()` on `vec` here,start of use after lifetime trace,accessed `*(elt)` here,start of value trace,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`] codetoanalyze/cpp/pulse/vector.cpp, insert_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [invalidation part of the trace starts here,memory potentially invalidated by call to `std::vector::insert()` on `vec` here,use-after-lifetime part of the trace starts here,invalid access to `*(elt)` here,trace of how the access expression was constructed starts here,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`]
codetoanalyze/cpp/pulse/vector.cpp, push_back_loop_bad, 6, VECTOR_INVALIDATION, no_bucket, ERROR, [start of end of lifetime trace,invalidated by potentially invalidated by call to `std::vector::push_back()` on `&(vec)` here,start of use after lifetime trace,accessed `*(elt)` here,start of value trace,returned from call to `std::vector::at(&(vec),(unsigned long) 1)`,assigned to `elt`] codetoanalyze/cpp/pulse/vector.cpp, push_back_loop_bad, 6, VECTOR_INVALIDATION, no_bucket, ERROR, [invalidation part of the trace starts here,memory potentially invalidated by call to `std::vector::push_back()` on `&(vec)` here,use-after-lifetime part of the trace starts here,invalid access to `*(elt)` here,trace of how the access expression was constructed starts here,returned from call to `std::vector::at(&(vec),(unsigned long) 1)`,assigned to `elt`]
codetoanalyze/cpp/pulse/vector.cpp, reserve_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [start of end of lifetime trace,invalidated by potentially invalidated by call to `std::vector::reserve()` on `vec` here,start of use after lifetime trace,accessed `*(elt)` here,start of value trace,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`] codetoanalyze/cpp/pulse/vector.cpp, reserve_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [invalidation part of the trace starts here,memory potentially invalidated by call to `std::vector::reserve()` on `vec` here,use-after-lifetime part of the trace starts here,invalid access to `*(elt)` here,trace of how the access expression was constructed starts here,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`]
codetoanalyze/cpp/pulse/vector.cpp, shrink_to_fit_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [start of end of lifetime trace,invalidated by potentially invalidated by call to `std::vector::shrink_to_fit()` on `vec` here,start of use after lifetime trace,accessed `*(elt)` here,start of value trace,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`] codetoanalyze/cpp/pulse/vector.cpp, shrink_to_fit_bad, 3, VECTOR_INVALIDATION, no_bucket, ERROR, [invalidation part of the trace starts here,memory potentially invalidated by call to `std::vector::shrink_to_fit()` on `vec` here,use-after-lifetime part of the trace starts here,invalid access to `*(elt)` here,trace of how the access expression was constructed starts here,returned from call to `std::vector::at(vec,(unsigned long) 1)`,assigned to `elt`]

Loading…
Cancel
Save