From bce1cce6bf1286541c57690ab1129fbc02c60f93 Mon Sep 17 00:00:00 2001 From: Artem Dergachev Date: Wed, 18 Dec 2019 13:19:44 -0800 Subject: [PATCH] [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(). --- .../lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 3 +++ .../Analysis/Inputs/system-header-simulator-objc.h | 5 ++++- clang/test/Analysis/malloc.mm | 23 +++++++++++++++++++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 01c7afe..0930638 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -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(), diff --git a/clang/test/Analysis/Inputs/system-header-simulator-objc.h b/clang/test/Analysis/Inputs/system-header-simulator-objc.h index df751d0..0dc6b36 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator-objc.h +++ b/clang/test/Analysis/Inputs/system-header-simulator-objc.h @@ -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 { diff --git a/clang/test/Analysis/malloc.mm b/clang/test/Analysis/malloc.mm index e84644b..1b7dd27 100644 --- a/clang/test/Analysis/malloc.mm +++ b/clang/test/Analysis/malloc.mm @@ -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}} -- 2.7.4