Summary: When a method is called in ObjC on nil, there is no NPE, the method is actually not called and the return value is 0/false/nil. (There is an exception in the case where the return type is non-POD. In that case it's UB. This will be addressed later). To implement this behaviour we add additional summary to ObjC instance methods {self = 0} {return = 0}. We also want to make sure that inferred summary will not be used in we call a method on nil, hence, we add a path condition {self > 0} to get a contradiction when needed. Reviewed By: jvillard Differential Revision: D26664187 fbshipit-source-id: cdac2a5bbmaster
parent
3ba6a1e9df
commit
e4ff4b500a
@ -0,0 +1,70 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
open PulseBasicInterface
|
||||
open PulseOperations.Import
|
||||
|
||||
let mk_objc_self_pvar proc_desc =
|
||||
let proc_name = Procdesc.get_proc_name proc_desc in
|
||||
Pvar.mk Mangled.self proc_name
|
||||
|
||||
|
||||
let mk_objc_method_nil_summary_aux proc_desc astate =
|
||||
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
|
||||
let ret_var = Procdesc.get_ret_var proc_desc in
|
||||
let ret_addr = AbstractValue.mk_fresh () in
|
||||
let ret_value = (ret_addr, []) in
|
||||
let* astate, ret_var_addr_hist = PulseOperations.eval Write location (Lvar ret_var) astate in
|
||||
let* astate = PulseOperations.write_deref location ~ref:ret_var_addr_hist ~obj:ret_value astate in
|
||||
let astate = PulseArithmetic.and_eq_int ret_addr IntLit.zero astate in
|
||||
let+ astate =
|
||||
PulseOperations.invalidate location (ConstantDereference IntLit.zero) ret_value astate
|
||||
in
|
||||
[astate]
|
||||
|
||||
|
||||
let mk_objc_method_nil_summary ({InterproceduralAnalysis.proc_desc} as analysis_data) initial =
|
||||
let proc_name = Procdesc.get_proc_name proc_desc in
|
||||
match (initial, proc_name) with
|
||||
| ContinueProgram astate, Procname.ObjC_Cpp {kind= ObjCInstanceMethod} ->
|
||||
let result = mk_objc_method_nil_summary_aux proc_desc astate in
|
||||
Some (PulseReport.report_list_result analysis_data result)
|
||||
| ContinueProgram _, _
|
||||
| ExitProgram _, _
|
||||
| AbortProgram _, _
|
||||
| LatentAbortProgram _, _
|
||||
| ISLLatentMemoryError _, _ ->
|
||||
None
|
||||
|
||||
|
||||
let append_objc_self_positive ({InterproceduralAnalysis.proc_desc} as analysis_data) astate =
|
||||
let location = Procdesc.get_loc proc_desc in
|
||||
let self = mk_objc_self_pvar proc_desc in
|
||||
match astate with
|
||||
| ContinueProgram astate ->
|
||||
let result =
|
||||
let+ astate, value = PulseOperations.eval_deref location (Lvar self) astate in
|
||||
let astate = PulseArithmetic.prune_positive (fst value) astate in
|
||||
[astate]
|
||||
in
|
||||
PulseReport.report_list_result analysis_data result
|
||||
| ExitProgram _ | AbortProgram _ | LatentAbortProgram _ | ISLLatentMemoryError _ ->
|
||||
[astate]
|
||||
|
||||
|
||||
let update_objc_method_posts analysis_data ~initial_astate ~posts =
|
||||
let nil_summary = mk_objc_method_nil_summary analysis_data initial_astate in
|
||||
match nil_summary with
|
||||
| None ->
|
||||
posts
|
||||
| Some nil_summary ->
|
||||
let posts = List.concat_map ~f:(append_objc_self_positive analysis_data) posts in
|
||||
nil_summary @ posts
|
@ -0,0 +1,17 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
open PulseDomainInterface
|
||||
|
||||
val update_objc_method_posts :
|
||||
PulseSummary.t InterproceduralAnalysis.t
|
||||
-> initial_astate:ExecutionDomain.t
|
||||
-> posts:ExecutionDomain.t list
|
||||
-> ExecutionDomain.t list
|
||||
(** For ObjC instance methods: adds path condition `self > 0` to given posts and appends additional
|
||||
nil summary. Does nothing to posts for other kinds of methods *)
|
@ -1,9 +1,4 @@
|
||||
codetoanalyze/objcpp/pulse/AllocPatternMemLeak.mm, A.create_no_release_leak_bad, 1, MEMORY_LEAK, no_bucket, ERROR, [allocation part of the trace starts here,allocated by call to `ABFDataCreate` (modelled),allocation part of the trace ends here,memory becomes unreachable here]
|
||||
codetoanalyze/objcpp/pulse/NPEBasic.mm, FP_methodReturnsPODNilOk, 0, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,when calling `methodReturnsPOD` here,parameter `obj` of methodReturnsPOD,when calling `SomeObject.returnsPOD` here,parameter `self` of SomeObject.returnsPOD,invalid access occurs here]
|
||||
codetoanalyze/objcpp/pulse/NPEBasic.mm, FP_testAccessPropertyAccessorOk, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,when calling `SomeObject.x` here,parameter `self` of SomeObject.x,invalid access occurs here]
|
||||
codetoanalyze/objcpp/pulse/NPEBasic.mm, FP_testCallMethodReturnsPODOk, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,when calling `SomeObject.returnsPOD` here,parameter `self` of SomeObject.returnsPOD,invalid access occurs here]
|
||||
codetoanalyze/objcpp/pulse/NPEBasic.mm, dereferenceNilBad, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here]
|
||||
codetoanalyze/objcpp/pulse/NPEBasic.mm, testAccessPropertyAccessorBad, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,when calling `SomeObject.ptr` here,parameter `self` of SomeObject.ptr,invalid access occurs here]
|
||||
codetoanalyze/objcpp/pulse/NPEBasic.mm, testCallMethodReturnsnonPODBad, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,when calling `SomeObject.returnsnonPOD` here,parameter `self` of SomeObject.returnsnonPOD,invalid access occurs here]
|
||||
codetoanalyze/objcpp/pulse/use_after_delete.mm, PulseTest.deref_deleted_in_objc_method_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `new` (modelled),return from call to `new` (modelled),assigned,was invalidated by `delete`,use-after-lifetime part of the trace starts here,passed as argument to `new` (modelled),return from call to `new` (modelled),assigned,when calling `Simple::Simple` here,parameter `__param_0` of Simple::Simple,invalid access occurs here]
|
||||
codetoanalyze/objcpp/pulse/use_after_delete.mm, deref_deleted_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `new` (modelled),return from call to `new` (modelled),assigned,was invalidated by `delete`,use-after-lifetime part of the trace starts here,passed as argument to `new` (modelled),return from call to `new` (modelled),assigned,when calling `Simple::Simple` here,parameter `__param_0` of Simple::Simple,invalid access occurs here]
|
||||
|
Loading…
Reference in new issue