555 lines · c
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 %s3// 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 %s5// 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 %s7// 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 %s9 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 14void clang_analyzer_eval(int);15 16void 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 22void 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 28void check_fgetc(void) {29 FILE *fp = tmpfile();30 fgetc(fp); // expected-warning {{Stream pointer might be NULL}}31 fclose(fp);32}33 34void 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 41void check_fputc(void) {42 FILE *fp = tmpfile();43 fputc('A', fp); // expected-warning {{Stream pointer might be NULL}}44 fclose(fp);45}46 47void check_fputs(void) {48 FILE *fp = tmpfile();49 fputs("ABC", fp); // expected-warning {{Stream pointer might be NULL}}50 fclose(fp);51}52 53void check_fprintf(void) {54 FILE *fp = tmpfile();55 fprintf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}}56 fclose(fp);57}58 59void check_fscanf(void) {60 FILE *fp = tmpfile();61 fscanf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}}62 fclose(fp);63}64 65void check_ungetc(void) {66 FILE *fp = tmpfile();67 ungetc('A', fp); // expected-warning {{Stream pointer might be NULL}}68 fclose(fp);69}70 71void check_fseek(void) {72 FILE *fp = tmpfile();73 fseek(fp, 0, 0); // expected-warning {{Stream pointer might be NULL}}74 fclose(fp);75}76 77void check_fseeko(void) {78 FILE *fp = tmpfile();79 fseeko(fp, 0, 0); // expected-warning {{Stream pointer might be NULL}}80 fclose(fp);81}82 83void check_ftell(void) {84 FILE *fp = tmpfile();85 ftell(fp); // expected-warning {{Stream pointer might be NULL}}86 fclose(fp);87}88 89void check_ftello(void) {90 FILE *fp = tmpfile();91 ftello(fp); // expected-warning {{Stream pointer might be NULL}}92 fclose(fp);93}94 95void check_rewind(void) {96 FILE *fp = tmpfile();97 rewind(fp); // expected-warning {{Stream pointer might be NULL}}98 fclose(fp);99}100 101void 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 108void 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 115void check_clearerr(void) {116 FILE *fp = tmpfile();117 clearerr(fp); // expected-warning {{Stream pointer might be NULL}}118 fclose(fp);119}120 121void check_feof(void) {122 FILE *fp = tmpfile();123 feof(fp); // expected-warning {{Stream pointer might be NULL}}124 fclose(fp);125}126 127void check_ferror(void) {128 FILE *fp = tmpfile();129 ferror(fp); // expected-warning {{Stream pointer might be NULL}}130 fclose(fp);131}132 133void check_fileno(void) {134 FILE *fp = tmpfile();135 fileno(fp); // expected-warning {{Stream pointer might be NULL}}136 fclose(fp);137}138 139void 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 146void 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 153void 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 159void 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 165void f_seek(void) {166 FILE *p = fopen("foo", "r");167 if (!p)168 return;169 fseek(p, 1, SEEK_SET); // no-warning170 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 174void f_seeko(void) {175 FILE *p = fopen("foo", "r");176 if (!p)177 return;178 fseeko(p, 1, SEEK_SET); // no-warning179 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 183void 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 191void 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 200void 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 208void 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 219void 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 231void 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 240FILE *f_null_checked(void) {241 FILE *p = fopen("foo.c", "r");242 if (p)243 return p; // no-warning244 else245 return 0;246}247 248void pr7831(FILE *fp) {249 fclose(fp); // no-warning250}251 252// PR 8081 - null pointer crash when 'whence' is not an integer constant253void pr8081(FILE *stream, long offset, int whence) {254 fseek(stream, offset, whence);255}256 257void 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 262void 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 280void 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 291extern FILE *GlobalF;292extern void takeFile(FILE *);293 294void check_escape1(void) {295 FILE *F = tmpfile();296 if (!F)297 return;298 fwrite("1", 1, 1, F); // may fail299 GlobalF = F;300 fwrite("1", 1, 1, F); // no warning301}302 303void check_escape2(void) {304 FILE *F = tmpfile();305 if (!F)306 return;307 fwrite("1", 1, 1, F); // may fail308 takeFile(F);309 fwrite("1", 1, 1, F); // no warning310}311 312void 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 fail321 fwrite("1", 1, 1, F); // no warning322}323 324void check_escape4(void) {325 FILE *F = tmpfile();326 if (!F)327 return;328 fwrite("1", 1, 1, F); // may fail329 330 // no escape at a non-StreamChecker-handled system call331 setbuf(F, "0");332 333 fwrite("1", 1, 1, F); // expected-warning {{might be 'indeterminate'}}334 fclose(F);335}336 337int Test;338_Noreturn void handle_error(void);339 340void check_leak_noreturn_1(void) {341 FILE *F1 = tmpfile();342 if (!F1)343 return;344 if (Test == 1) {345 handle_error(); // no warning346 }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.352void check_leak_noreturn_2(void) {353 FILE *F1 = tmpfile();354 if (!F1)355 return;356 if (Test == 1) {357 return; // no warning358 }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 364void fflush_after_fclose(void) {365 FILE *F = tmpfile();366 int Ret;367 fflush(NULL); // no-warning368 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 376void fflush_on_open_failed_stream(void) {377 FILE *F = tmpfile();378 if (!F) {379 fflush(F); // no-warning380 return;381 }382 fclose(F);383}384 385void 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 391void 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 397void 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 warning409 }410 411 free(line);412 fclose(file);413}414 415void 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 437void 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 457void 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-crash460 fclose(f);461}462 463typedef void VOID;464void 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-crash467 fclose(f);468}469 470struct FAM {471 int data;472 int tail[];473};474 475struct FAM0 {476 int data;477 int tail[0];478};479 480void 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 486void 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 492struct ZeroSized {493 int data[0];494};495 496void 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-crash499 fclose(f);500}501 502extern FILE *non_standard_stream_ptr;503void 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-TRUE507 clang_analyzer_eval(f == stdout); // expected-warning {{FALSE}} no-TRUE508 clang_analyzer_eval(f == stderr); // expected-warning {{FALSE}} no-TRUE509 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 515void 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 {{FALSE}}523 clang_analyzer_eval(fp == stdout); // expected-warning {{TRUE}}524 clang_analyzer_eval(oldStdout == stdout); // expected-warning {{FALSE}}525}526 527void only_success_path_does_not_alias_with_stdout(void) {528 if (stdout) return;529 FILE *f = fopen("/tmp/foof", "r"); // no-crash530 clang_analyzer_eval(f == 0);// expected-warning {{TRUE}} expected-warning {{FALSE}}531 if (!f) return;532 fclose(f);533}534 535extern void do_something();536 537void test_no_invalidate_at_system_call() {538 FILE *old_stdin = stdin;539 if ((stdin = fopen("x/y/z", "r")) == NULL)540 return;541 clang_analyzer_eval(old_stdin == stdin); // expected-warning{{FALSE}}542 free(malloc(100));543 clang_analyzer_eval(old_stdin == stdin); // expected-warning{{FALSE}}544 fclose(stdin);545}546 547void test_invalidate_at_non_system_call() {548 FILE *old_stdin = stdin;549 if ((stdin = fopen("x/y/z", "r")) == NULL)550 return;551 clang_analyzer_eval(old_stdin == stdin); // expected-warning{{FALSE}}552 do_something();553 clang_analyzer_eval(old_stdin == stdin); // expected-warning{{UNKNOWN}}554}555