[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().
This commit is contained in:
Artem Dergachev 2019-12-18 13:19:44 -08:00
parent 997bc8b2e6
commit bce1cce6bf
3 changed files with 29 additions and 2 deletions

View File

@ -1469,6 +1469,9 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
if (!*FreeWhenDone)
return;
if (Call.hasNonZeroCallbackArg())
return;
bool IsKnownToBeAllocatedMemory;
ProgramStateRef State =
FreeMemAux(C, Call.getArgExpr(0), Call.getOriginExpr(), C.getState(),

View File

@ -117,7 +117,10 @@ typedef double NSTimeInterval;
+ (id)dataWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b;
- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length;
- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)b;
- (id)initWithBytes:(void *)bytes length:(NSUInteger) length;
- (id)initWithBytesNoCopy:(void *)bytes
length:(NSUInteger)length
deallocator:(void (^)(void *bytes, NSUInteger length))deallocator;
- (id)initWithBytes:(void *)bytes length:(NSUInteger)length;
@end
typedef struct {

View File

@ -1,4 +1,8 @@
// RUN: %clang_analyze_cc1 -std=c++14 -analyzer-checker=core,unix.Malloc -analyzer-store=region -verify -fblocks %s
// RUN: %clang_analyze_cc1 -std=c++14 \
// RUN: -analyzer-checker=core,unix.Malloc,cplusplus.NewDelete \
// RUN: -analyzer-checker=unix.MismatchedDeallocator \
// RUN: -verify -fblocks %s
#import "Inputs/system-header-simulator-objc.h"
#import "Inputs/system-header-simulator-for-malloc.h"
@ -61,6 +65,23 @@ void testNSStringFreeWhenDoneNO(NSUInteger dataLength) {
NSString *nsstr = [[NSString alloc] initWithBytesNoCopy:data length:dataLength encoding:NSUTF8StringEncoding freeWhenDone:0]; // expected-warning{{leak}}
}
void testNSStringFreeWhenDoneNewDelete(NSUInteger dataLength) {
unsigned char *data = new unsigned char(42);
NSData *nsdata = [[NSData alloc] initWithBytesNoCopy:data
length:dataLength freeWhenDone:1];
// expected-warning@-2{{-initWithBytesNoCopy:length:freeWhenDone: cannot take ownership of memory allocated by 'new'}}
}
void testNSStringFreeWhenDoneNewDelete2(NSUInteger dataLength) {
unsigned char *data = new unsigned char(42);
NSData *nsdata = [[NSData alloc] initWithBytesNoCopy:data
length:dataLength
deallocator:^(void *bytes,
NSUInteger length) {
delete (unsigned char *)bytes;
}]; // no-warning
}
void testNSStringFreeWhenDoneNO2(NSUInteger dataLength) {
unichar *data = (unichar*)malloc(42);
NSString *nsstr = [[NSString alloc] initWithCharactersNoCopy:data length:dataLength freeWhenDone:0]; // expected-warning{{leak}}