Skip to content

Commit

Permalink
[analyzer] Teach MismatchedDealloc about initWithBytesNoCopy with dea…
Browse files Browse the repository at this point in the history
…llocator.

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().
  • Loading branch information
haoNoQ committed Dec 18, 2019
1 parent 997bc8b commit bce1cce
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 2 deletions.
3 changes: 3 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
5 changes: 4 additions & 1 deletion clang/test/Analysis/Inputs/system-header-simulator-objc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
23 changes: 22 additions & 1 deletion clang/test/Analysis/malloc.mm
Original file line number Diff line number Diff line change
@@ -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"

Expand Down Expand Up @@ -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}}
Expand Down

0 comments on commit bce1cce

Please sign in to comment.