From e0f0022fa18c3648153f4214ef62b2808d9e3eca Mon Sep 17 00:00:00 2001 From: Daiva Naudziuniene Date: Wed, 3 Feb 2021 03:03:07 -0800 Subject: [PATCH] [pulse][objcpp][npe] Basic examples Summary: Added some basic examples for Objective-C we want to address next in pulse nullptr dereference analysis. In particular, we should not get a `nil` dereference error when we call a method on `nil`, except if the method returns a non-POD (Plain Old Data) type. Reviewed By: ezgicicek Differential Revision: D26053402 fbshipit-source-id: 66f4600c3 --- .../codetoanalyze/objcpp/pulse/NPEBasic.mm | 67 +++++++++++++++++++ .../codetoanalyze/objcpp/pulse/issues.exp | 6 ++ 2 files changed, 73 insertions(+) create mode 100644 infer/tests/codetoanalyze/objcpp/pulse/NPEBasic.mm diff --git a/infer/tests/codetoanalyze/objcpp/pulse/NPEBasic.mm b/infer/tests/codetoanalyze/objcpp/pulse/NPEBasic.mm new file mode 100644 index 000000000..b4462030b --- /dev/null +++ b/infer/tests/codetoanalyze/objcpp/pulse/NPEBasic.mm @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#import +#include + +@interface SomeObject : NSObject + +@property int x; +@property std::shared_ptr ptr; + +- (int)returnsPOD; + +- (std::shared_ptr)returnsnonPOD; + +@end + +@implementation SomeObject + +- (int)returnsPOD { + return _x; +} + +- (std::shared_ptr)returnsnonPOD { + return std::shared_ptr(new int(_x)); +} + +@end + +int dereferenceNilBad() { + int* int_ptr = nil; + return *int_ptr; +} + +int FP_testCallMethodReturnsPODOk() { + SomeObject* obj = nil; + return [obj returnsPOD]; +} + +std::shared_ptr testCallMethodReturnsnonPODBad() { + SomeObject* obj = nil; + std::shared_ptr d = [obj returnsnonPOD]; // UB + return d; +} + +int FP_testAccessPropertyAccessorOk() { + SomeObject* obj = nil; + return obj.x; // calls property accessor method +} + +std::shared_ptr testAccessPropertyAccessorBad() { + SomeObject* obj = nil; + return obj.ptr; // calls property accessor method, but return type is non-POD +} + +int methodReturnsPOD(SomeObject* obj) { return [obj returnsPOD]; }; + +int FP_methodReturnsPODNilOk() { return methodReturnsPOD(nil); }; + +int methodReturnsPODNotNilOK() { + SomeObject* o = [SomeObject new]; + return methodReturnsPOD(o); +} diff --git a/infer/tests/codetoanalyze/objcpp/pulse/issues.exp b/infer/tests/codetoanalyze/objcpp/pulse/issues.exp index 87c75e0d7..ee421aef9 100644 --- a/infer/tests/codetoanalyze/objcpp/pulse/issues.exp +++ b/infer/tests/codetoanalyze/objcpp/pulse/issues.exp @@ -1,3 +1,9 @@ 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]