1 // RUN: %clang_analyze_cc1 -triple=x86_64-pc-linux-gnu -analyzer-checker=core,unix.Stream,debug.ExprInspection \ 2 // RUN: -analyzer-config eagerly-assume=false,unix.Stream:Pedantic=true -verify %s 3 // RUN: %clang_analyze_cc1 -triple=armv8-none-linux-eabi -analyzer-checker=core,unix.Stream,debug.ExprInspection \ 4 // RUN: -analyzer-config eagerly-assume=false,unix.Stream:Pedantic=true -verify %s 5 // RUN: %clang_analyze_cc1 -triple=aarch64-linux-gnu -analyzer-checker=core,unix.Stream,debug.ExprInspection \ 6 // RUN: -analyzer-config eagerly-assume=false,unix.Stream:Pedantic=true -verify %s 7 // RUN: %clang_analyze_cc1 -triple=hexagon -analyzer-checker=core,unix.Stream,debug.ExprInspection \ 8 // RUN: -analyzer-config eagerly-assume=false,unix.Stream:Pedantic=true -verify %s 9 10 #include "Inputs/system-header-simulator.h" 11 #include "Inputs/system-header-simulator-for-malloc.h" 12 #include "Inputs/system-header-simulator-for-valist.h" 13 14 void clang_analyzer_eval(int); 15 16 void check_fread(void) { 17 FILE *fp = tmpfile(); 18 fread(0, 0, 0, fp); // expected-warning {{Stream pointer might be NULL}} 19 fclose(fp); 20 } 21 22 void check_fwrite(void) { 23 FILE *fp = tmpfile(); 24 fwrite(0, 0, 0, fp); // expected-warning {{Stream pointer might be NULL}} 25 fclose(fp); 26 } 27 28 void check_fgetc(void) { 29 FILE *fp = tmpfile(); 30 fgetc(fp); // expected-warning {{Stream pointer might be NULL}} 31 fclose(fp); 32 } 33 34 void check_fgets(void) { 35 FILE *fp = tmpfile(); 36 char buf[256]; 37 fgets(buf, sizeof(buf), fp); // expected-warning {{Stream pointer might be NULL}} 38 fclose(fp); 39 } 40 41 void check_fputc(void) { 42 FILE *fp = tmpfile(); 43 fputc('A', fp); // expected-warning {{Stream pointer might be NULL}} 44 fclose(fp); 45 } 46 47 void check_fputs(void) { 48 FILE *fp = tmpfile(); 49 fputs("ABC", fp); // expected-warning {{Stream pointer might be NULL}} 50 fclose(fp); 51 } 52 53 void check_fprintf(void) { 54 FILE *fp = tmpfile(); 55 fprintf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}} 56 fclose(fp); 57 } 58 59 void check_fscanf(void) { 60 FILE *fp = tmpfile(); 61 fscanf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}} 62 fclose(fp); 63 } 64 65 void check_ungetc(void) { 66 FILE *fp = tmpfile(); 67 ungetc('A', fp); // expected-warning {{Stream pointer might be NULL}} 68 fclose(fp); 69 } 70 71 void check_fseek(void) { 72 FILE *fp = tmpfile(); 73 fseek(fp, 0, 0); // expected-warning {{Stream pointer might be NULL}} 74 fclose(fp); 75 } 76 77 void check_fseeko(void) { 78 FILE *fp = tmpfile(); 79 fseeko(fp, 0, 0); // expected-warning {{Stream pointer might be NULL}} 80 fclose(fp); 81 } 82 83 void check_ftell(void) { 84 FILE *fp = tmpfile(); 85 ftell(fp); // expected-warning {{Stream pointer might be NULL}} 86 fclose(fp); 87 } 88 89 void check_ftello(void) { 90 FILE *fp = tmpfile(); 91 ftello(fp); // expected-warning {{Stream pointer might be NULL}} 92 fclose(fp); 93 } 94 95 void check_rewind(void) { 96 FILE *fp = tmpfile(); 97 rewind(fp); // expected-warning {{Stream pointer might be NULL}} 98 fclose(fp); 99 } 100 101 void check_fgetpos(void) { 102 FILE *fp = tmpfile(); 103 fpos_t pos; 104 fgetpos(fp, &pos); // expected-warning {{Stream pointer might be NULL}} 105 fclose(fp); 106 } 107 108 void check_fsetpos(void) { 109 FILE *fp = tmpfile(); 110 fpos_t pos; 111 fsetpos(fp, &pos); // expected-warning {{Stream pointer might be NULL}} 112 fclose(fp); 113 } 114 115 void check_clearerr(void) { 116 FILE *fp = tmpfile(); 117 clearerr(fp); // expected-warning {{Stream pointer might be NULL}} 118 fclose(fp); 119 } 120 121 void check_feof(void) { 122 FILE *fp = tmpfile(); 123 feof(fp); // expected-warning {{Stream pointer might be NULL}} 124 fclose(fp); 125 } 126 127 void check_ferror(void) { 128 FILE *fp = tmpfile(); 129 ferror(fp); // expected-warning {{Stream pointer might be NULL}} 130 fclose(fp); 131 } 132 133 void check_fileno(void) { 134 FILE *fp = tmpfile(); 135 fileno(fp); // expected-warning {{Stream pointer might be NULL}} 136 fclose(fp); 137 } 138 139 void f_open(void) { 140 FILE *p = fopen("foo", "r"); 141 char buf[1024]; 142 fread(buf, 1, 1, p); // expected-warning {{Stream pointer might be NULL}} 143 fclose(p); 144 } 145 146 void f_dopen(int fd) { 147 FILE *F = fdopen(fd, "r"); 148 char buf[1024]; 149 fread(buf, 1, 1, F); // expected-warning {{Stream pointer might be NULL}} 150 fclose(F); 151 } 152 153 void f_vfprintf(int fd, va_list args) { 154 FILE *F = fdopen(fd, "r"); 155 vfprintf(F, "%d", args); // expected-warning {{Stream pointer might be NULL}} 156 fclose(F); 157 } 158 159 void f_vfscanf(int fd, va_list args) { 160 FILE *F = fdopen(fd, "r"); 161 vfscanf(F, "%u", args); // expected-warning {{Stream pointer might be NULL}} 162 fclose(F); 163 } 164 165 void f_seek(void) { 166 FILE *p = fopen("foo", "r"); 167 if (!p) 168 return; 169 fseek(p, 1, SEEK_SET); // no-warning 170 fseek(p, 1, 3); // expected-warning {{The whence argument to fseek() should be SEEK_SET, SEEK_END, or SEEK_CUR}} 171 fclose(p); 172 } 173 174 void f_seeko(void) { 175 FILE *p = fopen("foo", "r"); 176 if (!p) 177 return; 178 fseeko(p, 1, SEEK_SET); // no-warning 179 fseeko(p, 1, 3); // expected-warning {{The whence argument to fseek() should be SEEK_SET, SEEK_END, or SEEK_CUR}} 180 fclose(p); 181 } 182 183 void f_double_close(void) { 184 FILE *p = fopen("foo", "r"); 185 if (!p) 186 return; 187 fclose(p); 188 fclose(p); // expected-warning {{Use of a stream that might be already closed}} 189 } 190 191 void f_double_close_alias(void) { 192 FILE *p1 = fopen("foo", "r"); 193 if (!p1) 194 return; 195 FILE *p2 = p1; 196 fclose(p1); 197 fclose(p2); // expected-warning {{Use of a stream that might be already closed}} 198 } 199 200 void f_use_after_close(void) { 201 FILE *p = fopen("foo", "r"); 202 if (!p) 203 return; 204 fclose(p); 205 clearerr(p); // expected-warning {{Use of a stream that might be already closed}} 206 } 207 208 void f_open_after_close(void) { 209 FILE *p = fopen("foo", "r"); 210 if (!p) 211 return; 212 fclose(p); 213 p = fopen("foo", "r"); 214 if (!p) 215 return; 216 fclose(p); 217 } 218 219 void f_reopen_after_close(void) { 220 FILE *p = fopen("foo", "r"); 221 if (!p) 222 return; 223 fclose(p); 224 // Allow reopen after close. 225 p = freopen("foo", "w", p); 226 if (!p) 227 return; 228 fclose(p); 229 } 230 231 void f_leak(int c) { 232 FILE *p = fopen("foo.c", "r"); 233 if (!p) 234 return; 235 if(c) 236 return; // expected-warning {{Opened stream never closed. Potential resource leak}} 237 fclose(p); 238 } 239 240 FILE *f_null_checked(void) { 241 FILE *p = fopen("foo.c", "r"); 242 if (p) 243 return p; // no-warning 244 else 245 return 0; 246 } 247 248 void pr7831(FILE *fp) { 249 fclose(fp); // no-warning 250 } 251 252 // PR 8081 - null pointer crash when 'whence' is not an integer constant 253 void pr8081(FILE *stream, long offset, int whence) { 254 fseek(stream, offset, whence); 255 } 256 257 void check_freopen_1(void) { 258 FILE *f1 = freopen("foo.c", "r", (FILE *)0); // expected-warning {{Stream pointer might be NULL}} 259 f1 = freopen(0, "w", (FILE *)0x123456); // Do not report this as error. 260 } 261 262 void check_freopen_2(void) { 263 FILE *f1 = fopen("foo.c", "r"); 264 if (f1) { 265 FILE *f2 = freopen(0, "w", f1); 266 if (f2) { 267 // Check if f1 and f2 point to the same stream. 268 fclose(f1); 269 fclose(f2); // expected-warning {{Use of a stream that might be already closed}} 270 } else { 271 // Reopen failed. 272 // f1 is non-NULL but points to a possibly invalid stream. 273 rewind(f1); // expected-warning {{Stream might be invalid}} 274 // f2 is NULL but the previous error stops the checker. 275 rewind(f2); 276 } 277 } 278 } 279 280 void check_freopen_3(void) { 281 FILE *f1 = fopen("foo.c", "r"); 282 if (f1) { 283 // Unchecked result of freopen. 284 // The f1 may be invalid after this call. 285 freopen(0, "w", f1); 286 rewind(f1); // expected-warning {{Stream might be invalid}} 287 fclose(f1); 288 } 289 } 290 291 extern FILE *GlobalF; 292 extern void takeFile(FILE *); 293 294 void check_escape1(void) { 295 FILE *F = tmpfile(); 296 if (!F) 297 return; 298 fwrite("1", 1, 1, F); // may fail 299 GlobalF = F; 300 fwrite("1", 1, 1, F); // no warning 301 } 302 303 void check_escape2(void) { 304 FILE *F = tmpfile(); 305 if (!F) 306 return; 307 fwrite("1", 1, 1, F); // may fail 308 takeFile(F); 309 fwrite("1", 1, 1, F); // no warning 310 } 311 312 void check_escape3(void) { 313 FILE *F = tmpfile(); 314 if (!F) 315 return; 316 takeFile(F); 317 F = freopen(0, "w", F); 318 if (!F) 319 return; 320 fwrite("1", 1, 1, F); // may fail 321 fwrite("1", 1, 1, F); // no warning 322 } 323 324 void check_escape4(void) { 325 FILE *F = tmpfile(); 326 if (!F) 327 return; 328 fwrite("1", 1, 1, F); // may fail 329 330 // no escape at a non-StreamChecker-handled system call 331 setbuf(F, "0"); 332 333 fwrite("1", 1, 1, F); // expected-warning {{might be 'indeterminate'}} 334 fclose(F); 335 } 336 337 int Test; 338 _Noreturn void handle_error(void); 339 340 void check_leak_noreturn_1(void) { 341 FILE *F1 = tmpfile(); 342 if (!F1) 343 return; 344 if (Test == 1) { 345 handle_error(); // no warning 346 } 347 rewind(F1); 348 } // expected-warning {{Opened stream never closed. Potential resource leak}} 349 350 // Check that "location uniqueing" works. 351 // This results in reporting only one occurence of resource leak for a stream. 352 void check_leak_noreturn_2(void) { 353 FILE *F1 = tmpfile(); 354 if (!F1) 355 return; 356 if (Test == 1) { 357 return; // no warning 358 } 359 rewind(F1); 360 } // expected-warning {{Opened stream never closed. Potential resource leak}} 361 // FIXME: This warning should be placed at the `return` above. 362 // See https://reviews.llvm.org/D83120 about details. 363 364 void fflush_after_fclose(void) { 365 FILE *F = tmpfile(); 366 int Ret; 367 fflush(NULL); // no-warning 368 if (!F) 369 return; 370 if ((Ret = fflush(F)) != 0) 371 clang_analyzer_eval(Ret == EOF); // expected-warning {{TRUE}} 372 fclose(F); 373 fflush(F); // expected-warning {{Use of a stream that might be already closed}} 374 } 375 376 void fflush_on_open_failed_stream(void) { 377 FILE *F = tmpfile(); 378 if (!F) { 379 fflush(F); // no-warning 380 return; 381 } 382 fclose(F); 383 } 384 385 void getline_null_file() { 386 char *buffer = NULL; 387 size_t n = 0; 388 getline(&buffer, &n, NULL); // expected-warning {{Stream pointer might be NULL}} 389 } 390 391 void getdelim_null_file() { 392 char *buffer = NULL; 393 size_t n = 0; 394 getdelim(&buffer, &n, '\n', NULL); // expected-warning {{Stream pointer might be NULL}} 395 } 396 397 void getline_buffer_on_error() { 398 FILE *file = fopen("file.txt", "r"); 399 if (file == NULL) { 400 return; 401 } 402 403 char *line = NULL; 404 size_t len = 0; 405 if (getline(&line, &len, file) == -1) { 406 if (line[0] == '\0') {} // expected-warning {{The left operand of '==' is a garbage value}} 407 } else { 408 if (line[0] == '\0') {} // no warning 409 } 410 411 free(line); 412 fclose(file); 413 } 414 415 void getline_ret_value() { 416 FILE *file = fopen("file.txt", "r"); 417 if (file == NULL) { 418 return; 419 } 420 421 size_t n = 0; 422 char *buffer = NULL; 423 ssize_t r = getline(&buffer, &n, file); 424 425 if (r > -1) { 426 // The return value does *not* include the terminating null byte. 427 // The buffer must be large enough to include it. 428 clang_analyzer_eval(n > r); // expected-warning{{TRUE}} 429 clang_analyzer_eval(buffer != NULL); // expected-warning{{TRUE}} 430 } 431 432 fclose(file); 433 free(buffer); 434 } 435 436 437 void getline_buffer_size_negative() { 438 FILE *file = fopen("file.txt", "r"); 439 if (file == NULL) { 440 return; 441 } 442 443 size_t n = -1; 444 clang_analyzer_eval((ssize_t)n >= 0); // expected-warning{{FALSE}} 445 char *buffer = NULL; 446 ssize_t r = getline(&buffer, &n, file); 447 448 if (r > -1) { 449 clang_analyzer_eval((ssize_t)n > r); // expected-warning{{TRUE}} 450 clang_analyzer_eval(buffer != NULL); // expected-warning{{TRUE}} 451 } 452 453 free(buffer); 454 fclose(file); 455 } 456 457 void gh_93408_regression(void *buffer) { 458 FILE *f = fopen("/tmp/foo.txt", "r"); 459 fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} no-crash 460 fclose(f); 461 } 462 463 typedef void VOID; 464 void gh_93408_regression_typedef(VOID *buffer) { 465 FILE *f = fopen("/tmp/foo.txt", "r"); 466 fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} no-crash 467 fclose(f); 468 } 469 470 struct FAM { 471 int data; 472 int tail[]; 473 }; 474 475 struct FAM0 { 476 int data; 477 int tail[0]; 478 }; 479 480 void gh_93408_regression_FAM(struct FAM *p) { 481 FILE *f = fopen("/tmp/foo.txt", "r"); 482 fread(p->tail, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} 483 fclose(f); 484 } 485 486 void gh_93408_regression_FAM0(struct FAM0 *p) { 487 FILE *f = fopen("/tmp/foo.txt", "r"); 488 fread(p->tail, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} 489 fclose(f); 490 } 491 492 struct ZeroSized { 493 int data[0]; 494 }; 495 496 void gh_93408_regression_ZeroSized(struct ZeroSized *buffer) { 497 FILE *f = fopen("/tmp/foo.txt", "r"); 498 fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} no-crash 499 fclose(f); 500 } 501 502 extern FILE *non_standard_stream_ptr; 503 void test_fopen_does_not_alias_with_standard_streams(void) { 504 FILE *f = fopen("file", "r"); 505 if (!f) return; 506 clang_analyzer_eval(f == stdin); // expected-warning {{FALSE}} no-TRUE 507 clang_analyzer_eval(f == stdout); // expected-warning {{FALSE}} no-TRUE 508 clang_analyzer_eval(f == stderr); // expected-warning {{FALSE}} no-TRUE 509 clang_analyzer_eval(f == non_standard_stream_ptr); // expected-warning {{UNKNOWN}} 510 if (f != stdout) { 511 fclose(f); 512 } 513 } // no-leak: 'fclose()' is always called because 'f' cannot be 'stdout'. 514 515 void reopen_std_stream(void) { 516 FILE *oldStdout = stdout; 517 fclose(stdout); 518 FILE *fp = fopen("blah", "w"); 519 if (!fp) return; 520 521 stdout = fp; // Let's make them alias. 522 clang_analyzer_eval(fp == oldStdout); // expected-warning {{UNKNOWN}} 523 clang_analyzer_eval(fp == stdout); // expected-warning {{TRUE}} no-FALSE 524 clang_analyzer_eval(oldStdout == stdout); // expected-warning {{UNKNOWN}} 525 } 526 527 void only_success_path_does_not_alias_with_stdout(void) { 528 if (stdout) return; 529 FILE *f = fopen("/tmp/foof", "r"); // no-crash 530 if (!f) return; 531 fclose(f); 532 } 533