1 // Check the basic reporting/warning and the application of constraints. 2 // RUN: %clang_analyze_cc1 %s \ 3 // RUN: -analyzer-checker=core \ 4 // RUN: -analyzer-checker=alpha.unix.StdCLibraryFunctions \ 5 // RUN: -analyzer-config alpha.unix.StdCLibraryFunctions:ModelPOSIX=true \ 6 // RUN: -analyzer-checker=debug.StdCLibraryFunctionsTester \ 7 // RUN: -analyzer-checker=debug.ExprInspection \ 8 // RUN: -triple x86_64-unknown-linux-gnu \ 9 // RUN: -verify=report 10 11 // Check the bugpath related to the reports. 12 // RUN: %clang_analyze_cc1 %s \ 13 // RUN: -analyzer-checker=core \ 14 // RUN: -analyzer-checker=alpha.unix.StdCLibraryFunctions \ 15 // RUN: -analyzer-config alpha.unix.StdCLibraryFunctions:ModelPOSIX=true \ 16 // RUN: -analyzer-checker=debug.StdCLibraryFunctionsTester \ 17 // RUN: -analyzer-checker=debug.ExprInspection \ 18 // RUN: -triple x86_64-unknown-linux-gnu \ 19 // RUN: -analyzer-output=text \ 20 // RUN: -verify=bugpath 21 22 #include "Inputs/std-c-library-functions-POSIX.h" 23 24 void clang_analyzer_eval(int); 25 void clang_analyzer_warnIfReached(); 26 27 int glob; 28 29 void test_alnum_concrete(int v) { 30 int ret = isalnum(256); // \ 31 // report-warning{{The 1st argument to 'isalnum' is 256 but should be an unsigned char value or EOF}} \ 32 // bugpath-warning{{The 1st argument to 'isalnum' is 256 but should be an unsigned char value or EOF}} \ 33 // bugpath-note{{The 1st argument to 'isalnum' is 256 but should be an unsigned char value or EOF}} 34 (void)ret; 35 } 36 37 void test_alnum_symbolic(int x) { 38 int ret = isalnum(x); 39 (void)ret; 40 41 clang_analyzer_eval(EOF <= x && x <= 255); // \ 42 // report-warning{{TRUE}} \ 43 // bugpath-warning{{TRUE}} \ 44 // bugpath-note{{TRUE}} \ 45 // bugpath-note{{Left side of '&&' is true}} \ 46 // bugpath-note{{'x' is <= 255}} 47 } 48 49 void test_alnum_symbolic2(int x) { 50 if (x > 255) { // \ 51 // bugpath-note{{Assuming 'x' is > 255}} \ 52 // bugpath-note{{Taking true branch}} 53 54 int ret = isalnum(x); // \ 55 // report-warning{{The 1st argument to 'isalnum' is >= 256 but should be an unsigned char value or EOF}} \ 56 // bugpath-warning{{The 1st argument to 'isalnum' is >= 256 but should be an unsigned char value or EOF}} \ 57 // bugpath-note{{The 1st argument to 'isalnum' is >= 256 but should be an unsigned char value or EOF}} 58 59 (void)ret; 60 } 61 } 62 63 void test_toupper_concrete(int v) { 64 int ret = toupper(256); // \ 65 // report-warning{{The 1st argument to 'toupper' is 256 but should be an unsigned char value or EOF}} \ 66 // bugpath-warning{{The 1st argument to 'toupper' is 256 but should be an unsigned char value or EOF}} \ 67 // bugpath-note{{The 1st argument to 'toupper' is 256 but should be an unsigned char value or EOF}} 68 (void)ret; 69 } 70 71 void test_toupper_symbolic(int x) { 72 int ret = toupper(x); 73 (void)ret; 74 75 clang_analyzer_eval(EOF <= x && x <= 255); // \ 76 // report-warning{{TRUE}} \ 77 // bugpath-warning{{TRUE}} \ 78 // bugpath-note{{TRUE}} \ 79 // bugpath-note{{Left side of '&&' is true}} \ 80 // bugpath-note{{'x' is <= 255}} 81 } 82 83 void test_toupper_symbolic2(int x) { 84 if (x > 255) { // \ 85 // bugpath-note{{Assuming 'x' is > 255}} \ 86 // bugpath-note{{Taking true branch}} 87 88 int ret = toupper(x); // \ 89 // report-warning{{The 1st argument to 'toupper' is >= 256 but should be an unsigned char value or EOF}} \ 90 // bugpath-warning{{The 1st argument to 'toupper' is >= 256 but should be an unsigned char value or EOF}} \ 91 // bugpath-note{{The 1st argument to 'toupper' is >= 256 but should be an unsigned char value or EOF}} 92 93 (void)ret; 94 } 95 } 96 97 void test_tolower_concrete(int v) { 98 int ret = tolower(256); // \ 99 // report-warning{{The 1st argument to 'tolower' is 256 but should be an unsigned char value or EOF}} \ 100 // bugpath-warning{{The 1st argument to 'tolower' is 256 but should be an unsigned char value or EOF}} \ 101 // bugpath-note{{The 1st argument to 'tolower' is 256 but should be an unsigned char value or EOF}} 102 (void)ret; 103 } 104 105 void test_tolower_symbolic(int x) { 106 int ret = tolower(x); 107 (void)ret; 108 109 clang_analyzer_eval(EOF <= x && x <= 255); // \ 110 // report-warning{{TRUE}} \ 111 // bugpath-warning{{TRUE}} \ 112 // bugpath-note{{TRUE}} \ 113 // bugpath-note{{Left side of '&&' is true}} \ 114 // bugpath-note{{'x' is <= 255}} 115 } 116 117 void test_tolower_symbolic2(int x) { 118 if (x > 255) { // \ 119 // bugpath-note{{Assuming 'x' is > 255}} \ 120 // bugpath-note{{Taking true branch}} 121 122 int ret = tolower(x); // \ 123 // report-warning{{The 1st argument to 'tolower' is >= 256 but should be an unsigned char value or EOF}} \ 124 // bugpath-warning{{The 1st argument to 'tolower' is >= 256 but should be an unsigned char value or EOF}} \ 125 // bugpath-note{{The 1st argument to 'tolower' is >= 256 but should be an unsigned char value or EOF}} 126 127 (void)ret; 128 } 129 } 130 131 void test_toascii_concrete(int v) { 132 int ret = toascii(256); // \ 133 // report-warning{{The 1st argument to 'toascii' is 256 but should be an unsigned char value or EOF}} \ 134 // bugpath-warning{{The 1st argument to 'toascii' is 256 but should be an unsigned char value or EOF}} \ 135 // bugpath-note{{The 1st argument to 'toascii' is 256 but should be an unsigned char value or EOF}} 136 (void)ret; 137 } 138 139 void test_toascii_symbolic(int x) { 140 int ret = toascii(x); 141 (void)ret; 142 143 clang_analyzer_eval(EOF <= x && x <= 255); // \ 144 // report-warning{{TRUE}} \ 145 // bugpath-warning{{TRUE}} \ 146 // bugpath-note{{TRUE}} \ 147 // bugpath-note{{Left side of '&&' is true}} \ 148 // bugpath-note{{'x' is <= 255}} 149 } 150 151 void test_toascii_symbolic2(int x) { 152 if (x > 255) { // \ 153 // bugpath-note{{Assuming 'x' is > 255}} \ 154 // bugpath-note{{Taking true branch}} 155 156 int ret = toascii(x); // \ 157 // report-warning{{The 1st argument to 'toascii' is >= 256 but should be an unsigned char value or EOF}} \ 158 // bugpath-warning{{The 1st argument to 'toascii' is >= 256 but should be an unsigned char value or EOF}} \ 159 // bugpath-note{{The 1st argument to 'toascii' is >= 256 but should be an unsigned char value or EOF}} 160 161 (void)ret; 162 } 163 } 164 165 void test_notnull_concrete(FILE *fp) { 166 fread(0, sizeof(int), 10, fp); // \ 167 // report-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ 168 // bugpath-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ 169 // bugpath-note{{The 1st argument to 'fread' is NULL but should not be NULL}} 170 } 171 void test_notnull_symbolic(FILE *fp, int *buf) { 172 fread(buf, sizeof(int), 10, fp); 173 clang_analyzer_eval(buf != 0); // \ 174 // report-warning{{TRUE}} \ 175 // bugpath-warning{{TRUE}} \ 176 // bugpath-note{{TRUE}} \ 177 // bugpath-note{{'buf' is not equal to null}} 178 } 179 void test_notnull_symbolic2(FILE *fp, int *buf) { 180 if (!buf) // bugpath-note{{Assuming 'buf' is null}} \ 181 // bugpath-note{{Taking true branch}} 182 fread(buf, sizeof(int), 10, fp); // \ 183 // report-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ 184 // bugpath-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ 185 // bugpath-note{{The 1st argument to 'fread' is NULL but should not be NULL}} 186 } 187 void test_no_node_after_bug(FILE *fp, size_t size, size_t n, void *buf) { 188 if (fp) // \ 189 // bugpath-note{{Assuming 'fp' is null}} \ 190 // bugpath-note{{Taking false branch}} 191 return; 192 size_t ret = fread(buf, size, n, fp); // \ 193 // report-warning{{The 4th argument to 'fread' is NULL but should not be NULL}} \ 194 // bugpath-warning{{The 4th argument to 'fread' is NULL but should not be NULL}} \ 195 // bugpath-note{{The 4th argument to 'fread' is NULL but should not be NULL}} 196 clang_analyzer_warnIfReached(); // not reachable 197 } 198 199 // This is one test case for the ARR38-C SEI-CERT rule. 200 void ARR38_C_F(FILE *file) { 201 enum { BUFFER_SIZE = 1024 }; 202 wchar_t wbuf[BUFFER_SIZE]; // bugpath-note{{'wbuf' initialized here}} 203 204 const size_t size = sizeof(*wbuf); // bugpath-note{{'size' initialized to}} 205 const size_t nitems = sizeof(wbuf); // bugpath-note{{'nitems' initialized to}} 206 207 // The 3rd parameter should be the number of elements to read, not 208 // the size in bytes. 209 fread(wbuf, size, nitems, file); // \ 210 // report-warning{{The 1st argument to 'fread' is a buffer with size 4096 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 4096)}} \ 211 // bugpath-warning{{The 1st argument to 'fread' is a buffer with size 4096 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 4096)}} \ 212 // bugpath-note{{The 1st argument to 'fread' is a buffer with size 4096 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 4096)}} 213 } 214 215 int __two_constrained_args(int, int); 216 void test_constraints_on_multiple_args(int x, int y) { 217 // State split should not happen here. I.e. x == 1 should not be evaluated 218 // FALSE. 219 __two_constrained_args(x, y); 220 //NOTE! Because of the second `clang_analyzer_eval` call we have two bug 221 clang_analyzer_eval(x == 1); // \ 222 // report-warning{{TRUE}} \ 223 // bugpath-warning{{TRUE}} \ 224 // bugpath-note{{TRUE}} 225 clang_analyzer_eval(y == 1); // \ 226 // report-warning{{TRUE}} \ 227 // bugpath-warning{{TRUE}} \ 228 // bugpath-note{{TRUE}} 229 } 230 231 int __arg_constrained_twice(int); 232 void test_multiple_constraints_on_same_arg(int x) { 233 __arg_constrained_twice(x); 234 clang_analyzer_eval(x < 1 || x > 2); // \ 235 // report-warning{{TRUE}} \ 236 // bugpath-warning{{TRUE}} \ 237 // bugpath-note{{TRUE}} \ 238 // bugpath-note{{Assuming 'x' is < 1}} \ 239 // bugpath-note{{Left side of '||' is true}} 240 } 241 242 int __variadic(void *stream, const char *format, ...); 243 void test_arg_constraint_on_variadic_fun(void) { 244 __variadic(0, "%d%d", 1, 2); // \ 245 // report-warning{{The 1st argument to '__variadic' is NULL but should not be NULL}} \ 246 // bugpath-warning{{The 1st argument to '__variadic' is NULL but should not be NULL}} \ 247 // bugpath-note{{The 1st argument to '__variadic' is NULL but should not be NULL}} 248 } 249 250 int __buf_size_arg_constraint(const void *, size_t); 251 void test_buf_size_concrete(void) { 252 char buf[3]; // bugpath-note{{'buf' initialized here}} 253 __buf_size_arg_constraint(buf, 4); // \ 254 // report-warning{{The 1st argument to '__buf_size_arg_constraint' is a buffer with size 3 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4)}} \ 255 // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint' is a buffer with size 3 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4)}} \ 256 // bugpath-note{{The 1st argument to '__buf_size_arg_constraint' is a buffer with size 3 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4)}} 257 } 258 void test_buf_size_symbolic(int s) { 259 char buf[3]; 260 __buf_size_arg_constraint(buf, s); 261 clang_analyzer_eval(s <= 3); // \ 262 // report-warning{{TRUE}} \ 263 // bugpath-warning{{TRUE}} \ 264 // bugpath-note{{TRUE}} \ 265 // bugpath-note{{'s' is <= 3}} 266 } 267 void test_buf_size_symbolic_and_offset(int s) { 268 char buf[3]; 269 __buf_size_arg_constraint(buf + 1, s); 270 clang_analyzer_eval(s <= 2); // \ 271 // report-warning{{TRUE}} \ 272 // bugpath-warning{{TRUE}} \ 273 // bugpath-note{{TRUE}} \ 274 // bugpath-note{{'s' is <= 2}} 275 } 276 277 int __buf_size_arg_constraint_mul(const void *, size_t, size_t); 278 void test_buf_size_concrete_with_multiplication(void) { 279 short buf[3]; // bugpath-note{{'buf' initialized here}} 280 __buf_size_arg_constraint_mul(buf, 4, sizeof(short)); // \ 281 // report-warning{{The 1st argument to '__buf_size_arg_constraint_mul' is a buffer with size 6 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 2)}} \ 282 // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint_mul' is a buffer with size 6 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 2)}} \ 283 // bugpath-note{{The 1st argument to '__buf_size_arg_constraint_mul' is a buffer with size 6 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 2)}} 284 } 285 void test_buf_size_symbolic_with_multiplication(size_t s) { 286 short buf[3]; 287 __buf_size_arg_constraint_mul(buf, s, sizeof(short)); 288 clang_analyzer_eval(s * sizeof(short) <= 6); // \ 289 // report-warning{{TRUE}} \ 290 // bugpath-warning{{TRUE}} \ 291 // bugpath-note{{TRUE}} 292 } 293 void test_buf_size_symbolic_and_offset_with_multiplication(size_t s) { 294 short buf[3]; 295 __buf_size_arg_constraint_mul(buf + 1, s, sizeof(short)); 296 clang_analyzer_eval(s * sizeof(short) <= 4); // \ 297 // report-warning{{TRUE}} \ 298 // bugpath-warning{{TRUE}} \ 299 // bugpath-note{{TRUE}} 300 } 301 302 // The minimum buffer size for this function is set to 10. 303 int __buf_size_arg_constraint_concrete(const void *); 304 void test_min_buf_size(void) { 305 char buf[9];// bugpath-note{{'buf' initialized here}} 306 __buf_size_arg_constraint_concrete(buf); // \ 307 // report-warning{{The 1st argument to '__buf_size_arg_constraint_concrete' is a buffer with size 9 but should be a buffer with size equal to or greater than 10}} \ 308 // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint_concrete' is a buffer with size 9 but should be a buffer with size equal to or greater than 10}} \ 309 // bugpath-note{{The 1st argument to '__buf_size_arg_constraint_concrete' is a buffer with size 9 but should be a buffer with size equal to or greater than 10}} 310 } 311 312 void test_file_fd_at_functions() { 313 (void)linkat(-22, "from", AT_FDCWD, "to", 0); // \ 314 // report-warning{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}} \ 315 // bugpath-warning{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}} \ 316 // bugpath-note{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}} 317 318 // no warning for these functions if the AT_FDCWD value is used 319 (void)linkat(AT_FDCWD, "from", AT_FDCWD, "to", 0); 320 (void)faccessat(AT_FDCWD, "path", 0, 0); 321 (void)symlinkat("oldpath", AT_FDCWD, "newpath"); 322 (void)mkdirat(AT_FDCWD, "path", 0); 323 (void)mknodat(AT_FDCWD, "path", 0, 0); 324 (void)fchmodat(AT_FDCWD, "path", 0, 0); 325 (void)fchownat(AT_FDCWD, "path", 0, 0, 0); 326 (void)linkat(AT_FDCWD, "oldpath", AT_FDCWD, "newpath", 0); 327 (void)unlinkat(AT_FDCWD, "newpath", 0); 328 struct stat St; 329 (void)fstatat(AT_FDCWD, "newpath", &St, 0); 330 char Buf[10]; 331 (void)readlinkat(AT_FDCWD, "newpath", Buf, 10); 332 (void)renameat(AT_FDCWD, "oldpath", AT_FDCWD, "newpath"); 333 } 334