pgousseau updated this revision to Diff 33402. pgousseau added a comment. Replace 'dyn_cast' by 'cast' following Devin's review.
If all looks good could someone commit this patch for me ? Regards, Pierre http://reviews.llvm.org/D11832 Files: include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h lib/StaticAnalyzer/Checkers/CStringChecker.cpp lib/StaticAnalyzer/Core/RegionStore.cpp test/Analysis/pr22954.c
Index: test/Analysis/pr22954.c =================================================================== --- /dev/null +++ test/Analysis/pr22954.c @@ -0,0 +1,697 @@ +// Given code 'struct aa { char s1[4]; char * s2;} a; memcpy(a.s1, ...);', +// this test checks that the CStringChecker only invalidates the destination buffer array a.s1 (instead of a.s1 and a.s2). +// At the moment the whole of the destination array content is invalidated. +// If a.s1 region has a symbolic offset, the whole region of 'a' is invalidated. +// Specific triple set to test structures of size 0. +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-store=region -verify %s + +typedef __typeof(sizeof(int)) size_t; + +char *strdup(const char *s); +void free(void *); +void *memcpy(void *dst, const void *src, size_t n); // expected-note{{passing argument to parameter 'dst' here}} +void *malloc(size_t n); + +void clang_analyzer_eval(int); + +struct aa { + char s1[4]; + char *s2; +}; + +// Test different types of structure initialisation. +int f0() { + struct aa a0 = {{1, 2, 3, 4}, 0}; + a0.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(a0.s1, input, 4); + clang_analyzer_eval(a0.s1[0] == 'a'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a0.s1[1] == 'b'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a0.s1[2] == 'c'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a0.s1[3] == 'd'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a0.s2 == 0); // expected-warning{{UNKNOWN}} + free(a0.s2); // no warning + return 0; +} + +int f1() { + struct aa a1; + a1.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(a1.s1, input, 4); + clang_analyzer_eval(a1.s1[0] == 'a'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a1.s1[1] == 'b'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a1.s1[2] == 'c'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a1.s1[3] == 'd'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a1.s2 == 0); // expected-warning{{UNKNOWN}} + free(a1.s2); // no warning + return 0; +} + +int f2() { + struct aa a2 = {{1, 2}}; + a2.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(a2.s1, input, 4); + clang_analyzer_eval(a2.s1[0] == 'a'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a2.s1[1] == 'b'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a2.s1[2] == 'c'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a2.s1[3] == 'd'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a2.s2 == 0); // expected-warning{{UNKNOWN}} + free(a2.s2); // no warning + return 0; +} + +int f3() { + struct aa a3 = {{1, 2, 3, 4}, 0}; + a3.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + int * dest = (int*)a3.s1; + memcpy(dest, input, 4); + clang_analyzer_eval(a3.s1[0] == 'a'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(dest[0] == 'a'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a3.s1[1] == 'b'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(dest[1] == 'b'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a3.s1[2] == 'c'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(dest[2] == 'c'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a3.s1[3] == 'd'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(dest[3] == 'd'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a3.s2 == 0); // expected-warning{{UNKNOWN}} + free(a3.s2); // no warning + return 0; +} + +struct bb { + struct aa a; + char * s2; +}; + +int f4() { + struct bb b0 = {{1, 2, 3, 4}, 0}; + b0.s2 = strdup("hello"); + b0.a.s2 = strdup("hola"); + char input[] = {'a', 'b', 'c', 'd'}; + char * dest = (char*)(b0.a.s1); + memcpy(dest, input, 4); + clang_analyzer_eval(b0.a.s1[0] == 'a'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(dest[0] == 'a'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(b0.a.s1[1] == 'b'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(dest[1] == 'b'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(b0.a.s1[2] == 'c'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(dest[2] == 'c'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(b0.a.s1[3] == 'd'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(dest[3] == 'd'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(b0.s2 == 0); // expected-warning{{UNKNOWN}} + free(b0.a.s2); // no warning + free(b0.s2); // no warning + return 0; +} + +// Test that memory leaks are caught. +int f5() { + struct aa a0 = {{1, 2, 3, 4}, 0}; + a0.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(a0.s1, input, 4); + return 0; // expected-warning{{Potential leak of memory pointed to by 'a0.s2'}} +} + +int f6() { + struct aa a1; + a1.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(a1.s1, input, 4); + return 0; // expected-warning{{Potential leak of memory pointed to by 'a1.s2'}} +} + +int f7() { + struct aa a2 = {{1, 2}}; + a2.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(a2.s1, input, 4); + return 0; // expected-warning{{Potential leak of memory pointed to by 'a2.s2'}} +} + +int f8() { + struct aa a3 = {{1, 2, 3, 4}, 0}; + a3.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + int * dest = (int*)a3.s1; + memcpy(dest, input, 4); + return 0; // expected-warning{{Potential leak of memory pointed to by 'a3.s2'}} +} + +int f9() { + struct bb b0 = {{1, 2, 3, 4}, 0}; + b0.s2 = strdup("hello"); + b0.a.s2 = strdup("hola"); + char input[] = {'a', 'b', 'c', 'd'}; + char * dest = (char*)(b0.a.s1); + memcpy(dest, input, 4); + free(b0.a.s2); // expected-warning{{Potential leak of memory pointed to by 'b0.s2'}} + return 0; +} + +int f10() { + struct bb b0 = {{1, 2, 3, 4}, 0}; + b0.s2 = strdup("hello"); + b0.a.s2 = strdup("hola"); + char input[] = {'a', 'b', 'c', 'd'}; + char * dest = (char*)(b0.a.s1); + memcpy(dest, input, 4); + free(b0.s2); // expected-warning{{Potential leak of memory pointed to by 'b0.a.s2'}} + return 0; +} + +// Test invalidating fields being addresses of array. +struct cc { + char * s1; + char * s2; +}; + +int f11() { + char x[4] = {1, 2}; + x[0] = 1; + x[1] = 2; + struct cc c0; + c0.s2 = strdup("hello"); + c0.s1 = &x[0]; + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(c0.s1, input, 4); + clang_analyzer_eval(x[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(x[1] == 2); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(c0.s1[0] == 'a'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(c0.s1[1] == 'b'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(c0.s1[2] == 'c'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(c0.s1[3] == 'd'); // expected-warning{{UNKNOWN}} + free(c0.s2); // no-warning + return 0; +} + +// Test inverting field position between s1 and s2. +struct dd { + char *s2; + char s1[4]; +}; + +int f12() { + struct dd d0 = {0, {1, 2, 3, 4}}; + d0.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(d0.s1, input, 4); + clang_analyzer_eval(d0.s1[0] == 'a'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(d0.s1[1] == 'b'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(d0.s1[2] == 'c'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(d0.s1[3] == 'd'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(d0.s2 == 0); // expected-warning{{UNKNOWN}} + free(d0.s2); // no warning + return 0; +} + +// Test arrays of structs. +struct ee { + int a; + char b; +}; + +struct EE { + struct ee s1[2]; + char * s2; +}; + +int f13() { + struct EE E0 = {{{1, 2}, {3, 4}}, 0}; + E0.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(E0.s1, input, 4); + clang_analyzer_eval(E0.s1[0].a == 'a'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(E0.s1[0].b == 'b'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(E0.s1[1].a == 'c'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(E0.s1[1].b == 'd'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(E0.s2 == 0); // expected-warning{{UNKNOWN}} + free(E0.s2); // no warning + return 0; +} + +// Test global parameters. +struct aa a15 = {{1, 2, 3, 4}, 0}; + +int f15() { + a15.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(a15.s1, input, 4); + clang_analyzer_eval(a15.s1[0] == 'a'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a15.s1[1] == 'b'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a15.s1[2] == 'c'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a15.s1[3] == 'd'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a15.s2 == 0); // expected-warning{{UNKNOWN}} + free(a15.s2); // no warning + return 0; +} + +// Test array of 0 sized elements. +struct empty {}; +struct gg { + struct empty s1[4]; + char * s2; +}; + +int f16() { + struct gg g0 = {{}, 0}; + g0.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(g0.s1, input, 4); + clang_analyzer_eval(*(int*)(&g0.s1[0]) == 'a'); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by 'g0.s2'}} + clang_analyzer_eval(*(int*)(&g0.s1[1]) == 'b'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(*(int*)(&g0.s1[2]) == 'c'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(*(int*)(&g0.s1[3]) == 'd'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(g0.s2 == 0); // expected-warning{{UNKNOWN}} + free(g0.s2); // no warning + return 0; +} + +// Test array of 0 elements. +struct hh { + char s1[0]; + char * s2; +}; + +int f17() { + struct hh h0; + h0.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(h0.s1, input, 4); + clang_analyzer_eval(h0.s1[0] == 'a'); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by 'h0.s2'}} + clang_analyzer_eval(h0.s2 == 0); // expected-warning{{UNKNOWN}} + free(h0.s2); // no warning + return 0; +} + +// Test writing past the array. +struct ii { + char s1[4]; + int i; + int j; + char * s2; +}; + +int f18() { + struct ii i18 = {{1, 2, 3, 4}, 5, 6}; + i18.i = 10; + i18.j = 11; + i18.s2 = strdup("hello"); + char input[100] = {3}; + memcpy(i18.s1, input, 100); + clang_analyzer_eval(i18.s1[0] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by 'i18.s2'}} + clang_analyzer_eval(i18.s1[1] == 2); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(i18.s1[2] == 3); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(i18.s1[3] == 4); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(i18.i == 10); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(i18.j == 11); // expected-warning{{UNKNOWN}} + return 0; +} + +int f181() { + struct ii i181 = {{1, 2, 3, 4}, 5, 6}; + i181.i = 10; + i181.j = 11; + i181.s2 = strdup("hello"); + char input[100] = {3}; + memcpy(i181.s1, input, 5); // invalidate the whole region of i181 + clang_analyzer_eval(i181.s1[0] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by 'i181.s2'}} + clang_analyzer_eval(i181.s1[1] == 2); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(i181.s1[2] == 3); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(i181.s1[3] == 4); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(i181.i == 10); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(i181.j == 11); // expected-warning{{UNKNOWN}} + return 0; +} + +// Test array with a symbolic offset. +struct jj { + char s1[2]; + char * s2; +}; + +struct JJ { + struct jj s1[3]; + char * s2; +}; + +int f19(int i) { + struct JJ J0 = {{{1, 2, 0}, {3, 4, 0}, {5, 6, 0}}, 0}; + J0.s2 = strdup("hello"); + J0.s1[0].s2 = strdup("hello"); + J0.s1[1].s2 = strdup("hi"); + J0.s1[2].s2 = strdup("world"); + char input[2] = {'a', 'b'}; + memcpy(J0.s1[i].s1, input, 2); + clang_analyzer_eval(J0.s1[0].s1[0] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by field 's2'}}\ + expected-warning{{Potential leak of memory pointed to by 'J0.s2'}} + clang_analyzer_eval(J0.s1[0].s1[1] == 2); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(J0.s1[1].s1[0] == 3); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(J0.s1[1].s1[1] == 4); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(J0.s1[2].s1[0] == 5); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(J0.s1[2].s1[1] == 6); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(J0.s1[i].s1[0] == 5); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(J0.s1[i].s1[1] == 6); // expected-warning{{UNKNOWN}} + // FIXME: memory leak warning for J0.s2 should be emitted here instead of after memcpy call. + return 0; // no warning +} + +// Test array with its super region having symbolic offseted regions. +int f20(int i) { + struct aa * a20 = malloc(sizeof(struct aa) * 2); + a20[0].s1[0] = 1; + a20[0].s1[1] = 2; + a20[0].s1[2] = 3; + a20[0].s1[3] = 4; + a20[0].s2 = strdup("hello"); + a20[1].s1[0] = 5; + a20[1].s1[1] = 6; + a20[1].s1[2] = 7; + a20[1].s1[3] = 8; + a20[1].s2 = strdup("world"); + a20[i].s2 = strdup("hola"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(a20[0].s1, input, 4); + clang_analyzer_eval(a20[0].s1[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[0].s1[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[0].s1[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[0].s1[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[0].s2 == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[1].s1[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[1].s1[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[1].s1[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[1].s1[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[1].s2 == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[i].s1[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[i].s1[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[i].s1[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[i].s1[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a20[i].s2 == 0); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by 'a20'}} + + return 0; +} + +// Test array's region and super region both having symbolic offsets. +int f21(int i) { + struct aa * a21 = malloc(sizeof(struct aa) * 2); + a21[0].s1[0] = 1; + a21[0].s1[1] = 2; + a21[0].s1[2] = 3; + a21[0].s1[3] = 4; + a21[0].s2 = 0; + a21[1].s1[0] = 5; + a21[1].s1[1] = 6; + a21[1].s1[2] = 7; + a21[1].s1[3] = 8; + a21[1].s2 = 0; + a21[i].s2 = strdup("hello"); + a21[i].s1[0] = 1; + a21[i].s1[1] = 2; + a21[i].s1[2] = 3; + a21[i].s1[3] = 4; + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(a21[i].s1, input, 4); + clang_analyzer_eval(a21[0].s1[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[0].s1[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[0].s1[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[0].s1[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[0].s2 == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[1].s1[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[1].s1[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[1].s1[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[1].s1[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[1].s2 == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[i].s1[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[i].s1[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[i].s1[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[i].s1[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a21[i].s2 == 0); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by 'a21'}} + + return 0; +} + +// Test regions aliasing other regions. +struct ll { + char s1[4]; + char * s2; +}; + +struct mm { + char s3[4]; + char * s4; +}; + +int f24() { + struct ll l24 = {{1, 2, 3, 4}, 0}; + struct mm * m24 = (struct mm *)&l24; + m24->s4 = strdup("hello"); + char input[] = {1, 2, 3, 4}; + memcpy(m24->s3, input, 4); + clang_analyzer_eval(m24->s3[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m24->s3[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m24->s3[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m24->s3[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l24.s1[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l24.s1[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l24.s1[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l24.s1[3] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by field 's4'}} + return 0; +} + +// Test region with potential aliasing and symbolic offsets. +// Store assumes no aliasing. +int f25(int i, int j, struct ll * l, struct mm * m) { + m->s4 = strdup("hola"); // m->s4 not tracked + m->s3[0] = 1; + m->s3[1] = 2; + m->s3[2] = 3; + m->s3[3] = 4; + m->s3[j] = 5; // invalidates m->s3 + l->s2 = strdup("hello"); // l->s2 not tracked + l->s1[0] = 6; + l->s1[1] = 7; + l->s1[2] = 8; + l->s1[3] = 9; + l->s1[i] = 10; // invalidates l->s1 + char input[] = {1, 2, 3, 4}; + memcpy(m->s3, input, 4); // does not invalidate l->s1[i] + clang_analyzer_eval(m->s3[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m->s3[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m->s3[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m->s3[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m->s3[i] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m->s3[j] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l->s1[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l->s1[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l->s1[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l->s1[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l->s1[i] == 1); // expected-warning{{FALSE}} + clang_analyzer_eval(l->s1[j] == 1); // expected-warning{{UNKNOWN}} + return 0; +} + +// Test size with symbolic size argument. +int f26(int i) { + struct aa a26 = {{1, 2, 3, 4}, 0}; + a26.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(a26.s1, input, i); // i assumed in bound + clang_analyzer_eval(a26.s1[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a26.s1[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a26.s1[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a26.s1[3] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by 'a26.s2'}} + return 0; +} + +// Test sizeof as a size argument. +int f261() { + struct aa a261 = {{1, 2, 3, 4}, 0}; + a261.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(a261.s1, input, sizeof(a261.s1)); + clang_analyzer_eval(a261.s1[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a261.s1[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a261.s1[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a261.s1[3] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by 'a261.s2'}} + return 0; +} + +// Test negative size argument. +int f262() { + struct aa a262 = {{1, 2, 3, 4}, 0}; + a262.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(a262.s1, input, -1); + clang_analyzer_eval(a262.s1[0] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by 'a262.s2'}} + clang_analyzer_eval(a262.s1[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a262.s1[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a262.s1[3] == 1); // expected-warning{{UNKNOWN}} + return 0; +} + +// Test casting regions with symbolic offseted sub regions. +int f27(int i) { + struct mm m27 = {{1, 2, 3, 4}, 0}; + m27.s4 = strdup("hello"); + m27.s3[i] = 5; + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(((struct ll*)(&m27))->s1, input, 4); + clang_analyzer_eval(m27.s3[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m27.s3[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m27.s3[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m27.s3[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m27.s3[i] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by 'm27.s4'}} + return 0; +} + +int f28(int i, int j, int k, int l) { + struct mm m28[2]; + m28[i].s4 = strdup("hello"); + m28[j].s3[k] = 1; + struct ll * l28 = (struct ll*)(&m28[1]); + l28->s1[l] = 2; + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(l28->s1, input, 4); + clang_analyzer_eval(m28[0].s3[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m28[0].s3[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m28[0].s3[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m28[0].s3[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m28[1].s3[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m28[1].s3[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m28[1].s3[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m28[1].s3[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m28[i].s3[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m28[i].s3[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m28[i].s3[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m28[i].s3[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m28[j].s3[k] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(l28->s1[l] == 2); // expected-warning{{UNKNOWN}} + return 0; +} + +int f29(int i, int j, int k, int l, int m) { + struct mm m29[2]; + m29[i].s4 = strdup("hello"); + m29[j].s3[k] = 1; + struct ll * l29 = (struct ll*)(&m29[l]); + l29->s1[m] = 2; + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(l29->s1, input, 4); + clang_analyzer_eval(m29[0].s3[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m29[0].s3[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m29[0].s3[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m29[0].s3[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m29[1].s3[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m29[1].s3[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m29[1].s3[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m29[1].s3[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m29[i].s3[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m29[i].s3[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m29[i].s3[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m29[i].s3[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(m29[j].s3[k] == 1); // expected-warning{{TRUE}}\ + expected-warning{{Potential leak of memory pointed to by field 's4'}} + clang_analyzer_eval(l29->s1[m] == 2); // expected-warning{{UNKNOWN}} + return 0; +} + +// Test unions' fields. +union uu { + char x; + char s1[4]; +}; + +int f30() { + union uu u30 = { .s1 = {1, 2, 3, 4}}; + char input[] = {1, 2, 3, 4}; + memcpy(u30.s1, input, 4); + clang_analyzer_eval(u30.s1[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(u30.s1[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(u30.s1[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(u30.s1[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(u30.x == 1); // expected-warning{{UNKNOWN}} + return 0; +} + +struct kk { + union uu u; + char * s2; +}; + +int f31() { + struct kk k31; + k31.s2 = strdup("hello"); + k31.u.x = 1; + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(k31.u.s1, input, 4); + clang_analyzer_eval(k31.u.s1[0] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by 'k31.s2'}} + clang_analyzer_eval(k31.u.s1[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(k31.u.s1[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(k31.u.s1[3] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(k31.u.x == 1); // expected-warning{{UNKNOWN}} + // FIXME: memory leak warning for k31.s2 should be emitted here. + return 0; +} + +union vv { + int x; + char * s2; +}; + +int f32() { + union vv v32; + v32.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(v32.s2, input, 4); + clang_analyzer_eval(v32.s2[0] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(v32.s2[1] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(v32.s2[2] == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(v32.s2[3] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{Potential leak of memory pointed to by 'v32.s2'}} + return 0; +} + +struct nn { + int s1; + int i; + int j; + int k; + char * s2; +}; + +// Test bad types to dest buffer. +int f33() { + struct nn n33 = {1, 2, 3, 4, 0}; + n33.s2 = strdup("hello"); + char input[] = {'a', 'b', 'c', 'd'}; + memcpy(n33.s1, input, 4); // expected-warning{{incompatible integer to pointer conversion passing 'int' to parameter of type 'void *'}} + clang_analyzer_eval(n33.i == 2); // expected-warning{{TRUE}} + clang_analyzer_eval(n33.j == 3); // expected-warning{{TRUE}} + clang_analyzer_eval(n33.k == 4); // expected-warning{{TRUE}} + clang_analyzer_eval(((char*)(n33.s1))[0] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{cast to 'char *' from smaller integer type 'int'}} + clang_analyzer_eval(((char*)(n33.s1))[1] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{cast to 'char *' from smaller integer type 'int'}} + clang_analyzer_eval(((char*)(n33.s1))[2] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{cast to 'char *' from smaller integer type 'int'}} + clang_analyzer_eval(((char*)(n33.s1))[3] == 1); // expected-warning{{UNKNOWN}}\ + expected-warning{{cast to 'char *' from smaller integer type 'int'}} + clang_analyzer_eval(n33.s2 == 0); //expected-warning{{UNKNOWN}} + return 0; // expected-warning{{Potential leak of memory pointed to by 'n33.s2'}} +} Index: lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- lib/StaticAnalyzer/Core/RegionStore.cpp +++ lib/StaticAnalyzer/Core/RegionStore.cpp @@ -710,8 +710,7 @@ } bool AddToWorkList(const MemRegion *R) { - const MemRegion *BaseR = R->getBaseRegion(); - return AddToWorkList(WorkListElement(BaseR), getCluster(BaseR)); + return static_cast<DERIVED*>(this)->AddToWorkList(R); } void RunWorkList() { @@ -956,9 +955,20 @@ void VisitCluster(const MemRegion *baseR, const ClusterBindings *C); void VisitBinding(SVal V); + + using ClusterAnalysis::AddToWorkList; + + bool AddToWorkList(const MemRegion *R); }; } +bool invalidateRegionsWorker::AddToWorkList(const MemRegion *R) { + bool doNotInvalidateSuperRegion = ITraits.hasTrait( + R, RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + const MemRegion *BaseR = doNotInvalidateSuperRegion ? R : R->getBaseRegion(); + return AddToWorkList(WorkListElement(BaseR), getCluster(BaseR)); +} + void invalidateRegionsWorker::VisitBinding(SVal V) { // A symbol? Mark it touched by the invalidation. if (SymbolRef Sym = V.getAsSymbol()) @@ -1071,6 +1081,66 @@ } if (const ArrayType *AT = Ctx.getAsArrayType(T)) { + bool doNotInvalidateSuperRegion = ITraits.hasTrait( + baseR, + RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + + if (doNotInvalidateSuperRegion) { + // We are not doing blank invalidation of the whole array region so we + // have to manually invalidate each elements. + Optional<uint64_t> NumElements; + + // Compute lower and upper offsets for region within array. + if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(AT)) + NumElements = CAT->getSize().getZExtValue(); + if (!NumElements) // We are not dealing with a constant size array + goto conjure_default; + QualType ElementTy = AT->getElementType(); + uint64_t ElemSize = Ctx.getTypeSize(ElementTy); + const RegionOffset &RO = baseR->getAsOffset(); + const MemRegion *SuperR = baseR->getBaseRegion(); + if (RO.hasSymbolicOffset()) { + // If base region has a symbolic offset, + // we revert to invalidating the super region. + if (SuperR) + AddToWorkList(SuperR); + goto conjure_default; + } + assert(RO.getOffset() >= 0 && "Offset should not be negative"); + uint64_t LowerOffset = RO.getOffset(); + uint64_t UpperOffset = LowerOffset + *NumElements * ElemSize; + + // Invalidate regions which are within array boundaries, + // or have a symbolic offset. + if (!SuperR) + goto conjure_default; + + const ClusterBindings *C = B.lookup(SuperR); + if (!C) + goto conjure_default; + + for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; + ++I) { + const BindingKey &BK = I.getKey(); + Optional<uint64_t> ROffset = + BK.hasSymbolicOffset() ? Optional<uint64_t>() : BK.getOffset(); + // Check offset is not symbolic and within array's boundaries. + // Handles arrays of 0 elements and of 0-sized elements as well. + if (!ROffset || + (ROffset && + ((*ROffset >= LowerOffset && *ROffset < UpperOffset) || + (LowerOffset == UpperOffset && *ROffset == LowerOffset)))) { + B = B.removeBinding(I.getKey()); + // Bound symbolic regions need to be invalidated for dead symbol + // detection. + SVal V = I.getData(); + const MemRegion *R = V.getAsRegion(); + if (R && isa<SymbolicRegion>(R)) + VisitBinding(V); + } + } + } + conjure_default: // Set the default value of the array to conjured symbol. DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, @@ -2187,11 +2257,20 @@ void VisitCluster(const MemRegion *baseR, const ClusterBindings *C); using ClusterAnalysis<removeDeadBindingsWorker>::VisitCluster; + using ClusterAnalysis::AddToWorkList; + + bool AddToWorkList(const MemRegion *R); + bool UpdatePostponed(); void VisitBinding(SVal V); }; } +bool removeDeadBindingsWorker::AddToWorkList(const MemRegion *R) { + const MemRegion *BaseR = R->getBaseRegion(); + return AddToWorkList(WorkListElement(BaseR), getCluster(BaseR)); +} + void removeDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C) { Index: lib/StaticAnalyzer/Checkers/CStringChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -145,7 +145,8 @@ static ProgramStateRef InvalidateBuffer(CheckerContext &C, ProgramStateRef state, const Expr *Ex, SVal V, - bool IsSourceBuffer); + bool IsSourceBuffer, + const Expr *Size); static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx, const MemRegion *MR); @@ -193,6 +194,14 @@ ProgramStateRef state, NonLoc left, NonLoc right) const; + + // Return true if destination buffer of copy function is in bound. + // Expects SVal of Size to be positive and unsigned. + // Expects SVal of FirstBuf to be a FieldRegion. + static bool IsFirstBufInBound(CheckerContext &C, + ProgramStateRef state, + const Expr *FirstBuf, + const Expr *Size); }; } //end anonymous namespace @@ -814,10 +823,68 @@ return strRegion->getStringLiteral(); } +bool CStringChecker::IsFirstBufInBound(CheckerContext &C, + ProgramStateRef state, + const Expr *FirstBuf, + const Expr *Size) { + + // Originally copied from CheckBufferAccess and CheckLocation. + SValBuilder &svalBuilder = C.getSValBuilder(); + ASTContext &Ctx = svalBuilder.getContext(); + const LocationContext *LCtx = C.getLocationContext(); + + QualType sizeTy = Size->getType(); + QualType PtrTy = Ctx.getPointerType(Ctx.CharTy); + SVal BufVal = state->getSVal(FirstBuf, LCtx); + + SVal LengthVal = state->getSVal(Size, LCtx); + // Cast is safe as the size argument to copy functions are of integral type. + NonLoc Length = LengthVal.castAs<NonLoc>(); + + // Compute the offset of the last element to be accessed: size-1. + NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>(); + NonLoc LastOffset = + svalBuilder.evalBinOpNN(state, BO_Sub, Length, One, sizeTy) + .castAs<NonLoc>(); + + // Check that the first buffer is sufficiently long. + SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType()); + // Cast is safe as caller checks BufVal is a MemRegionVal. + Loc BufLoc = BufStart.castAs<Loc>(); + + SVal BufEnd = + svalBuilder.evalBinOpLN(state, BO_Add, BufLoc, LastOffset, PtrTy); + + // Check for out of bound array element access. + const MemRegion *R = BufEnd.getAsRegion(); + // BufStart is a MemRegionVal so BufEnd should be one too. + assert(R && "BufEnd should be a MemRegion"); + + // Cast is safe as BufVal's region is a FieldRegion. + const ElementRegion *ER = cast<ElementRegion>(R); + + assert(ER->getValueType() == C.getASTContext().CharTy && + "IsFirstBufInBound should only be called with char* ElementRegions"); + + // Get the size of the array. + const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion()); + SVal Extent = + svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder)); + DefinedOrUnknownSVal ExtentSize = Extent.castAs<DefinedOrUnknownSVal>(); + + // Get the index of the accessed element. + DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); + + ProgramStateRef StInBound = state->assumeInBound(Idx, ExtentSize, true); + + return static_cast<bool>(StInBound); +} + ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, ProgramStateRef state, const Expr *E, SVal V, - bool IsSourceBuffer) { + bool IsSourceBuffer, + const Expr *Size) { Optional<Loc> L = V.getAs<Loc>(); if (!L) return state; @@ -847,6 +914,16 @@ RegionAndSymbolInvalidationTraits::TK_PreserveContents); ITraits.setTrait(R, RegionAndSymbolInvalidationTraits::TK_SuppressEscape); CausesPointerEscape = true; + } else { + const MemRegion::Kind& K = R->getKind(); + if (K == MemRegion::FieldRegionKind) + if (Size && IsFirstBufInBound(C, state, E, Size)) { + // If destination buffer is a field region and access is in bound, + // do not invalidate its super region. + ITraits.setTrait( + R, + RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + } } return state->invalidateRegions(R, E, C.blockCount(), LCtx, @@ -1000,12 +1077,12 @@ // This would probably remove any existing bindings past the end of the // copied region, but that's still an improvement over blank invalidation. state = InvalidateBuffer(C, state, Dest, C.getSVal(Dest), - /*IsSourceBuffer*/false); + /*IsSourceBuffer*/false, Size); // Invalidate the source (const-invalidation without const-pointer-escaping // the address of the top-level region). state = InvalidateBuffer(C, state, Source, C.getSVal(Source), - /*IsSourceBuffer*/true); + /*IsSourceBuffer*/true, nullptr); C.addTransition(state); } @@ -1620,11 +1697,12 @@ // This would probably remove any existing bindings past the end of the // string, but that's still an improvement over blank invalidation. state = InvalidateBuffer(C, state, Dst, *dstRegVal, - /*IsSourceBuffer*/false); + /*IsSourceBuffer*/false, nullptr); // Invalidate the source (const-invalidation without const-pointer-escaping // the address of the top-level region). - state = InvalidateBuffer(C, state, srcExpr, srcVal, /*IsSourceBuffer*/true); + state = InvalidateBuffer(C, state, srcExpr, srcVal, /*IsSourceBuffer*/true, + nullptr); // Set the C string length of the destination, if we know it. if (isBounded && !isAppending) { @@ -1848,7 +1926,7 @@ // Invalidate the search string, representing the change of one delimiter // character to NUL. State = InvalidateBuffer(C, State, SearchStrPtr, Result, - /*IsSourceBuffer*/false); + /*IsSourceBuffer*/false, nullptr); // Overwrite the search string pointer. The new value is either an address // further along in the same string, or NULL if there are no more tokens. Index: include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -1333,7 +1333,9 @@ /// Tells that a region's contents is not changed. TK_PreserveContents = 0x1, /// Suppress pointer-escaping of a region. - TK_SuppressEscape = 0x2 + TK_SuppressEscape = 0x2, + // Do not invalidate super region. + TK_DoNotInvalidateSuperRegion = 0x4 // Do not forget to extend StorageTypeForKinds if number of traits exceed // the number of bits StorageTypeForKinds can store.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits