diff options
Diffstat (limited to 'test/Analysis')
-rw-r--r-- | test/Analysis/additive-folding.c | 14 | ||||
-rw-r--r-- | test/Analysis/array-struct-region.c | 47 | ||||
-rw-r--r-- | test/Analysis/bstring.c | 37 | ||||
-rw-r--r-- | test/Analysis/constant-folding.c | 74 | ||||
-rw-r--r-- | test/Analysis/dead-stores.c | 32 | ||||
-rw-r--r-- | test/Analysis/flat-store.c | 11 | ||||
-rw-r--r-- | test/Analysis/idempotent-operations.c | 203 | ||||
-rw-r--r-- | test/Analysis/idempotent-operations.cpp | 15 | ||||
-rw-r--r-- | test/Analysis/malloc.c | 124 | ||||
-rw-r--r-- | test/Analysis/misc-ps-region-store.cpp | 27 | ||||
-rw-r--r-- | test/Analysis/misc-ps-region-store.m | 101 | ||||
-rw-r--r-- | test/Analysis/misc-ps.m | 58 | ||||
-rw-r--r-- | test/Analysis/null-deref-ps.c | 9 | ||||
-rw-r--r-- | test/Analysis/outofbound.c | 24 | ||||
-rw-r--r-- | test/Analysis/plist-output.m | 2 | ||||
-rw-r--r-- | test/Analysis/retain-release-region-store.m | 27 | ||||
-rw-r--r-- | test/Analysis/retain-release.m | 9 | ||||
-rw-r--r-- | test/Analysis/stack-addr-ps.cpp | 8 | ||||
-rw-r--r-- | test/Analysis/stream.c | 38 | ||||
-rw-r--r-- | test/Analysis/string.c | 240 | ||||
-rw-r--r-- | test/Analysis/uninit-vals-ps-region.m | 4 | ||||
-rw-r--r-- | test/Analysis/unreachable-code-path.c | 104 |
22 files changed, 1097 insertions, 111 deletions
diff --git a/test/Analysis/additive-folding.c b/test/Analysis/additive-folding.c index 15d7588..e4a5651 100644 --- a/test/Analysis/additive-folding.c +++ b/test/Analysis/additive-folding.c @@ -18,7 +18,7 @@ void separateExpressions (int a) { char* buf = malloc(1); if (a != 0 && b == 0) - return; // no-warning + return; // expected-warning{{never executed}} free(buf); } @@ -29,7 +29,7 @@ void oneLongExpression (int a) { char* buf = malloc(1); if (a != 0 && b == 0) - return; // no-warning + return; // expected-warning{{never executed}} free(buf); } @@ -40,11 +40,11 @@ void mixedTypes (int a) { // This is part of PR7406. int b = a + 1LL; if (a != 0 && (b-1) == 0) // not crash - return; // no warning + return; // expected-warning{{never executed}} int c = a + 1U; if (a != 0 && (c-1) == 0) // not crash - return; // no warning + return; // expected-warning{{never executed}} free(buf); } @@ -85,7 +85,7 @@ void mixed_eq_ne (int a) { if (a+1U != 2) return; // no-warning if (a-1U != 0) - return; // no-warning + return; // expected-warning{{never executed}} free(b); } @@ -96,7 +96,7 @@ void mixed_ne_eq (int a) { if (a+1U == 2) return; // no-warning if (a-1U == 0) - return; // no-warning + return; // expected-warning{{never executed}} free(b); } @@ -191,7 +191,7 @@ void tautologyGE (unsigned a) { void tautologyLT (unsigned a) { char* b = malloc(1); if (a < 0) - return; // no-warning + return; // expected-warning{{never executed}} free(b); } diff --git a/test/Analysis/array-struct-region.c b/test/Analysis/array-struct-region.c new file mode 100644 index 0000000..dabd25b --- /dev/null +++ b/test/Analysis/array-struct-region.c @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -analyze -analyzer-experimental-checks -analyzer-experimental-internal-checks -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=basic -verify %s +// RUN: %clang_cc1 -analyze -analyzer-experimental-checks -analyzer-experimental-internal-checks -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=range -verify %s + +int string_literal_init() { + char a[] = "abc"; + char b[2] = "abc"; // expected-warning{{too long}} + char c[5] = "abc"; + + if (a[1] != 'b') + return 0; // expected-warning{{never executed}} + if (b[1] != 'b') + return 0; // expected-warning{{never executed}} + if (c[1] != 'b') + return 0; // expected-warning{{never executed}} + + if (a[3] != 0) + return 0; // expected-warning{{never executed}} + if (c[3] != 0) + return 0; // expected-warning{{never executed}} + + if (c[4] != 0) + return 0; // expected-warning{{never executed}} + + return 42; +} + +void nested_compound_literals(int rad) { + int vec[6][2] = {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, + {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}}; + int a; + + for (a = 0; a < 6; ++a) { + vec[a][0] *= rad; // no-warning + vec[a][1] *= rad; // no-warning + } +} + +void nested_compound_literals_float(float rad) { + float vec[6][2] = {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, + {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}}; + int a; + + for (a = 0; a < 6; ++a) { + vec[a][0] *= rad; // no-warning + vec[a][1] *= rad; // no-warning + } +} diff --git a/test/Analysis/bstring.c b/test/Analysis/bstring.c index f4ddb0a..ffe420f 100644 --- a/test/Analysis/bstring.c +++ b/test/Analysis/bstring.c @@ -48,27 +48,30 @@ void *memcpy(void *restrict s1, const void *restrict s2, size_t n); void memcpy0 () { char src[] = {1, 2, 3, 4}; - char dst[4]; + char dst[4] = {0}; memcpy(dst, src, 4); // no-warning if (memcpy(dst, src, 4) != dst) { - (void)*(char*)0; // no-warning -- should be unreachable + (void)*(char*)0; // no-warning } + + if (dst[0] != 0) + (void)*(char*)0; // expected-warning{{null}} } void memcpy1 () { char src[] = {1, 2, 3, 4}; char dst[10]; - memcpy(dst, src, 5); // expected-warning{{out-of-bound}} + memcpy(dst, src, 5); // expected-warning{{Byte string function accesses out-of-bound array element}} } void memcpy2 () { char src[] = {1, 2, 3, 4}; char dst[1]; - memcpy(dst, src, 4); // expected-warning{{out-of-bound}} + memcpy(dst, src, 4); // expected-warning{{Byte string function overflows destination buffer}} } void memcpy3 () { @@ -82,14 +85,14 @@ void memcpy4 () { char src[] = {1, 2, 3, 4}; char dst[10]; - memcpy(dst+2, src+2, 3); // expected-warning{{out-of-bound}} + memcpy(dst+2, src+2, 3); // expected-warning{{Byte string function accesses out-of-bound array element}} } void memcpy5() { char src[] = {1, 2, 3, 4}; char dst[3]; - memcpy(dst+2, src+2, 2); // expected-warning{{out-of-bound}} + memcpy(dst+2, src+2, 2); // expected-warning{{Byte string function overflows destination buffer}} } void memcpy6() { @@ -150,13 +153,16 @@ void *memmove(void *s1, const void *s2, size_t n); void memmove0 () { char src[] = {1, 2, 3, 4}; - char dst[4]; + char dst[4] = {0}; memmove(dst, src, 4); // no-warning if (memmove(dst, src, 4) != dst) { - (void)*(char*)0; // no-warning -- should be unreachable + (void)*(char*)0; // no-warning } + + if (dst[0] != 0) + (void)*(char*)0; // expected-warning{{null}} } void memmove1 () { @@ -170,7 +176,7 @@ void memmove2 () { char src[] = {1, 2, 3, 4}; char dst[1]; - memmove(dst, src, 4); // expected-warning{{out-of-bound}} + memmove(dst, src, 4); // expected-warning{{overflow}} } //===----------------------------------------------------------------------=== @@ -246,6 +252,12 @@ void memcmp6 (char *a, char *b, size_t n) { (void)*(char*)0; // expected-warning{{null}} } +int memcmp7 (char *a, size_t x, size_t y, size_t n) { + // We used to crash when either of the arguments was unknown. + return memcmp(a, &a[x*y], n) + + memcmp(&a[x*y], a, n); +} + //===----------------------------------------------------------------------=== // bcopy() //===----------------------------------------------------------------------=== @@ -257,9 +269,12 @@ void bcopy(/*const*/ void *s1, void *s2, size_t n); void bcopy0 () { char src[] = {1, 2, 3, 4}; - char dst[4]; + char dst[4] = {0}; bcopy(src, dst, 4); // no-warning + + if (dst[0] != 0) + (void)*(char*)0; // expected-warning{{null}} } void bcopy1 () { @@ -273,5 +288,5 @@ void bcopy2 () { char src[] = {1, 2, 3, 4}; char dst[1]; - bcopy(src, dst, 4); // expected-warning{{out-of-bound}} + bcopy(src, dst, 4); // expected-warning{{overflow}} } diff --git a/test/Analysis/constant-folding.c b/test/Analysis/constant-folding.c index 6ed2b39..9191a9e 100644 --- a/test/Analysis/constant-folding.c +++ b/test/Analysis/constant-folding.c @@ -9,51 +9,51 @@ void testComparisons (int a) { // Sema can already catch the simple comparison a==a, // since that's usually a logic error (and not path-dependent). int b = a; - if (!(b==a)) WARN; - if (!(b>=a)) WARN; - if (!(b<=a)) WARN; - if (b!=a) WARN; - if (b>a) WARN; - if (b<a) WARN; + if (!(b==a)) WARN; // expected-warning{{never executed}} + if (!(b>=a)) WARN; // expected-warning{{never executed}} + if (!(b<=a)) WARN; // expected-warning{{never executed}} + if (b!=a) WARN; // expected-warning{{never executed}} + if (b>a) WARN; // expected-warning{{never executed}} + if (b<a) WARN; // expected-warning{{never executed}} } void testSelfOperations (int a) { - if ((a|a) != a) WARN; - if ((a&a) != a) WARN; - if ((a^a) != 0) WARN; - if ((a-a) != 0) WARN; + if ((a|a) != a) WARN; // expected-warning{{never executed}} + if ((a&a) != a) WARN; // expected-warning{{never executed}} + if ((a^a) != 0) WARN; // expected-warning{{never executed}} + if ((a-a) != 0) WARN; // expected-warning{{never executed}} } void testIdempotent (int a) { - if ((a*1) != a) WARN; - if ((a/1) != a) WARN; - if ((a+0) != a) WARN; - if ((a-0) != a) WARN; - if ((a<<0) != a) WARN; - if ((a>>0) != a) WARN; - if ((a^0) != a) WARN; - if ((a&(~0)) != a) WARN; - if ((a|0) != a) WARN; + if ((a*1) != a) WARN; // expected-warning{{never executed}} + if ((a/1) != a) WARN; // expected-warning{{never executed}} + if ((a+0) != a) WARN; // expected-warning{{never executed}} + if ((a-0) != a) WARN; // expected-warning{{never executed}} + if ((a<<0) != a) WARN; // expected-warning{{never executed}} + if ((a>>0) != a) WARN; // expected-warning{{never executed}} + if ((a^0) != a) WARN; // expected-warning{{never executed}} + if ((a&(~0)) != a) WARN; // expected-warning{{never executed}} + if ((a|0) != a) WARN; // expected-warning{{never executed}} } void testReductionToConstant (int a) { - if ((a*0) != 0) WARN; - if ((a&0) != 0) WARN; - if ((a|(~0)) != (~0)) WARN; + if ((a*0) != 0) WARN; // expected-warning{{never executed}} + if ((a&0) != 0) WARN; // expected-warning{{never executed}} + if ((a|(~0)) != (~0)) WARN; // expected-warning{{never executed}} } void testSymmetricIntSymOperations (int a) { - if ((2+a) != (a+2)) WARN; - if ((2*a) != (a*2)) WARN; - if ((2&a) != (a&2)) WARN; - if ((2^a) != (a^2)) WARN; - if ((2|a) != (a|2)) WARN; + if ((2+a) != (a+2)) WARN; // expected-warning{{never executed}} + if ((2*a) != (a*2)) WARN; // expected-warning{{never executed}} + if ((2&a) != (a&2)) WARN; // expected-warning{{never executed}} + if ((2^a) != (a^2)) WARN; // expected-warning{{never executed}} + if ((2|a) != (a|2)) WARN; // expected-warning{{never executed}} } void testAsymmetricIntSymOperations (int a) { - if (((~0) >> a) != (~0)) WARN; - if ((0 >> a) != 0) WARN; - if ((0 << a) != 0) WARN; + if (((~0) >> a) != (~0)) WARN; // expected-warning{{never executed}} + if ((0 >> a) != 0) WARN; // expected-warning{{never executed}} + if ((0 << a) != 0) WARN; // expected-warning{{never executed}} // Unsigned right shift shifts in zeroes. if ((((unsigned)(~0)) >> ((unsigned) a)) != ((unsigned)(~0))) @@ -62,11 +62,11 @@ void testAsymmetricIntSymOperations (int a) { void testLocations (char *a) { char *b = a; - if (!(b==a)) WARN; - if (!(b>=a)) WARN; - if (!(b<=a)) WARN; - if (b!=a) WARN; - if (b>a) WARN; - if (b<a) WARN; - if (b-a) WARN; + if (!(b==a)) WARN; // expected-warning{{never executed}} + if (!(b>=a)) WARN; // expected-warning{{never executed}} + if (!(b<=a)) WARN; // expected-warning{{never executed}} + if (b!=a) WARN; // expected-warning{{never executed}} + if (b>a) WARN; // expected-warning{{never executed}} + if (b<a) WARN; // expected-warning{{never executed}} + if (b-a) WARN; // expected-warning{{never executed}} } diff --git a/test/Analysis/dead-stores.c b/test/Analysis/dead-stores.c index defd7e0..57d5d11 100644 --- a/test/Analysis/dead-stores.c +++ b/test/Analysis/dead-stores.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -Wunused-variable -analyze -analyzer-experimental-internal-checks -analyzer-check-dead-stores -fblocks -verify -Wno-unreachable-code -analyzer-opt-analyze-nested-blocks %s +// RUN: %clang_cc1 -Wunused-variable -analyze -analyzer-experimental-internal-checks -analyzer-check-objc-mem -analyzer-check-dead-stores -fblocks -verify -Wno-unreachable-code -analyzer-opt-analyze-nested-blocks %s // RUN: %clang_cc1 -Wunused-variable -analyze -analyzer-experimental-internal-checks -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=basic -analyzer-check-dead-stores -fblocks -verify -Wno-unreachable-code -analyzer-opt-analyze-nested-blocks %s // RUN: %clang_cc1 -Wunused-variable -analyze -analyzer-experimental-internal-checks -analyzer-check-objc-mem -analyzer-store=basic -analyzer-constraints=range -analyzer-check-dead-stores -fblocks -verify -Wno-unreachable-code -analyzer-opt-analyze-nested-blocks %s // RUN: %clang_cc1 -Wunused-variable -analyze -analyzer-experimental-internal-checks -analyzer-check-objc-mem -analyzer-store=region -analyzer-constraints=basic -analyzer-check-dead-stores -fblocks -verify -Wno-unreachable-code -analyzer-opt-analyze-nested-blocks %s @@ -150,7 +150,7 @@ void f15(unsigned x, unsigned y) { int f16(int x) { x = x * 2; - x = sizeof(int [x = (x || x + 1) * 2]) // expected-warning{{Although the value stored to 'x' is used}} + x = sizeof(int [x = (x || x + 1) * 2]) // expected-warning{{Although the value stored to 'x' is used}} expected-warning{{The left operand to '*' is always 1}} ? 5 : 8; return x; } @@ -158,7 +158,7 @@ int f16(int x) { // Self-assignments should not be flagged as dead stores. void f17() { int x = 1; - x = x; // no-warning + x = x; } // <rdar://problem/6506065> @@ -458,7 +458,31 @@ void rdar8014335() { // Note that the next value stored to 'i' is never executed // because the next statement to be executed is the 'break' // in the increment code of the first loop. - i = i * 3; // expected-warning{{Value stored to 'i' is never read}} + i = i * 3; // expected-warning{{Value stored to 'i' is never read}} expected-warning{{The left operand to '*' is always 1}} } } +// <rdar://problem/8320674> NullStmts followed by do...while() can lead to disconnected CFG +// +// This previously caused bogus dead-stores warnings because the body of the first do...while was +// disconnected from the entry of the function. +typedef struct { float r; float i; } s_rdar8320674; +typedef struct { s_rdar8320674 x[1]; } s2_rdar8320674; + +void rdar8320674(s_rdar8320674 *z, unsigned y, s2_rdar8320674 *st, int m) +{ + s_rdar8320674 * z2; + s_rdar8320674 * tw1 = st->x; + s_rdar8320674 t; + z2 = z + m; + do{ + ; ; + do{ (t).r = (*z2).r*(*tw1).r - (*z2).i*(*tw1).i; (t).i = (*z2).r*(*tw1).i + (*z2).i*(*tw1).r; }while(0); + tw1 += y; + do { (*z2).r=(*z).r-(t).r; (*z2).i=(*z).i-(t).i; }while(0); + do { (*z).r += (t).r; (*z).i += (t).i; }while(0); + ++z2; + ++z; + }while (--m); +} + diff --git a/test/Analysis/flat-store.c b/test/Analysis/flat-store.c new file mode 100644 index 0000000..bb274b0 --- /dev/null +++ b/test/Analysis/flat-store.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=flat -verify %s +#define FAIL ((void)*(char*)0) +struct simple { int x; }; + +void PR7297 () { + struct simple a; + struct simple *p = &a; + p->x = 5; + if (!p[0].x) FAIL; // no-warning + if (p[0].x) FAIL; // expected-warning {{null}} +} diff --git a/test/Analysis/idempotent-operations.c b/test/Analysis/idempotent-operations.c index 9cef08e..5c9a59d 100644 --- a/test/Analysis/idempotent-operations.c +++ b/test/Analysis/idempotent-operations.c @@ -1,52 +1,189 @@ -// RUN: %clang_cc1 -analyze -analyzer-idempotent-operation -analyzer-store=region -analyzer-constraints=range -fblocks -verify -analyzer-opt-analyze-nested-blocks -analyzer-check-objc-mem -verify %s +// RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-constraints=range -fblocks -analyzer-opt-analyze-nested-blocks -analyzer-check-objc-mem -analyzer-check-idempotent-operations -verify %s // Basic tests extern void test(int i); +extern void test_f(float f); -void basic() { +unsigned basic() { int x = 10, zero = 0, one = 1; // x op x - x = x; // expected-warning {{idempotent operation; both operands are always equal in value}} - test(x - x); // expected-warning {{idempotent operation; both operands are always equal in value}} - x -= x; // expected-warning {{idempotent operation; both operands are always equal in value}} + x = x; // expected-warning {{Assigned value is always the same as the existing value}} + test(x - x); // expected-warning {{Both operands to '-' always have the same value}} + x -= x; // expected-warning {{Both operands to '-=' always have the same value}} x = 10; // no-warning - test(x / x); // expected-warning {{idempotent operation; both operands are always equal in value}} - x /= x; // expected-warning {{idempotent operation; both operands are always equal in value}} + test(x / x); // expected-warning {{Both operands to '/' always have the same value}} + x /= x; // expected-warning {{Both operands to '/=' always have the same value}} x = 10; // no-warning - test(x & x); // expected-warning {{idempotent operation; both operands are always equal in value}} - x &= x; // expected-warning {{idempotent operation; both operands are always equal in value}} - test(x | x); // expected-warning {{idempotent operation; both operands are always equal in value}} - x |= x; // expected-warning {{idempotent operation; both operands are always equal in value}} + test(x & x); // expected-warning {{Both operands to '&' always have the same value}} + x &= x; // expected-warning {{Both operands to '&=' always have the same value}} + test(x | x); // expected-warning {{Both operands to '|' always have the same value}} + x |= x; // expected-warning {{Both operands to '|=' always have the same value}} // x op 1 - test(x * one); // expected-warning {{idempotent operation; the right operand is always 1}} - x *= one; // expected-warning {{idempotent operation; the right operand is always 1}} - test(x / one); // expected-warning {{idempotent operation; the right operand is always 1}} - x /= one; // expected-warning {{idempotent operation; the right operand is always 1}} + test(x * one); // expected-warning {{The right operand to '*' is always 1}} + x *= one; // expected-warning {{The right operand to '*=' is always 1}} + test(x / one); // expected-warning {{The right operand to '/' is always 1}} + x /= one; // expected-warning {{The right operand to '/=' is always 1}} // 1 op x - test(one * x); // expected-warning {{idempotent operation; the left operand is always 1}} + test(one * x); // expected-warning {{The left operand to '*' is always 1}} // x op 0 - test(x + zero); // expected-warning {{idempotent operation; the right operand is always 0}} - test(x - zero); // expected-warning {{idempotent operation; the right operand is always 0}} - test(x * zero); // expected-warning {{idempotent operation; the right operand is always 0}} - test(x & zero); // expected-warning {{idempotent operation; the right operand is always 0}} - test(x | zero); // expected-warning {{idempotent operation; the right operand is always 0}} - test(x ^ zero); // expected-warning {{idempotent operation; the right operand is always 0}} - test(x << zero); // expected-warning {{idempotent operation; the right operand is always 0}} - test(x >> zero); // expected-warning {{idempotent operation; the right operand is always 0}} + test(x + zero); // expected-warning {{The right operand to '+' is always 0}} + test(x - zero); // expected-warning {{The right operand to '-' is always 0}} + test(x * zero); // expected-warning {{The right operand to '*' is always 0}} + test(x & zero); // expected-warning {{The right operand to '&' is always 0}} + test(x | zero); // expected-warning {{The right operand to '|' is always 0}} + test(x ^ zero); // expected-warning {{The right operand to '^' is always 0}} + test(x << zero); // expected-warning {{The right operand to '<<' is always 0}} + test(x >> zero); // expected-warning {{The right operand to '>>' is always 0}} // 0 op x - test(zero + x); // expected-warning {{idempotent operation; the left operand is always 0}} - test(zero - x); // expected-warning {{idempotent operation; the left operand is always 0}} - test(zero / x); // expected-warning {{idempotent operation; the left operand is always 0}} - test(zero * x); // expected-warning {{idempotent operation; the left operand is always 0}} - test(zero & x); // expected-warning {{idempotent operation; the left operand is always 0}} - test(zero | x); // expected-warning {{idempotent operation; the left operand is always 0}} - test(zero ^ x); // expected-warning {{idempotent operation; the left operand is always 0}} - test(zero << x); // expected-warning {{idempotent operation; the left operand is always 0}} - test(zero >> x); // expected-warning {{idempotent operation; the left operand is always 0}} + test(zero + x); // expected-warning {{The left operand to '+' is always 0}} + test(zero - x); // expected-warning {{The left operand to '-' is always 0}} + test(zero / x); // expected-warning {{The left operand to '/' is always 0}} + test(zero * x); // expected-warning {{The left operand to '*' is always 0}} + test(zero & x); // expected-warning {{The left operand to '&' is always 0}} + test(zero | x); // expected-warning {{The left operand to '|' is always 0}} + test(zero ^ x); // expected-warning {{The left operand to '^' is always 0}} + test(zero << x); // expected-warning {{The left operand to '<<' is always 0}} + test(zero >> x); // expected-warning {{The left operand to '>>' is always 0}} + + // Overwrite the values so these aren't marked as Pseudoconstants + x = 1; + zero = 2; + one = 3; + + return x + zero + one; +} + +void floats(float x) { + test_f(x * 1.0); // no-warning + test_f(x * 1.0F); // no-warning +} + +// Ensure that we don't report false poitives in complex loops +void bailout() { + int unused = 0, result = 4; + result = result; // expected-warning {{Assigned value is always the same as the existing value}} + + for (unsigned bg = 0; bg < 1024; bg ++) { + result = bg * result; // no-warning + + for (int i = 0; i < 256; i++) { + unused *= i; // no-warning + } + } +} + +// Relaxed liveness - check that we don't kill liveness at assignments +typedef unsigned uintptr_t; +void kill_at_assign() { + short array[2]; + uintptr_t x = array; // expected-warning{{incompatible pointer to integer conversion}} + short *p = x; // expected-warning{{incompatible integer to pointer conversion}} + + // The following branch should be infeasible. + if (!(p = &array[0])) { // expected-warning{{Assigned value is always the same as the existing value}} + p = 0; + *p = 1; // no-warning + } +} + +// False positive tests + +unsigned false1() { + int a = 10; + return a * (5 - 2 - 3); // no-warning +} + +enum testenum { enum1 = 0, enum2 }; +unsigned false2() { + int a = 1234; + return enum1 + a; // no-warning +} + +// Self assignments of unused variables are common false positives +unsigned false3(int param, int param2) { + param = param; // no-warning + + // if a self assigned variable is used later, then it should be reported still + param2 = param2; // expected-warning{{Assigned value is always the same as the existing value}} + + unsigned nonparam = 5; + + nonparam = nonparam; // expected-warning{{Assigned value is always the same as the existing value}} + + return param2 + nonparam; +} + +// Pseudo-constants (vars only read) and constants should not be reported +unsigned false4() { + // Trivial constant + const int height = 1; + int c = 42; + test(height * c); // no-warning + + // Pseudo-constant (never changes after decl) + int width = height; + + return width * 10; // no-warning +} + +// Block pseudoconstants +void false4a() { + // Pseudo-constant + __block int a = 1; + int b = 10; + __block int c = 0; + b *= a; // no-warning + + ^{ + // Psuedoconstant block var + test(b * c); // no-warning + + // Non-pseudoconstant block var + int d = 0; + test(b * d); // expected-warning{{The right operand to '*' is always 0}} + d = 5; + test(d); + }(); + + test(a + b); +} + +// Static vars are common false positives +int false5() { + static int test = 0; + int a = 56; + a *= test; // no-warning + test++; + return a; +} + +// Non-local storage vars are considered false positives +int globalInt = 1; +int false6() { + int localInt = 23; + + localInt /= globalInt; + + return localInt; +} + +// Check that assignments filter out false positives correctly +int false7() { + int zero = 0; // psuedo-constant + int one = 1; + + int a = 55; + a = a; // expected-warning{{Assigned value is always the same as the existing value}} + a = enum1 * a; // no-warning + + int b = 123; + b = b; // no-warning + + return a; } diff --git a/test/Analysis/idempotent-operations.cpp b/test/Analysis/idempotent-operations.cpp new file mode 100644 index 0000000..c5d1ceb --- /dev/null +++ b/test/Analysis/idempotent-operations.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-constraints=range -fblocks -analyzer-opt-analyze-nested-blocks -analyzer-check-objc-mem -analyzer-check-idempotent-operations -verify %s + +// C++ specific false positives + +extern void test(int i); +extern void test_ref(int &i); + +// Test references affecting pseudoconstants +void false1() { + int a = 0; + int five = 5; + int &b = a; + test(five * a); // expected-warning {{The right operand to '*' is always 0}} + b = 4; +} diff --git a/test/Analysis/malloc.c b/test/Analysis/malloc.c index b4c1314..e443150 100644 --- a/test/Analysis/malloc.c +++ b/test/Analysis/malloc.c @@ -4,22 +4,136 @@ void *malloc(size_t); void free(void *); void *realloc(void *ptr, size_t size); void *calloc(size_t nmemb, size_t size); +void __attribute((ownership_returns(malloc))) *my_malloc(size_t); +void __attribute((ownership_takes(malloc, 1))) my_free(void *); +void __attribute((ownership_returns(malloc, 1))) *my_malloc2(size_t); +void __attribute((ownership_holds(malloc, 1))) my_hold(void *); + +// Duplicate attributes are silly, but not an error. +// Duplicate attribute has no extra effect. +// If two are of different kinds, that is an error and reported as such. +void __attribute((ownership_holds(malloc, 1))) +__attribute((ownership_holds(malloc, 1))) +__attribute((ownership_holds(malloc, 3))) my_hold2(void *, void *, void *); +void *my_malloc3(size_t); +void *myglobalpointer; +struct stuff { + void *somefield; +}; +struct stuff myglobalstuff; void f1() { int *p = malloc(12); return; // expected-warning{{Allocated memory never released. Potential memory leak.}} } -void f1_b() { - int *p = malloc(12); // expected-warning{{Allocated memory never released. Potential memory leak.}} -} - void f2() { int *p = malloc(12); free(p); free(p); // expected-warning{{Try to free a memory block that has been released}} } +// ownership attributes tests +void naf1() { + int *p = my_malloc3(12); + return; // no-warning +} + +void n2af1() { + int *p = my_malloc2(12); + return; // expected-warning{{Allocated memory never released. Potential memory leak.}} +} + +void af1() { + int *p = my_malloc(12); + return; // expected-warning{{Allocated memory never released. Potential memory leak.}} +} + +void af1_b() { + int *p = my_malloc(12); // expected-warning{{Allocated memory never released. Potential memory leak.}} +} + +void af1_c() { + myglobalpointer = my_malloc(12); // no-warning +} + +void af1_d() { + struct stuff mystuff; + mystuff.somefield = my_malloc(12); // expected-warning{{Allocated memory never released. Potential memory leak.}} +} + +// Test that we can pass out allocated memory via pointer-to-pointer. +void af1_e(void **pp) { + *pp = my_malloc(42); // no-warning +} + +void af1_f(struct stuff *somestuff) { + somestuff->somefield = my_malloc(12); // no-warning +} + +// Allocating memory for a field via multiple indirections to our arguments is OK. +void af1_g(struct stuff **pps) { + *pps = my_malloc(sizeof(struct stuff)); // no-warning + (*pps)->somefield = my_malloc(42); // no-warning +} + +void af2() { + int *p = my_malloc(12); + my_free(p); + free(p); // expected-warning{{Try to free a memory block that has been released}} +} + +void af2b() { + int *p = my_malloc(12); + free(p); + my_free(p); // expected-warning{{Try to free a memory block that has been released}} +} + +void af2c() { + int *p = my_malloc(12); + free(p); + my_hold(p); // expected-warning{{Try to free a memory block that has been released}} +} + +void af2d() { + int *p = my_malloc(12); + free(p); + my_hold2(0, 0, p); // expected-warning{{Try to free a memory block that has been released}} +} + +// No leak if malloc returns null. +void af2e() { + int *p = my_malloc(12); + if (!p) + return; // no-warning + free(p); // no-warning +} + +// This case would inflict a double-free elsewhere. +// However, this case is considered an analyzer bug since it causes false-positives. +void af3() { + int *p = my_malloc(12); + my_hold(p); + free(p); // no-warning +} + +// This case would inflict a double-free elsewhere. +// However, this case is considered an analyzer bug since it causes false-positives. +int * af4() { + int *p = my_malloc(12); + my_free(p); + return p; // no-warning +} + +// This case is (possibly) ok, be conservative +int * af5() { + int *p = my_malloc(12); + my_hold(p); + return p; // no-warning +} + + + // This case tests that storing malloc'ed memory to a static variable which is // then returned is not leaked. In the absence of known contracts for functions // or inter-procedural analysis, this is a conservative answer. @@ -117,7 +231,7 @@ char callocZeroesBad () { char *buf = calloc(2,2); char result = buf[3]; // no-warning if (buf[1] != 0) { - free(buf); + free(buf); // expected-warning{{never executed}} } return result; // expected-warning{{never released}} } diff --git a/test/Analysis/misc-ps-region-store.cpp b/test/Analysis/misc-ps-region-store.cpp index 6794d48..bfa5e5c 100644 --- a/test/Analysis/misc-ps-region-store.cpp +++ b/test/Analysis/misc-ps-region-store.cpp @@ -132,3 +132,30 @@ int TestHandleThis::null_deref_positive() { return 0; } +// PR 7675 - passing literals by-reference +void pr7675(const double &a); +void pr7675(const int &a); +void pr7675(const char &a); +void pr7675_i(const _Complex double &a); + +void pr7675_test() { + pr7675(10.0); + pr7675(10); + pr7675('c'); + pr7675_i(4.0i); + // Add null deref to ensure we are analyzing the code up to this point. + int *p = 0; + *p = 0xDEADBEEF; // expected-warning{{null pointer}} +} + +// <rdar://problem/8375510> - CFGBuilder should handle temporaries. +struct R8375510 { + R8375510(); + ~R8375510(); + R8375510 operator++(int); +}; + +int r8375510(R8375510 x, R8375510 y) { + for (; ; x++) { } +} + diff --git a/test/Analysis/misc-ps-region-store.m b/test/Analysis/misc-ps-region-store.m index 6b4f658..a4e0d0b 100644 --- a/test/Analysis/misc-ps-region-store.m +++ b/test/Analysis/misc-ps-region-store.m @@ -253,7 +253,7 @@ void rdar_7249327(unsigned int A[2*32]) { a = A; b = B; - n = *a++; + n = *a++; // expected-warning{{Assigned value is always the same as the existing value}} if (n) x += *b++; // no-warning } @@ -1041,3 +1041,102 @@ void pr_7450() { pr_7450_aux(p + 8); } +// <rdar://problem/8243408> - Symbolicate struct values returned by value. +struct s_rdar_8243408 { int x; }; +extern struct s_rdar_8243408 rdar_8243408_aux(void); +void rdar_8243408(void) { + struct s_rdar_8243408 a = { 1 }, *b = 0; + while (a.x && !b) + a = rdar_8243408_aux(); + + // Previously there was a false error here with 'b' being null. + (void) (a.x && b->x); // no-warning + + // Introduce a null deref to ensure we are checking this path. + int *p = 0; + *p = 0xDEADBEEF; // expected-warning{{Dereference of null pointer}} +} + +// <rdar://problem/8258814> +int r8258814() +{ + int foo; + int * a = &foo; + a[0] = 10; + // Do not warn that the value of 'foo' is uninitialized. + return foo; // no-warning +} + +// PR 8052 - Don't crash when reasoning about loads from a function address.\n +typedef unsigned int __uint32_t; +typedef unsigned long vm_offset_t; +typedef __uint32_t pd_entry_t; +typedef unsigned char u_char; +typedef unsigned int u_int; +typedef unsigned long u_long; +extern int bootMP_size; +void bootMP(void); +static void +pr8052(u_int boot_addr) +{ + int x; + int size = *(int *) ((u_long) & bootMP_size); + u_char *src = (u_char *) ((u_long) bootMP); + u_char *dst = (u_char *) boot_addr + ((vm_offset_t) ((((((((1 << +12) / (sizeof(pd_entry_t))) - 1) - 1) - (260 - 2))) << 22) | ((0) << 12))); + for (x = 0; + x < size; + ++x) + *dst++ = *src++; +} + +// PR 8015 - don't return undefined values for arrays when using a valid +// symbolic index +int pr8015_A(); +void pr8015_B(const char *); + +void pr8015_C() { + int number = pr8015_A(); + const char *numbers[] = { "zero" }; + if (number == 0) { + pr8015_B(numbers[number]); // no-warning + } +} + +// FIXME: This is a false positive due to not reasoning about symbolic +// array indices correctly. Discussion in PR 8015. +void pr8015_D_FIXME() { + int number = pr8015_A(); + const char *numbers[] = { "zero" }; + if (number == 0) { + if (numbers[number] == numbers[0]) + return; + int *p = 0; + *p = 0xDEADBEEF; // expected-warning{{Dereference of null pointer}} + } +} + +void pr8015_E() { + // Similar to pr8015_C, but number is allowed to be a valid range. + unsigned number = pr8015_A(); + const char *numbers[] = { "zero", "one", "two" }; + if (number < 3) { + pr8015_B(numbers[number]); // no-warning + } +} + +void pr8015_F_FIXME() { + // Similar to pr8015_E, but like pr8015_D we check if the pointer + // is the same as one of the string literals. The null dereference + // here is not feasible in practice, so this is a false positive. + int number = pr8015_A(); + const char *numbers[] = { "zero", "one", "two" }; + if (number < 3) { + const char *p = numbers[number]; + if (p == numbers[0] || p == numbers[1] || p == numbers[2]) + return; + int *q = 0; + *q = 0xDEADBEEF; // expected-warning{{Dereference of null pointer}} + } +} + diff --git a/test/Analysis/misc-ps.m b/test/Analysis/misc-ps.m index b1d47e2..4fbaa49 100644 --- a/test/Analysis/misc-ps.m +++ b/test/Analysis/misc-ps.m @@ -86,11 +86,11 @@ unsigned r6268365Aux(); void r6268365() { unsigned x = 0; - x &= r6268365Aux(); + x &= r6268365Aux(); // expected-warning{{The left operand to '&=' is always 0}} unsigned j = 0; if (x == 0) ++j; - if (x == 0) x = x / j; // no-warning + if (x == 0) x = x / j; // expected-warning{{Assigned value is always the same as the existing value}} expected-warning{{The right operand to '/' is always 1}} } void divzeroassume(unsigned x, unsigned j) { @@ -298,6 +298,7 @@ void rdar_6777209(char *p) { typedef void *Opcode; Opcode pr_4033_getOpcode(); void pr_4033(void) { + void *lbl = &&next_opcode; next_opcode: { Opcode op = pr_4033_getOpcode(); @@ -406,14 +407,14 @@ void test_trivial_symbolic_comparison(int *x) { int test_trivial_symbolic_comparison_aux(); int a = test_trivial_symbolic_comparison_aux(); int b = a; - if (a != b) { + if (a != b) { // expected-warning{{Both operands to '!=' always have the same value}} int *p = 0; *p = 0xDEADBEEF; // no-warning } a = a == 1; b = b == 1; - if (a != b) { + if (a != b) { // expected-warning{{Both operands to '!=' always have the same value}} int *p = 0; *p = 0xDEADBEEF; // no-warning } @@ -457,7 +458,7 @@ void rdar_7062158_2() { // ElementRegion is created. unsigned char test_array_index_bitwidth(const unsigned char *p) { unsigned short i = 0; - for (i = 0; i < 2; i++) p = &p[i]; + for (i = 0; i < 2; i++) p = &p[i]; return p[i+1]; } @@ -1020,3 +1021,50 @@ void pr7475_warn() { *someStatic = 0; // expected-warning{{null pointer}} } +// <rdar://problem/8202272> - __imag passed non-complex should not crash +float f0(_Complex float x) { + float l0 = __real x; + return __real l0 + __imag l0; +} + + +//===----------------------------------------------------------------------=== +// Test that we can reduce symbols to constants whether they are on the left +// or right side of an expression. +//===----------------------------------------------------------------------=== + +void reduce_to_constant(int x, int y) { + if (x != 20) + return; + + int a = x + y; + int b = y + x; + + if (y == -20 && a != 0) + (void)*(char*)0; // no-warning + if (y == -20 && b != 0) + (void)*(char*)0; // no-warning +} + +// <rdar://problem/8360854> - Test that code after a switch statement with no +// 'case:' labels is correctly evaluated. +void r8360854(int n) { + switch (n) { + default: ; + } + int *p = 0; + *p = 0xDEADBEEF; // expected-warning{{null pointer}} +} + +// PR 8050 - crash in CastSizeChecker when pointee is an incomplete type +typedef long unsigned int __darwin_size_t; +typedef __darwin_size_t size_t; +void *malloc(size_t); + +struct PR8050; + +void pr8050(struct PR8050 **arg) +{ + *arg = malloc(1); +} + diff --git a/test/Analysis/null-deref-ps.c b/test/Analysis/null-deref-ps.c index 7ca22ad..8daa845 100644 --- a/test/Analysis/null-deref-ps.c +++ b/test/Analysis/null-deref-ps.c @@ -64,13 +64,13 @@ int f4_b() { short array[2]; uintptr_t x = array; // expected-warning{{incompatible pointer to integer conversion}} short *p = x; // expected-warning{{incompatible integer to pointer conversion}} - + // The following branch should be infeasible. - if (!(p = &array[0])) { + if (!(p == &array[0])) { // expected-warning{{Both operands to '==' always have the same value}} p = 0; *p = 1; // no-warning } - + if (p) { *p = 5; // no-warning p = 0; @@ -81,7 +81,6 @@ int f4_b() { return 0; } - int f5() { char *s = "hello world"; @@ -280,7 +279,7 @@ void f12(HF12ITEM i, char *q) { // Test handling of translating between integer "pointers" and back. void f13() { int *x = 0; - if (((((int) x) << 2) + 1) >> 1) *x = 1; // no-warning + if (((((int) x) << 2) + 1) >> 1) *x = 1; } // PR 4759 - Attribute non-null checking by the analyzer was not correctly diff --git a/test/Analysis/outofbound.c b/test/Analysis/outofbound.c index 9b48730..ed51dc6 100644 --- a/test/Analysis/outofbound.c +++ b/test/Analysis/outofbound.c @@ -71,3 +71,27 @@ void sizeof_vla(int a) { y[5] = 5; // expected-warning{{out-of-bound}} } } + +void alloca_region(int a) { + if (a == 5) { + char *x = __builtin_alloca(a); + x[4] = 4; // no-warning + x[5] = 5; // expected-warning{{out-of-bound}} + } +} + +int symbolic_index(int a) { + int x[2] = {1, 2}; + if (a == 2) { + return x[a]; // expected-warning{{out-of-bound}} + } + return 0; +} + +int symbolic_index2(int a) { + int x[2] = {1, 2}; + if (a < 0) { + return x[a]; // expected-warning{{out-of-bound}} + } + return 0; +} diff --git a/test/Analysis/plist-output.m b/test/Analysis/plist-output.m index aa866de..95faa06 100644 --- a/test/Analysis/plist-output.m +++ b/test/Analysis/plist-output.m @@ -205,7 +205,7 @@ void test_null_field(void) { // CHECK: </dict> // CHECK: <dict> // CHECK: <key>line</key><integer>10</integer> -// CHECK: <key>col</key><integer>3</integer> +// CHECK: <key>col</key><integer>7</integer> // CHECK: <key>file</key><integer>0</integer> // CHECK: </dict> // CHECK: </array> diff --git a/test/Analysis/retain-release-region-store.m b/test/Analysis/retain-release-region-store.m index db49b91..7b98554 100644 --- a/test/Analysis/retain-release-region-store.m +++ b/test/Analysis/retain-release-region-store.m @@ -50,6 +50,7 @@ typedef struct _NSZone NSZone; @end @protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end @interface NSObject <NSObject> {} +- (id)init; + (id)allocWithZone:(NSZone *)zone; + (id)alloc; - (void)dealloc; @@ -223,3 +224,29 @@ void pr6699(int x) { } } +// <rdar://problem/8261992> Idempotent operation checker false positive with ObjC ivars +@interface R8261992 : NSObject { + @package int myIvar; +} +@end + +static void R8261992_ChangeMyIvar(R8261992 *tc) { + tc->myIvar = 5; +} + +void R8261992_test(R8261992 *tc) { + int temp = tc->myIvar; + // The ivar binding for tc->myIvar gets invalidated. + R8261992_ChangeMyIvar(tc); + tc->myIvar = temp; // no-warning + tc = [[R8261992 alloc] init]; + temp = tc->myIvar; // no-warning + // The ivar binding for tc->myIvar gets invalidated. + R8261992_ChangeMyIvar(tc); + tc->myIvar = temp; + [tc release]; // no-warning + // did we analyze this? + int *p = 0x0; + *p = 0xDEADBEEF; // expected-warning{{null}} +} + diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m index c9c7d27..064165a 100644 --- a/test/Analysis/retain-release.m +++ b/test/Analysis/retain-release.m @@ -1358,3 +1358,12 @@ void test_blocks_1_indirect_retain_via_call(void) { } @end +// <rdar://problem/8272168> - Correcly handle Class<...> in Cocoa Conventions +// detector. + +@protocol Prot_R8272168 @end +Class <Prot_R8272168> GetAClassThatImplementsProt_R8272168(); +void r8272168() { + GetAClassThatImplementsProt_R8272168(); +} + diff --git a/test/Analysis/stack-addr-ps.cpp b/test/Analysis/stack-addr-ps.cpp new file mode 100644 index 0000000..593ba1d --- /dev/null +++ b/test/Analysis/stack-addr-ps.cpp @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -fsyntax-only -fblocks -verify %s + +// FIXME: Only the stack-address checking in Sema catches this right now, and +// the stack analyzer doesn't handle the ImplicitCastExpr (lvalue). +const int& g() { + int s; + return s; // expected-warning{{reference to stack memory associated with local variable 's' returned}} +} diff --git a/test/Analysis/stream.c b/test/Analysis/stream.c index 2b6a903..73bbc13 100644 --- a/test/Analysis/stream.c +++ b/test/Analysis/stream.c @@ -6,6 +6,8 @@ typedef struct _IO_FILE FILE; #define SEEK_CUR 1 /* Seek from current position. */ #define SEEK_END 2 /* Seek from end of file. */ extern FILE *fopen(const char *path, const char *mode); +extern FILE *tmpfile(void); +extern int fclose(FILE *fp); extern size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); extern int fseek (FILE *__stream, long int __off, int __whence); extern long int ftell (FILE *__stream); @@ -15,21 +17,25 @@ void f1(void) { FILE *p = fopen("foo", "r"); char buf[1024]; fread(buf, 1, 1, p); // expected-warning {{Stream pointer might be NULL.}} + fclose(p); } void f2(void) { FILE *p = fopen("foo", "r"); fseek(p, 1, SEEK_SET); // expected-warning {{Stream pointer might be NULL.}} + fclose(p); } void f3(void) { FILE *p = fopen("foo", "r"); ftell(p); // expected-warning {{Stream pointer might be NULL.}} + fclose(p); } void f4(void) { FILE *p = fopen("foo", "r"); rewind(p); // expected-warning {{Stream pointer might be NULL.}} + fclose(p); } void f5(void) { @@ -38,4 +44,36 @@ void f5(void) { return; fseek(p, 1, SEEK_SET); // no-warning fseek(p, 1, 3); // expected-warning {{The whence argument to fseek() should be SEEK_SET, SEEK_END, or SEEK_CUR.}} + fclose(p); +} + +void f6(void) { + FILE *p = fopen("foo", "r"); + fclose(p); + fclose(p); // expected-warning {{Try to close a file Descriptor already closed. Cause undefined behaviour.}} +} + +void f7(void) { + FILE *p = tmpfile(); + ftell(p); // expected-warning {{Stream pointer might be NULL.}} + fclose(p); +} + +void f8(int c) { + FILE *p = fopen("foo.c", "r"); + if(c) + return; // expected-warning {{Opened File never closed. Potential Resource leak.}} + fclose(p); +} + +FILE *f9(void) { + FILE *p = fopen("foo.c", "r"); + if (p) + return p; // no-warning + else + return 0; +} + +void pr7831(FILE *fp) { + fclose(fp); // no-warning } diff --git a/test/Analysis/string.c b/test/Analysis/string.c new file mode 100644 index 0000000..35ed710 --- /dev/null +++ b/test/Analysis/string.c @@ -0,0 +1,240 @@ +// RUN: %clang_cc1 -analyze -analyzer-experimental-internal-checks -analyzer-check-objc-mem -analyzer-store=region -analyzer-experimental-checks -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-experimental-internal-checks -analyzer-check-objc-mem -analyzer-store=region -analyzer-experimental-checks -verify %s +// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-experimental-internal-checks -analyzer-check-objc-mem -analyzer-store=region -analyzer-experimental-checks -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-experimental-internal-checks -analyzer-check-objc-mem -analyzer-store=region -analyzer-experimental-checks -verify %s + +//===----------------------------------------------------------------------=== +// Declarations +//===----------------------------------------------------------------------=== + +// Some functions are so similar to each other that they follow the same code +// path, such as memcpy and __memcpy_chk, or memcmp and bcmp. If VARIANT is +// defined, make sure to use the variants instead to make sure they are still +// checked by the analyzer. + +// Some functions are implemented as builtins. These should be #defined as +// BUILTIN(f), which will prepend "__builtin_" if USE_BUILTINS is defined. + +// Functions that have variants and are also availabe as builtins should be +// declared carefully! See memcpy() for an example. + +#ifdef USE_BUILTINS +# define BUILTIN(f) __builtin_ ## f +#else /* USE_BUILTINS */ +# define BUILTIN(f) f +#endif /* USE_BUILTINS */ + +#define NULL 0 +typedef typeof(sizeof(int)) size_t; + +//===----------------------------------------------------------------------=== +// strlen() +//===----------------------------------------------------------------------=== + +#define strlen BUILTIN(strlen) +size_t strlen(const char *s); + +void strlen_constant0() { + if (strlen("123") != 3) + (void)*(char*)0; // no-warning +} + +void strlen_constant1() { + const char *a = "123"; + if (strlen(a) != 3) + (void)*(char*)0; // no-warning +} + +void strlen_constant2(char x) { + char a[] = "123"; + if (strlen(a) != 3) + (void)*(char*)0; // no-warning + a[0] = x; + if (strlen(a) != 3) + (void)*(char*)0; // expected-warning{{null}} +} + +size_t strlen_null() { + return strlen(0); // expected-warning{{Null pointer argument in call to byte string function}} +} + +size_t strlen_fn() { + return strlen((char*)&strlen_fn); // expected-warning{{Argument to byte string function is the address of the function 'strlen_fn', which is not a null-terminated string}} +} + +size_t strlen_nonloc() { +label: + return strlen((char*)&&label); // expected-warning{{Argument to byte string function is the address of the label 'label', which is not a null-terminated string}} +} + +void strlen_subregion() { + struct two_strings { char a[2], b[2] }; + extern void use_two_strings(struct two_strings *); + + struct two_strings z; + use_two_strings(&z); + + size_t a = strlen(z.a); + z.b[0] = 5; + size_t b = strlen(z.a); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + use_two_strings(&z); + + size_t c = strlen(z.a); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +extern void use_string(char *); +void strlen_argument(char *x) { + size_t a = strlen(x); + size_t b = strlen(x); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + use_string(x); + + size_t c = strlen(x); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +extern char global_str[]; +void strlen_global() { + size_t a = strlen(global_str); + size_t b = strlen(global_str); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + // Call a function with unknown effects, which should invalidate globals. + use_string(0); + + size_t c = strlen(global_str); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +void strlen_indirect(char *x) { + size_t a = strlen(x); + char *p = x; + char **p2 = &p; + size_t b = strlen(x); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + extern void use_string_ptr(char*const*); + use_string_ptr(p2); + + size_t c = strlen(x); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +void strlen_liveness(const char *x) { + if (strlen(x) < 5) + return; + if (strlen(x) < 5) + (void)*(char*)0; // no-warning +} + +//===----------------------------------------------------------------------=== +// strcpy() +//===----------------------------------------------------------------------=== + +#ifdef VARIANT + +#define __strcpy_chk BUILTIN(__strcpy_chk) +char *__strcpy_chk(char *restrict s1, const char *restrict s2, size_t destlen); + +#define strcpy(a,b) __strcpy_chk(a,b,(size_t)-1) + +#else /* VARIANT */ + +#define strcpy BUILTIN(strcpy) +char *strcpy(char *restrict s1, const char *restrict s2); + +#endif /* VARIANT */ + + +void strcpy_null_dst(char *x) { + strcpy(NULL, x); // expected-warning{{Null pointer argument in call to byte string function}} +} + +void strcpy_null_src(char *x) { + strcpy(x, NULL); // expected-warning{{Null pointer argument in call to byte string function}} +} + +void strcpy_fn(char *x) { + strcpy(x, (char*)&strcpy_fn); // expected-warning{{Argument to byte string function is the address of the function 'strcpy_fn', which is not a null-terminated string}} +} + +void strcpy_effects(char *x, char *y) { + char a = x[0]; + + if (strcpy(x, y) != x) + (void)*(char*)0; // no-warning + + if (strlen(x) != strlen(y)) + (void)*(char*)0; // no-warning + + if (a != x[0]) + (void)*(char*)0; // expected-warning{{null}} +} + +void strcpy_overflow(char *y) { + char x[4]; + if (strlen(y) == 4) + strcpy(x, y); // expected-warning{{Byte string function overflows destination buffer}} +} + +void strcpy_no_overflow(char *y) { + char x[4]; + if (strlen(y) == 3) + strcpy(x, y); // no-warning +} + +//===----------------------------------------------------------------------=== +// stpcpy() +//===----------------------------------------------------------------------=== + +#ifdef VARIANT + +#define __stpcpy_chk BUILTIN(__stpcpy_chk) +char *__stpcpy_chk(char *restrict s1, const char *restrict s2, size_t destlen); + +#define stpcpy(a,b) __stpcpy_chk(a,b,(size_t)-1) + +#else /* VARIANT */ + +#define stpcpy BUILTIN(stpcpy) +char *stpcpy(char *restrict s1, const char *restrict s2); + +#endif /* VARIANT */ + + +void stpcpy_effect(char *x, char *y) { + char a = x[0]; + + if (stpcpy(x, y) != &x[strlen(y)]) + (void)*(char*)0; // no-warning + + if (strlen(x) != strlen(y)) + (void)*(char*)0; // no-warning + + if (a != x[0]) + (void)*(char*)0; // expected-warning{{null}} +} + +void stpcpy_overflow(char *y) { + char x[4]; + if (strlen(y) == 4) + stpcpy(x, y); // expected-warning{{Byte string function overflows destination buffer}} +} + +void stpcpy_no_overflow(char *y) { + char x[4]; + if (strlen(y) == 3) + stpcpy(x, y); // no-warning +} diff --git a/test/Analysis/uninit-vals-ps-region.m b/test/Analysis/uninit-vals-ps-region.m index 69c1ecd..751e91b 100644 --- a/test/Analysis/uninit-vals-ps-region.m +++ b/test/Analysis/uninit-vals-ps-region.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -verify %s +// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -analyzer-check-idempotent-operations -verify %s struct s { int data; @@ -42,7 +42,7 @@ void test_uninit_pos_3() { void test_uninit_neg() { struct TestUninit v1 = { 0, 0 }; struct TestUninit v2 = test_uninit_aux(); - test_unit_aux2(v2.x + v1.y); // no-warning + test_unit_aux2(v2.x + v1.y); // expected-warning{{The right operand to '+' is always 0}} } extern void test_uninit_struct_arg_aux(struct TestUninit arg); diff --git a/test/Analysis/unreachable-code-path.c b/test/Analysis/unreachable-code-path.c new file mode 100644 index 0000000..0715327 --- /dev/null +++ b/test/Analysis/unreachable-code-path.c @@ -0,0 +1,104 @@ +// RUN: %clang_cc1 -analyze -analyzer-experimental-checks -analyzer-check-objc-mem -analyzer-check-dead-stores -verify -analyzer-opt-analyze-nested-blocks %s + +extern void foo(int a); + +// The first few tests are non-path specific - we should be able to find them + +void test(unsigned a) { + switch (a) { + a += 5; // expected-warning{{never executed}} + case 2: + a *= 10; + case 3: + a %= 2; + } + foo(a); +} + +void test2(unsigned a) { + help: + if (a > 0) + return; + if (a == 0) + return; + foo(a); // expected-warning{{never executed}} + goto help; +} + +void test3(unsigned a) { + while(1); + if (a > 5) { // expected-warning{{never executed}} + return; + } +} + +// These next tests are path-sensitive + +void test4() { + int a = 5; + + while (a > 1) + a -= 2; + + if (a > 1) { + a = a + 56; // expected-warning{{never executed}} + } + + foo(a); +} + +extern void bar(char c); + +void test5(const char *c) { + foo(c[0]); + + if (!c) { + bar(1); // expected-warning{{never executed}} + } +} + +// These next tests are false positives and should not generate warnings + +void test6(const char *c) { + if (c) return; + if (!c) return; + __builtin_unreachable(); // no-warning +} + +// Compile-time constant false positives +#define CONSTANT 0 +enum test_enum { Off, On }; +void test7() { + if (CONSTANT) + return; // no-warning + + if (sizeof(int)) + return; // no-warning + + if (Off) + return; // no-warning +} + +void test8() { + static unsigned a = 0; + + if (a) + a = 123; // no-warning + + a = 5; +} + +// Check for bugs where multiple statements are reported +void test9(unsigned a) { + switch (a) { + if (a) // expected-warning{{never executed}} + foo(a + 5); // no-warning + else // no-warning + foo(a); // no-warning + case 1: + case 2: + break; + default: + break; + } +} |