aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorJordan Rose <jordan_rose@apple.com>2013-03-09 00:59:10 +0000
committerJordan Rose <jordan_rose@apple.com>2013-03-09 00:59:10 +0000
commit9fe09f30f76cb65ca2a5fcd8e649f5b2f0cf02bd (patch)
tree85dc583de1428eb7355be7e093af0498744b038b /test
parent41d016454b48f25c93d4b3fb84cfc7e426a9bd73 (diff)
[analyzer] Be more consistent about Objective-C methods that free memory.
Previously, MallocChecker's pointer escape check and its post-call state update for Objective-C method calls had a fair amount duplicated logic and not-entirely-consistent checks. This commit restructures all this to be more consistent and possibly allow us to be more aggressive in warning about double-frees. New policy (applies to system header methods only): (1) If this is a method we know about, model it as taking/holding ownership of the passed-in buffer. (1a) ...unless there's a "freeWhenDone:" parameter with a zero (NO) value. (2) If there's a "freeWhenDone:" parameter (but it's not a method we know about), treat the buffer as escaping if the value is non-zero (YES) and non-escaping if it's zero (NO). (3) If the first selector piece ends with "NoCopy" (but it's not a method we know about and there's no "freeWhenDone:" parameter), treat the buffer as escaping. The reason that (2) and (3) don't explicitly model the ownership transfer is because we can't be sure that they will actually free the memory using free(), and we wouldn't want to emit a spurious "mismatched allocator" warning (coming in Anton's upcoming patch). In the future, we may have an idea of a "generic deallocation", i.e. we assume that the deallocator is correct but still continue tracking the region so that we can warn about double-frees. Patch by Anton Yartsev, with modifications from me. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@176744 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test')
-rw-r--r--test/Analysis/Inputs/system-header-simulator-for-malloc.h34
-rw-r--r--test/Analysis/malloc.mm47
2 files changed, 66 insertions, 15 deletions
diff --git a/test/Analysis/Inputs/system-header-simulator-for-malloc.h b/test/Analysis/Inputs/system-header-simulator-for-malloc.h
new file mode 100644
index 0000000000..e76455655e
--- /dev/null
+++ b/test/Analysis/Inputs/system-header-simulator-for-malloc.h
@@ -0,0 +1,34 @@
+// Like the compiler, the static analyzer treats some functions differently if
+// they come from a system header -- for example, it is assumed that system
+// functions do not arbitrarily free() their parameters, and that some bugs
+// found in system headers cannot be fixed by the user and should be
+// suppressed.
+#pragma clang system_header
+
+typedef __typeof(sizeof(int)) size_t;
+void *malloc(size_t);
+void *calloc(size_t, size_t);
+void free(void *);
+
+
+#if __OBJC__
+
+#import "system-header-simulator-objc.h"
+
+@interface Wrapper : NSData
+- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)len;
+@end
+
+@implementation Wrapper
+- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)len {
+ return [self initWithBytesNoCopy:bytes length:len freeWhenDone:1]; // no-warning
+}
+@end
+
+@interface CustomData : NSData
++ (id)somethingNoCopy:(char *)bytes;
++ (id)somethingNoCopy:(void *)bytes length:(NSUInteger)length freeWhenDone:(BOOL)freeBuffer;
++ (id)something:(char *)bytes freeWhenDone:(BOOL)freeBuffer;
+@end
+
+#endif
diff --git a/test/Analysis/malloc.mm b/test/Analysis/malloc.mm
index f2a195ce1d..2f583b45fd 100644
--- a/test/Analysis/malloc.mm
+++ b/test/Analysis/malloc.mm
@@ -1,19 +1,6 @@
// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-store=region -verify -fblocks %s
-#include "Inputs/system-header-simulator-objc.h"
-
-typedef __typeof(sizeof(int)) size_t;
-void *malloc(size_t);
-void free(void *);
-
-@interface Wrapper : NSData
-- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)len;
-@end
-
-@implementation Wrapper
-- (id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)len {
- return [self initWithBytesNoCopy:bytes length:len freeWhenDone:1]; // no-warning
-}
-@end
+#import "Inputs/system-header-simulator-objc.h"
+#import "Inputs/system-header-simulator-for-malloc.h"
// Done with headers. Start testing.
void testNSDatafFreeWhenDoneNoError(NSUInteger dataLength) {
@@ -79,6 +66,11 @@ void testNSStringFreeWhenDoneNO2(NSUInteger dataLength) {
NSString *nsstr = [[NSString alloc] initWithCharactersNoCopy:data length:dataLength freeWhenDone:0]; // expected-warning{{leak}}
}
+void testOffsetFree() {
+ int *p = (int *)malloc(sizeof(int));
+ NSData *nsdata = [NSData dataWithBytesNoCopy:++p length:sizeof(int) freeWhenDone:1]; // expected-warning{{Argument to free() is offset by 4 bytes from the start of memory allocated by malloc()}}
+}
+
void testRelinquished1() {
void *data = malloc(42);
NSData *nsdata = [NSData dataWithBytesNoCopy:data length:42 freeWhenDone:1];
@@ -92,6 +84,31 @@ void testRelinquished2() {
[NSData dataWithBytesNoCopy:data length:42]; // expected-warning {{Attempt to free released memory}}
}
+void testNoCopy() {
+ char *p = (char *)calloc(sizeof(int), 1);
+ CustomData *w = [CustomData somethingNoCopy:p]; // no-warning
+}
+
+void testFreeWhenDone() {
+ char *p = (char *)calloc(sizeof(int), 1);
+ CustomData *w = [CustomData something:p freeWhenDone:1]; // no-warning
+}
+
+void testFreeWhenDonePositive() {
+ char *p = (char *)calloc(sizeof(int), 1);
+ CustomData *w = [CustomData something:p freeWhenDone:0]; // expected-warning{{leak}}
+}
+
+void testFreeWhenDoneNoCopy() {
+ int *p = (int *)malloc(sizeof(int));
+ CustomData *w = [CustomData somethingNoCopy:p length:sizeof(int) freeWhenDone:1]; // no-warning
+}
+
+void testFreeWhenDoneNoCopyPositive() {
+ int *p = (int *)malloc(sizeof(int));
+ CustomData *w = [CustomData somethingNoCopy:p length:sizeof(int) freeWhenDone:0]; // expected-warning{{leak}}
+}
+
// Test CF/NS...NoCopy. PR12100: Pointers can escape when custom deallocators are provided.
void testNSDatafFreeWhenDone(NSUInteger dataLength) {
CFStringRef str;