Skip to content

Commit bce1cce

Browse files
committed
[analyzer] Teach MismatchedDealloc about initWithBytesNoCopy with deallocator.
MallocChecker warns when memory is passed into -[NSData initWithBytesNoCopy] but isn't allocated by malloc(), because it will be deallocated by free(). However, initWithBytesNoCopy has an overload that takes an arbitrary block for deallocating the object. If such overload is used, it is no longer necessary to make sure that the memory is allocated by malloc().
1 parent 997bc8b commit bce1cce

File tree

3 files changed

+29
-2
lines changed

3 files changed

+29
-2
lines changed

clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,6 +1469,9 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
14691469
if (!*FreeWhenDone)
14701470
return;
14711471

1472+
if (Call.hasNonZeroCallbackArg())
1473+
return;
1474+
14721475
bool IsKnownToBeAllocatedMemory;
14731476
ProgramStateRef State =
14741477
FreeMemAux(C, Call.getArgExpr(0), Call.getOriginExpr(), C.getState(),

clang/test/Analysis/Inputs/system-header-simulator-objc.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,10 @@ typedef double NSTimeInterval;
117117
+ (id)dataWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b;
118118
- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length;
119119
- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b;
120-
- (id)initWithBytes:(void *)bytes length:(NSUInteger) length;
120+
- (id)initWithBytesNoCopy:(void *)bytes
121+
length:(NSUInteger)length
122+
deallocator:(void (^)(void *bytes, NSUInteger length))deallocator;
123+
- (id)initWithBytes:(void *)bytes length:(NSUInteger)length;
121124
@end
122125

123126
typedef struct {

clang/test/Analysis/malloc.mm

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
// RUN: %clang_analyze_cc1 -std=c++14 -analyzer-checker=core,unix.Malloc -analyzer-store=region -verify -fblocks %s
1+
// RUN: %clang_analyze_cc1 -std=c++14 \
2+
// RUN: -analyzer-checker=core,unix.Malloc,cplusplus.NewDelete \
3+
// RUN: -analyzer-checker=unix.MismatchedDeallocator \
4+
// RUN: -verify -fblocks %s
5+
26
#import "Inputs/system-header-simulator-objc.h"
37
#import "Inputs/system-header-simulator-for-malloc.h"
48

@@ -61,6 +65,23 @@ void testNSStringFreeWhenDoneNO(NSUInteger dataLength) {
6165
NSString *nsstr = [[NSString alloc] initWithBytesNoCopy:data length:dataLength encoding:NSUTF8StringEncoding freeWhenDone:0]; // expected-warning{{leak}}
6266
}
6367

68+
void testNSStringFreeWhenDoneNewDelete(NSUInteger dataLength) {
69+
unsigned char *data = new unsigned char(42);
70+
NSData *nsdata = [[NSData alloc] initWithBytesNoCopy:data
71+
length:dataLength freeWhenDone:1];
72+
// expected-warning@-2{{-initWithBytesNoCopy:length:freeWhenDone: cannot take ownership of memory allocated by 'new'}}
73+
}
74+
75+
void testNSStringFreeWhenDoneNewDelete2(NSUInteger dataLength) {
76+
unsigned char *data = new unsigned char(42);
77+
NSData *nsdata = [[NSData alloc] initWithBytesNoCopy:data
78+
length:dataLength
79+
deallocator:^(void *bytes,
80+
NSUInteger length) {
81+
delete (unsigned char *)bytes;
82+
}]; // no-warning
83+
}
84+
6485
void testNSStringFreeWhenDoneNO2(NSUInteger dataLength) {
6586
unichar *data = (unichar*)malloc(42);
6687
NSString *nsstr = [[NSString alloc] initWithCharactersNoCopy:data length:dataLength freeWhenDone:0]; // expected-warning{{leak}}

0 commit comments

Comments
 (0)