| // RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-checker=core,security.ArrayBound,debug.ExprInspection -verify %s |
| |
| // Miscellaneous tests for `security.ArrayBound` where we only test the |
| // presence or absence of a bug report. If a test doesn't fit in a more |
| // specific file and doesn't need to verify the details of 'note' diagnostics, |
| // then it should be placed here. |
| |
| void clang_analyzer_value(int); |
| |
| // Tests doing an out-of-bounds access after the end of an array using: |
| // - constant integer index |
| // - constant integer size for buffer |
| void test1(int x) { |
| int buf[100]; |
| buf[100] = 1; // expected-warning{{Out of bound access to memory}} |
| } |
| |
| void test1_ok(int x) { |
| int buf[100]; |
| buf[99] = 1; // no-warning |
| } |
| |
| const char test1_strings_underrun(int x) { |
| const char *mystr = "mary had a little lamb"; |
| return mystr[-1]; // expected-warning{{Out of bound access to memory}} |
| } |
| |
| const char test1_strings_overrun(int x) { |
| const char *mystr = "mary had a little lamb"; |
| return mystr[1000]; // expected-warning{{Out of bound access to memory}} |
| } |
| |
| const char test1_strings_ok(int x) { |
| const char *mystr = "mary had a little lamb"; |
| return mystr[5]; // no-warning |
| } |
| |
| // Tests doing an out-of-bounds access after the end of an array using: |
| // - indirect pointer to buffer |
| // - constant integer index |
| // - constant integer size for buffer |
| void test1_ptr(int x) { |
| int buf[100]; |
| int *p = buf; |
| p[101] = 1; // expected-warning{{Out of bound access to memory}} |
| } |
| |
| void test1_ptr_ok(int x) { |
| int buf[100]; |
| int *p = buf; |
| p[99] = 1; // no-warning |
| } |
| |
| // Tests doing an out-of-bounds access before the start of an array using: |
| // - indirect pointer to buffer, manipulated using simple pointer arithmetic |
| // - constant integer index |
| // - constant integer size for buffer |
| void test1_ptr_arith(int x) { |
| int buf[100]; |
| int *p = buf; |
| p = p + 100; |
| p[0] = 1; // expected-warning{{Out of bound access to memory}} |
| } |
| |
| void test1_ptr_arith_ok(int x) { |
| int buf[100]; |
| int *p = buf; |
| p = p + 99; |
| p[0] = 1; // no-warning |
| } |
| |
| void test1_ptr_arith_bad(int x) { |
| int buf[100]; |
| int *p = buf; |
| p = p + 99; |
| p[1] = 1; // expected-warning{{Out of bound access to memory}} |
| } |
| |
| void test1_ptr_arith_ok2(int x) { |
| int buf[100]; |
| int *p = buf; |
| p = p + 99; |
| p[-1] = 1; // no-warning |
| } |
| |
| // Tests doing an out-of-bounds access before the start of an array using: |
| // - constant integer index |
| // - constant integer size for buffer |
| void test2(int x) { |
| int buf[100]; |
| buf[-1] = 1; // expected-warning{{Out of bound access to memory}} |
| } |
| |
| // Tests doing an out-of-bounds access before the start of an array using: |
| // - indirect pointer to buffer |
| // - constant integer index |
| // - constant integer size for buffer |
| void test2_ptr(int x) { |
| int buf[100]; |
| int *p = buf; |
| p[-1] = 1; // expected-warning{{Out of bound access to memory}} |
| } |
| |
| // Tests doing an out-of-bounds access before the start of an array using: |
| // - indirect pointer to buffer, manipulated using simple pointer arithmetic |
| // - constant integer index |
| // - constant integer size for buffer |
| void test2_ptr_arith(int x) { |
| int buf[100]; |
| int *p = buf; |
| --p; |
| p[0] = 1; // expected-warning {{Out of bound access to memory preceding}} |
| } |
| |
| // Tests doing an out-of-bounds access before the start of a multi-dimensional |
| // array using: |
| // - constant integer indices |
| // - constant integer sizes for the array |
| void test2_multi(int x) { |
| int buf[100][100]; |
| buf[0][-1] = 1; // expected-warning{{Out of bound access to memory}} |
| } |
| |
| // Tests doing an out-of-bounds access before the start of a multi-dimensional |
| // array using: |
| // - constant integer indices |
| // - constant integer sizes for the array |
| void test2_multi_b(int x) { |
| int buf[100][100]; |
| buf[-1][0] = 1; // expected-warning{{Out of bound access to memory}} |
| } |
| |
| void test2_multi_ok(int x) { |
| int buf[100][100]; |
| buf[0][0] = 1; // no-warning |
| } |
| |
| void test3(int x) { |
| int buf[100]; |
| if (x < 0) |
| buf[x] = 1; // expected-warning{{Out of bound access to memory}} |
| } |
| |
| void test4(int x) { |
| int buf[100]; |
| if (x > 99) |
| buf[x] = 1; // expected-warning{{Out of bound access to memory}} |
| } |
| |
| // Don't warn when indexing below the start of a symbolic region's whose |
| // base extent we don't know. |
| int *get_symbolic(void); |
| void test_underflow_symbolic(void) { |
| int *buf = get_symbolic(); |
| buf[-1] = 0; // no-warning |
| } |
| |
| // But warn if we understand the internal memory layout of a symbolic region. |
| typedef struct { |
| int id; |
| char name[256]; |
| } user_t; |
| |
| user_t *get_symbolic_user(void); |
| char test_underflow_symbolic_2() { |
| user_t *user = get_symbolic_user(); |
| return user->name[-1]; // expected-warning{{Out of bound access to memory}} |
| } |
| |
| void test_incomplete_struct(void) { |
| extern struct incomplete incomplete; |
| int *p = (int *)&incomplete; |
| p[1] = 42; // no-warning |
| } |
| |
| void test_extern_void(void) { |
| extern void v; |
| int *p = (int *)&v; |
| p[1] = 42; // no-warning |
| } |
| |
| struct incomplete; |
| char test_comparison_with_extent_symbol(struct incomplete *p) { |
| // Previously this was reported as a (false positive) overflow error because |
| // the extent symbol of the area pointed by `p` was an unsigned and the '-1' |
| // was converted to its type by `evalBinOpNN`. |
| return ((char *)p)[-1]; // no-warning |
| } |
| |
| int table[256], small_table[128]; |
| int test_cast_to_unsigned(signed char x) { |
| unsigned char y = x; |
| if (x >= 0) |
| return x; |
| // FIXME: Here the analyzer ignores the signed -> unsigned cast, and manages to |
| // load a negative value from an unsigned variable. |
| // The underlying issue is tracked by Github ticket #39492. |
| clang_analyzer_value(y); // expected-warning {{8s:{ [-128, -1] } }} |
| // However, a hack in the ArrayBound checker suppresses the false positive |
| // underflow report that would be generated here. |
| return table[y]; // no-warning |
| } |
| |
| int test_cast_to_unsigned_overflow(signed char x) { |
| unsigned char y = x; |
| if (x >= 0) |
| return x; |
| // FIXME: As in 'test_cast_to_unsigned', the analyzer thinks that this |
| // unsigned variable contains a negative value. |
| clang_analyzer_value(y); // expected-warning {{8s:{ [-128, -1] } }} |
| // FIXME: The following subscript expression should produce an overflow |
| // report (because negative signed char corresponds to unsigned char >= 128); |
| // but the hack in ArrayBound just silences reports and cannot "restore" the |
| // real execution paths. |
| return small_table[y]; // no-warning |
| } |
| |
| int test_negative_offset_with_unsigned_idx(void) { |
| // An example where the subscript operator uses an unsigned index, but the |
| // underflow report is still justified. |
| int *p = table - 10; |
| unsigned idx = 2u; |
| return p[idx]; // expected-warning {{Out of bound access to memory preceding}} |
| } |
| |
| struct three_words { int c[3]; }; |
| struct seven_words { int c[7]; }; |
| void partially_in_bounds(void) { |
| struct seven_words c; |
| struct three_words a, *p = (struct three_words *)&c; |
| p[0] = a; // no-warning |
| p[1] = a; // no-warning |
| p[2] = a; // should warn |
| // FIXME: This is an overflow, but currently security.ArrayBound only checks |
| // that the _beginning_ of the accessed element is within bounds. |
| } |
| |
| void vla(int a) { |
| if (a == 5) { |
| int x[a]; |
| x[4] = 4; // no-warning |
| x[5] = 5; // expected-warning{{Out of bound access}} |
| } |
| } |
| |
| void sizeof_vla(int a) { |
| // FIXME: VLA modeling is not good enough to cover this case. |
| if (a == 5) { |
| char x[a]; |
| int y[sizeof(x)]; |
| y[4] = 4; // no-warning |
| y[5] = 5; // should be {{Out of bounds access}} |
| } |
| } |
| |
| void sizeof_vla_2(int a) { |
| // FIXME: VLA modeling is not good enough to cover this case. |
| if (a == 5) { |
| char x[a]; |
| int y[sizeof(x) / sizeof(char)]; |
| y[4] = 4; // no-warning |
| y[5] = 5; // should be {{Out of bounds access}} |
| } |
| } |
| |
| void sizeof_vla_3(int a) { |
| // FIXME: VLA modeling is not good enough to cover this case. |
| if (a == 5) { |
| char x[a]; |
| int y[sizeof(*&*&*&x)]; |
| y[4] = 4; // no-warning |
| y[5] = 5; // should be {{Out of bounds access}} |
| } |
| } |