[pulse] Init struct fields with zero when a message is sent to nil

Summary:
By D26664187 (e4ff4b500a), it returns nil when a message is sent to nil.

On the other hand, when the return type is a struct type, the semantics should be different: it should return a struct with all fields are initialized with nil.

According to

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html#//apple_ref/doc/uid/TP40011210-CH4-SW28

> Note: If you expect a return value from a message sent to nil, the return value will be nil for object return types, 0 for numeric types, and NO for BOOL types. Returned structures have all members initialized to zero.

Reviewed By: jvillard

Differential Revision: D27289996

fbshipit-source-id: 1ddd9b088
master
Sungkeun Cho 4 years ago committed by Facebook GitHub Bot
parent cd3c7b55da
commit 346380c649

@ -540,6 +540,8 @@ let get_ret_type pdesc = pdesc.attributes.ret_type
let get_ret_var pdesc = Pvar.get_ret_pvar (get_proc_name pdesc)
let get_ret_param_var pdesc = Pvar.get_ret_param_pvar (get_proc_name pdesc)
let get_start_node pdesc = pdesc.start_node
(** Return [true] iff the procedure is defined, and not just declared *)

@ -267,6 +267,8 @@ val is_ret_type_pod : t -> bool
val get_ret_var : t -> Pvar.t
val get_ret_param_var : t -> Pvar.t
val get_start_node : t -> Node.t
val get_static_callees : t -> Procname.t list

@ -6,6 +6,7 @@
*)
open! IStd
open PulseDomainInterface
open PulseOperations.Import
let mk_objc_self_pvar proc_desc =
@ -13,13 +14,43 @@ let mk_objc_self_pvar proc_desc =
Pvar.mk Mangled.self proc_name
let mk_objc_method_nil_summary_aux proc_desc astate =
let init_fields_zero tenv location ~zero addr typ astate =
let get_fields typ =
match typ.Typ.desc with
| Tstruct struct_name ->
Tenv.lookup tenv struct_name |> Option.map ~f:(fun {Struct.fields} -> fields)
| _ ->
None
in
let rec init_fields_zero_helper addr typ astate =
match get_fields typ with
| Some fields ->
List.fold fields ~init:(Ok astate) ~f:(fun acc (field, field_typ, _) ->
let* acc = acc in
let acc, field_addr = Memory.eval_edge addr (FieldAccess field) acc in
init_fields_zero_helper field_addr field_typ acc )
| None ->
PulseOperations.write_deref location ~ref:addr ~obj:zero astate
in
init_fields_zero_helper addr typ astate
let mk_objc_method_nil_summary_aux tenv proc_desc astate =
(* Constructs summary {self = 0} {return = self}.
This allows us to connect invalidation with invalid access in the trace *)
let location = Procdesc.get_loc proc_desc in
let self = mk_objc_self_pvar proc_desc in
let* astate, self_value = PulseOperations.eval_deref location (Lvar self) astate in
let* astate = PulseArithmetic.prune_eq_zero (fst self_value) astate in
match List.last (Procdesc.get_formals proc_desc) with
| Some (last_formal, {desc= Tptr (typ, _)}) when Mangled.equal last_formal Ident.name_return_param
->
let ret_param_var = Procdesc.get_ret_param_var proc_desc in
let* astate, ret_param_var_addr_hist =
PulseOperations.eval_deref location (Lvar ret_param_var) astate
in
init_fields_zero tenv location ~zero:self_value ret_param_var_addr_hist typ astate
| _ ->
let ret_var = Procdesc.get_ret_var proc_desc in
let* astate, ret_var_addr_hist = PulseOperations.eval Write location (Lvar ret_var) astate in
PulseOperations.write_deref location ~ref:ret_var_addr_hist ~obj:self_value astate
@ -35,7 +66,7 @@ let mk_objc_method_nil_summary {InterproceduralAnalysis.tenv; proc_desc; err_log
We create a nil summary to avoid reporting NPE in this case.
However, there is an exception in the case where the return type is non-POD.
In that case it's UB and we want to report an error. *)
let result = mk_objc_method_nil_summary_aux proc_desc astate in
let result = mk_objc_method_nil_summary_aux tenv proc_desc astate in
Some (PulseReport.report_result tenv proc_desc err_log result)
| ContinueProgram _, _
| ExitProgram _, _

@ -9,6 +9,11 @@
@interface Uninit : NSObject
@end
struct my_struct {
int x;
int y;
};
@implementation Uninit
- (void)capture_in_closure_ok {
@ -79,4 +84,16 @@
return x;
}
- (struct my_struct)return_my_struct {
struct my_struct s;
s.x = 1;
s.y = 2;
return s;
}
+ (int)call_return_my_struct_ok {
Uninit* o = nil;
return [o return_my_struct].x;
}
@end

Loading…
Cancel
Save