diff --git a/infer/src/IR/HilExp.ml b/infer/src/IR/HilExp.ml index 7ab83e6ec..1bd8d1d14 100644 --- a/infer/src/IR/HilExp.ml +++ b/infer/src/IR/HilExp.ml @@ -233,6 +233,18 @@ module AccessExpression = struct 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 *) let rec to_access_path t = let rec to_access_path_ t = diff --git a/infer/src/IR/HilExp.mli b/infer/src/IR/HilExp.mli index bba7be2a8..2c0dccb04 100644 --- a/infer/src/IR/HilExp.mli +++ b/infer/src/IR/HilExp.mli @@ -88,6 +88,8 @@ module AccessExpression : sig [@@deriving compare] val fold_vars : (t, Var.t, 'accum) Container.fold + + val add_access : _ Access.t -> t -> t option end val pp : F.formatter -> t -> unit diff --git a/infer/src/pulse/Pulse.ml b/infer/src/pulse/Pulse.ml index 95ec959a4..616f9aa2e 100644 --- a/infer/src/pulse/Pulse.ml +++ b/infer/src/pulse/Pulse.ml @@ -64,7 +64,7 @@ module PulseTransferFunctions = struct >>= fun astate -> match lhs_access with | 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 | _ -> Ok astate ) @@ -145,25 +145,26 @@ module PulseTransferFunctions = struct if flags.cf_injected_destructor then match (call, actuals) with | ( 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) -> (* ignore inner destructors, only trigger out of scope on the final destructor call *) - Some destroyed_access + Some (pvar, typ) | _ -> None else None (** [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 *) - let invalidate access_expr = + let invalidate pvar typ access astate = PulseOperations.invalidate - (PulseTrace.Immediate {imm= GoneOutOfScope access_expr; location= call_loc}) - call_loc access_expr + (PulseTrace.Immediate {imm= GoneOutOfScope (pvar, typ); location= call_loc}) + call_loc access astate in - invalidate (HilExp.AccessExpression.dereference out_of_scope_access_expr) astate - >>= invalidate out_of_scope_access_expr + let out_of_scope_base = HilExp.AccessExpression.base (Var.of_pvar pvar, typ) in + 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 @@ -185,11 +186,11 @@ module PulseTransferFunctions = struct let posts = interprocedural_call summary ret call actuals flags call_loc astate in PerfEvent.(log (fun logger -> log_end_event logger ())) ; match get_out_of_scope_object call actuals flags with - | Some access_expr -> - L.d_printfln "%a is going out of scope" HilExp.AccessExpression.pp access_expr ; + | Some pvar_typ -> + L.d_printfln "%a is going out of scope" Pvar.pp_value (fst pvar_typ) ; 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 | None -> posts ) diff --git a/infer/src/pulse/PulseAbductiveDomain.ml b/infer/src/pulse/PulseAbductiveDomain.ml index 5ec9f7645..ebf68b949 100644 --- a/infer/src/pulse/PulseAbductiveDomain.ml +++ b/infer/src/pulse/PulseAbductiveDomain.ml @@ -323,7 +323,7 @@ module PrePost = struct if phys_equal astate call_state.astate then (call_state, var_value) else ({call_state with astate}, var_value) in - f ~stack_value ~addr_caller call_state + f var ~stack_value ~addr_caller 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 [call_state.astate] starting from address [addr_caller]. Report an error if some invalid addresses are traversed in the process. *) - let rec materialize_pre_from_address callee_proc_name call_location ~pre ~addr_pre ~addr_caller - trace call_state = + let rec materialize_pre_from_address callee_proc_name call_location access_expr ~pre ~addr_pre + ~addr_caller trace call_state = let mk_action action = PulseTrace.ViaCall {action; proc_name= callee_proc_name; location= call_location} in @@ -373,7 +373,8 @@ module PrePost = struct match Memory.check_valid action addr_caller call_state.astate with | Error invalidated_by -> Error - (PulseDiagnostic.AccessToInvalidAddress {invalidated_by; accessed_by= action; trace}) + (PulseDiagnostic.AccessToInvalidAddress + {access= access_expr; invalidated_by; accessed_by= action; trace}) | Ok astate -> let call_state = {call_state with astate} in Container.fold_result @@ -383,7 +384,13 @@ module PrePost = struct Memory.materialize_edge addr_caller access call_state.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 )) |> function Some result -> result | None -> Ok call_state ) @@ -393,20 +400,20 @@ module PrePost = struct [call_state.astate] *) 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) - (Option.map ~f:fst actual) ; + (Option.map ~f:fst3 actual) ; match actual with | None -> (* the expression representing the actual couldn't be evaluated down to an abstract address *) Ok call_state - | Some (addr_caller, trace) -> ( + | Some (addr_caller, access_expr, trace) -> ( (let open Option.Monad_infix in PulseDomain.Stack.find_opt formal pre.PulseDomain.stack >>= fun (addr_formal_pre, _) -> PulseDomain.Memory.find_edge_opt addr_formal_pre Dereference pre.PulseDomain.heap >>| fun (formal_pre, _) -> - materialize_pre_from_address callee_proc_name call_location ~pre ~addr_pre:formal_pre - ~addr_caller trace call_state) + materialize_pre_from_address callee_proc_name call_location access_expr ~pre + ~addr_pre:formal_pre ~addr_caller trace 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 = 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 = Option.map loc_opt ~f:(fun loc -> PulseTrace.VariableDeclaration loc) |> Option.to_list 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) ~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 = 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 | None -> call_state - | Some (addr_caller, _trace) -> ( + | Some (addr_caller, _access_expr, _trace) -> ( let open Option.Monad_infix in match 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 = match 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 (record_post_for_address callee_proc_name call_location pre_post ~addr_callee ~addr_caller call_state) ) diff --git a/infer/src/pulse/PulseAbductiveDomain.mli b/infer/src/pulse/PulseAbductiveDomain.mli index 9b06367e4..56b71ff64 100644 --- a/infer/src/pulse/PulseAbductiveDomain.mli +++ b/infer/src/pulse/PulseAbductiveDomain.mli @@ -76,7 +76,7 @@ module PrePost : sig -> t -> formals:Var.t list -> 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, PulseDiagnostic.t) result end diff --git a/infer/src/pulse/PulseDiagnostic.ml b/infer/src/pulse/PulseDiagnostic.ml index d056bed01..1959c8f7e 100644 --- a/infer/src/pulse/PulseDiagnostic.ml +++ b/infer/src/pulse/PulseDiagnostic.ml @@ -10,12 +10,13 @@ module F = Format type t = | 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 ; 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 @@ -27,10 +28,24 @@ let get_location = function let get_message = function - | AccessToInvalidAddress {accessed_by; invalidated_by; _} -> - F.asprintf "%a accesses address invalidated by %a past its lifetime" describe_access - accessed_by describe_invalidation invalidated_by - | StackVariableAddressEscape {variable} -> + | AccessToInvalidAddress {access; accessed_by; invalidated_by; _} -> + (* TODO: [access] might be something irrelevant to the user, shouldn't print it in that case + *) + 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 = if Var.is_cpp_temporary var then F.pp_print_string f "C++ temporary" else F.fprintf f "stack variable `%a`" Var.pp var @@ -53,20 +68,24 @@ let get_trace = function | Some location -> add_errlog_header ~title location errlog in - add_errlog_header ~title:"start of end of lifetime trace" - (PulseTrace.location_of_action_start invalidated_by) - @@ PulseTrace.add_errlog_of_action ~nesting:1 ~action_name:"invalidated by" - PulseInvalidation.describe invalidated_by - @@ add_errlog_header ~title:"start of use after lifetime trace" - (PulseTrace.location_of_action_start accessed_by) - @@ PulseTrace.add_errlog_of_action ~nesting:1 ~action_name:"accessed" - (Pp.in_backticks HilExp.AccessExpression.pp) - accessed_by - @@ add_header_if_some ~title:"start of value trace" (PulseTrace.get_start_location trace) + let pp_invalid_access f access = + F.fprintf f "invalid access to `%a`" HilExp.AccessExpression.pp access + in + add_errlog_header ~title:"invalidation part of the trace starts here" + (PulseTrace.outer_location_of_action invalidated_by) + @@ PulseTrace.add_errlog_of_action ~nesting:1 PulseInvalidation.describe invalidated_by + @@ add_errlog_header ~title:"use-after-lifetime part of the trace starts here" + (PulseTrace.outer_location_of_action accessed_by) + @@ PulseTrace.add_errlog_of_action ~nesting:1 pp_invalid_access accessed_by + @@ 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 @@ [] - | 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 diff --git a/infer/src/pulse/PulseDiagnostic.mli b/infer/src/pulse/PulseDiagnostic.mli index 1b5c1bd31..a324e8289 100644 --- a/infer/src/pulse/PulseDiagnostic.mli +++ b/infer/src/pulse/PulseDiagnostic.mli @@ -7,12 +7,14 @@ open! IStd +(** an error to report to the user *) type t = | 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 ; 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 diff --git a/infer/src/pulse/PulseInvalidation.ml b/infer/src/pulse/PulseInvalidation.ml index 5b3ed72ad..dbf0473aa 100644 --- a/infer/src/pulse/PulseInvalidation.ml +++ b/infer/src/pulse/PulseInvalidation.ml @@ -40,7 +40,7 @@ let pp_std_vector_function f = function type t = | CFree of HilExp.AccessExpression.t | CppDelete of HilExp.AccessExpression.t - | GoneOutOfScope of HilExp.AccessExpression.t + | GoneOutOfScope of Pvar.t * Typ.t | Nullptr | StdVector of std_vector_function * HilExp.AccessExpression.t [@@deriving compare] @@ -60,15 +60,21 @@ let issue_type_of_cause = function let describe f = function | 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 -> - F.fprintf f "`delete` on `%a`" HilExp.AccessExpression.pp access_expr - | GoneOutOfScope access_expr -> - F.fprintf f "`%a` gone out of scope" HilExp.AccessExpression.pp access_expr + F.fprintf f "memory invalidated by `delete` on `%a`" HilExp.AccessExpression.pp access_expr + | GoneOutOfScope (pvar, typ) -> + 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 -> - F.fprintf f "null pointer" + F.fprintf f "the null pointer" | 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 diff --git a/infer/src/pulse/PulseInvalidation.mli b/infer/src/pulse/PulseInvalidation.mli index 0add4660f..fd68557ef 100644 --- a/infer/src/pulse/PulseInvalidation.mli +++ b/infer/src/pulse/PulseInvalidation.mli @@ -22,7 +22,7 @@ val pp_std_vector_function : Format.formatter -> std_vector_function -> unit type t = | CFree of HilExp.AccessExpression.t | CppDelete of HilExp.AccessExpression.t - | GoneOutOfScope of HilExp.AccessExpression.t + | GoneOutOfScope of Pvar.t * Typ.t | Nullptr | StdVector of std_vector_function * HilExp.AccessExpression.t [@@deriving compare] diff --git a/infer/src/pulse/PulseOperations.ml b/infer/src/pulse/PulseOperations.ml index 49ecdf44b..2e80f8e0c 100644 --- a/infer/src/pulse/PulseOperations.ml +++ b/infer/src/pulse/PulseOperations.ml @@ -26,23 +26,24 @@ type t = PulseAbductiveDomain.t type 'a access_result = ('a, PulseDiagnostic.t) result (** 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 |> 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 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 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 = match dereference_to_ignore with | Some 0 -> Ok astate | _ -> - check_addr_access action addr_trace astate + check_addr_access access_expr action addr_trace astate in match (path, on_last) with | [], `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 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 = @@ -121,8 +122,10 @@ and walk_access_expr ~on_last astate access_expr location = let astate, base_addr_trace = let astate, (addr, init_loc_opt) = Stack.materialize access_var astate in let trace = - Option.value_map init_loc_opt ~default:[] ~f:(fun init_loc -> - [PulseTrace.VariableDeclaration init_loc] ) + Option.map init_loc_opt ~f:(fun init_loc -> + if Var.is_cpp_temporary access_var then PulseTrace.CppTemporaryCreated init_loc + else PulseTrace.VariableDeclaration init_loc ) + |> Option.to_list in (astate, (addr, trace)) in @@ -131,7 +134,7 @@ and walk_access_expr ~on_last astate access_expr location = Ok (astate, base_addr_trace) | _ -> 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) astate ) @@ -188,14 +191,14 @@ let write location access_expr addr astate = let invalidate cause location access_expr astate = materialize_address astate access_expr location >>= 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) let invalidate_array_elements cause location access_expr astate = materialize_address astate access_expr location >>= 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 -> match Memory.find_opt (fst addr_trace) astate with | None -> @@ -211,33 +214,33 @@ let invalidate_array_elements cause location access_expr astate = edges astate -let check_address_of_local_variable proc_desc address astate = - let proc_location = Procdesc.get_loc proc_desc in - let proc_name = Procdesc.get_proc_name proc_desc in +let check_address_escape escape_location proc_desc address trace astate = let check_address_of_cpp_temporary () = Memory.find_opt address astate |> Option.fold_result ~init:() ~f:(fun () (_, attrs) -> IContainer.iter_result ~fold:Attributes.fold attrs ~f:(fun attr -> match attr with - | Attribute.AddressOfCppTemporary (variable, location_opt) -> - let location = Option.value ~default:proc_location location_opt in - Error (PulseDiagnostic.StackVariableAddressEscape {variable; location}) + | Attribute.AddressOfCppTemporary (variable, _) -> + Error + (PulseDiagnostic.StackVariableAddressEscape + {variable; location= escape_location; trace}) | _ -> Ok () ) ) in 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 - ~f:(fun (variable, (var_address, init_location)) -> + ~f:(fun (variable, (var_address, _)) -> if AbstractAddress.equal var_address address && ( Var.is_cpp_temporary variable || Var.is_local_to_procedure proc_name variable && not (Procdesc.is_captured_var proc_desc variable) ) then ( - let location = Option.value ~default:proc_location init_location in - L.d_printfln_escaped "Stack Variable &%a detected at address %a" Var.pp variable + L.d_printfln_escaped "Stack variable address &%a detected at address %a" Var.pp variable AbstractAddress.pp address ; - Error (PulseDiagnostic.StackVariableAddressEscape {variable; location}) ) + Error + (PulseDiagnostic.StackVariableAddressEscape {variable; location= escape_location; trace}) ) else Ok () ) in 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 -> match Stack.find_opt var astate with | 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 | _ -> heap ) @@ -305,7 +306,7 @@ module Closures = struct ~fold:(IContainer.fold_of_pervasives_map_fold ~fold:Memory.Edges.fold) edges ~f:(fun (access, addr_trace) -> 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 _ -> () else Ok () ) | _ -> @@ -365,7 +366,8 @@ module Interproc = struct match actual with | HilExp.AccessExpression access_expr -> 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) ) >>= fun (astate, rev_actual_addresses) -> diff --git a/infer/src/pulse/PulseOperations.mli b/infer/src/pulse/PulseOperations.mli index e455122fc..245512e2d 100644 --- a/infer/src/pulse/PulseOperations.mli +++ b/infer/src/pulse/PulseOperations.mli @@ -76,8 +76,8 @@ val invalidate_array_elements : val remove_vars : Var.t list -> t -> t -(* TODO: better name and pass location to report where we returned *) -val check_address_of_local_variable : Procdesc.t -> AbstractAddress.t -> t -> t access_result +val check_address_escape : + Location.t -> Procdesc.t -> AbstractAddress.t -> PulseTrace.t -> t -> t access_result module Interproc : sig val call : diff --git a/infer/src/pulse/PulseTrace.ml b/infer/src/pulse/PulseTrace.ml index a297a29d9..25ec9e987 100644 --- a/infer/src/pulse/PulseTrace.ml +++ b/infer/src/pulse/PulseTrace.ml @@ -9,6 +9,7 @@ module F = Format type breadcrumb = | VariableDeclaration of Location.t + | CppTemporaryCreated of Location.t | Assignment of {lhs: HilExp.AccessExpression.t; location: Location.t} | Capture of { captured_as: AccessPath.base @@ -22,7 +23,9 @@ type breadcrumb = let pp_breadcrumb_no_location fmt = function | 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= _} -> F.fprintf fmt "`%a` captured as `%a`" HilExp.AccessExpression.pp captured AccessPath.pp_base captured_as @@ -39,7 +42,11 @@ let pp_breadcrumb_no_location fmt = 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 @@ -77,11 +84,6 @@ let pp_action pp_immediate fmt = function 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 | Immediate {imm; _} -> imm @@ -89,21 +91,19 @@ let rec immediate_of_action = function 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 = match action with | Immediate {imm; location} -> let rev_errlog = - Errlog.make_trace_element nesting location - (F.asprintf "%s %a here" action_name pp_immediate imm) - [] + Errlog.make_trace_element nesting location (F.asprintf "%a here" pp_immediate imm) [] :: rev_errlog in List.rev_append rev_errlog errlog | ViaCall {action; proc_name; location} -> aux ~nesting:(nesting + 1) ( 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 ) action diff --git a/infer/src/pulse/PulseTrace.mli b/infer/src/pulse/PulseTrace.mli index 3a2f673a2..6b42482ff 100644 --- a/infer/src/pulse/PulseTrace.mli +++ b/infer/src/pulse/PulseTrace.mli @@ -10,6 +10,7 @@ module F = Format type breadcrumb = | VariableDeclaration of Location.t + | CppTemporaryCreated of Location.t | Assignment of {lhs: HilExp.AccessExpression.t; location: Location.t} | Capture of { captured_as: AccessPath.base @@ -36,15 +37,12 @@ type 'a action = 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 outer_location_of_action : 'a action -> Location.t val add_errlog_of_action : nesting:int - -> action_name:string -> (F.formatter -> 'a -> unit) -> 'a action -> Errlog.loc_trace_elem sexp_list diff --git a/infer/tests/codetoanalyze/cpp/pulse/issues.exp b/infer/tests/codetoanalyze/cpp/pulse/issues.exp index 14d95286f..71f0c6746 100644 --- a/infer/tests/codetoanalyze/cpp/pulse/issues.exp +++ b/infer/tests/codetoanalyze/cpp/pulse/issues.exp @@ -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_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/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, 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/deduplication.cpp, deduplication::SomeTemplatedClass::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::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/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_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_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_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, 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/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/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_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/returns.cpp, returns::return_literal_stack_reference_bad, 0, STACK_VARIABLE_ADDRESS_ESCAPE, no_bucket, ERROR, [] -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_variable_stack_reference1_bad, 1, STACK_VARIABLE_ADDRESS_ESCAPE, no_bucket, ERROR, [] -codetoanalyze/cpp/pulse/returns.cpp, returns::return_variable_stack_reference2_bad, 1, STACK_VARIABLE_ADDRESS_ESCAPE, no_bucket, ERROR, [] -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_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, 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, 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, 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, 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_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_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::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_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 `(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::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::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_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_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_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_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, [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/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, 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, 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_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, 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_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, 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, 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, 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, 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/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, [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, [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, [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::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::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, [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, [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, [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, [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, [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, [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, [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, [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, [C++ temporary created,returned here] +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, 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, 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, [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, [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, [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, [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, [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, [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, [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, [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, [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, [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 `(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, [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, [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, [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, [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, [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, [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, [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, [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, [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, [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, [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, [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, [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, [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, [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, [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, [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, [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`]