// RUN: %clang_analyze_cc1 -triple=x86_64-pc-linux-gnu -analyzer-checker=core,unix.Stream,debug.ExprInspection \
// RUN: -analyzer-config eagerly-assume=false,unix.Stream:Pedantic=true -verify %s
// RUN: %clang_analyze_cc1 -triple=armv8-none-linux-eabi -analyzer-checker=core,unix.Stream,debug.ExprInspection \
// RUN: -analyzer-config eagerly-assume=false,unix.Stream:Pedantic=true -verify %s
// RUN: %clang_analyze_cc1 -triple=aarch64-linux-gnu -analyzer-checker=core,unix.Stream,debug.ExprInspection \
// RUN: -analyzer-config eagerly-assume=false,unix.Stream:Pedantic=true -verify %s
// RUN: %clang_analyze_cc1 -triple=hexagon -analyzer-checker=core,unix.Stream,debug.ExprInspection \
// RUN: -analyzer-config eagerly-assume=false,unix.Stream:Pedantic=true -verify %s
#include "Inputs/system-header-simulator.h"
#include "Inputs/system-header-simulator-for-malloc.h"
#include "Inputs/system-header-simulator-for-valist.h"
void clang_analyzer_eval(int);
void check_fread(void) {
FILE *fp = tmpfile();
fread(0, 0, 0, fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_fwrite(void) {
FILE *fp = tmpfile();
fwrite(0, 0, 0, fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_fgetc(void) {
FILE *fp = tmpfile();
fgetc(fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_fgets(void) {
FILE *fp = tmpfile();
char buf[256];
fgets(buf, sizeof(buf), fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_fputc(void) {
FILE *fp = tmpfile();
fputc('A', fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_fputs(void) {
FILE *fp = tmpfile();
fputs("ABC", fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_fprintf(void) {
FILE *fp = tmpfile();
fprintf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_fscanf(void) {
FILE *fp = tmpfile();
fscanf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_ungetc(void) {
FILE *fp = tmpfile();
ungetc('A', fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_fseek(void) {
FILE *fp = tmpfile();
fseek(fp, 0, 0); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_fseeko(void) {
FILE *fp = tmpfile();
fseeko(fp, 0, 0); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_ftell(void) {
FILE *fp = tmpfile();
ftell(fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_ftello(void) {
FILE *fp = tmpfile();
ftello(fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_rewind(void) {
FILE *fp = tmpfile();
rewind(fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_fgetpos(void) {
FILE *fp = tmpfile();
fpos_t pos;
fgetpos(fp, &pos); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_fsetpos(void) {
FILE *fp = tmpfile();
fpos_t pos;
fsetpos(fp, &pos); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_clearerr(void) {
FILE *fp = tmpfile();
clearerr(fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_feof(void) {
FILE *fp = tmpfile();
feof(fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_ferror(void) {
FILE *fp = tmpfile();
ferror(fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void check_fileno(void) {
FILE *fp = tmpfile();
fileno(fp); // expected-warning {{Stream pointer might be NULL}}
fclose(fp);
}
void f_open(void) {
FILE *p = fopen("foo", "r");
char buf[1024];
fread(buf, 1, 1, p); // expected-warning {{Stream pointer might be NULL}}
fclose(p);
}
void f_dopen(int fd) {
FILE *F = fdopen(fd, "r");
char buf[1024];
fread(buf, 1, 1, F); // expected-warning {{Stream pointer might be NULL}}
fclose(F);
}
void f_vfprintf(int fd, va_list args) {
FILE *F = fdopen(fd, "r");
vfprintf(F, "%d", args); // expected-warning {{Stream pointer might be NULL}}
fclose(F);
}
void f_vfscanf(int fd, va_list args) {
FILE *F = fdopen(fd, "r");
vfscanf(F, "%u", args); // expected-warning {{Stream pointer might be NULL}}
fclose(F);
}
void f_seek(void) {
FILE *p = fopen("foo", "r");
if (!p)
return;
fseek(p, 1, SEEK_SET); // no-warning
fseek(p, 1, 3); // expected-warning {{The whence argument to fseek() should be SEEK_SET, SEEK_END, or SEEK_CUR}}
fclose(p);
}
void f_seeko(void) {
FILE *p = fopen("foo", "r");
if (!p)
return;
fseeko(p, 1, SEEK_SET); // no-warning
fseeko(p, 1, 3); // expected-warning {{The whence argument to fseek() should be SEEK_SET, SEEK_END, or SEEK_CUR}}
fclose(p);
}
void f_double_close(void) {
FILE *p = fopen("foo", "r");
if (!p)
return;
fclose(p);
fclose(p); // expected-warning {{Stream might be already closed}}
}
void f_double_close_alias(void) {
FILE *p1 = fopen("foo", "r");
if (!p1)
return;
FILE *p2 = p1;
fclose(p1);
fclose(p2); // expected-warning {{Stream might be already closed}}
}
void f_use_after_close(void) {
FILE *p = fopen("foo", "r");
if (!p)
return;
fclose(p);
clearerr(p); // expected-warning {{Stream might be already closed}}
}
void f_open_after_close(void) {
FILE *p = fopen("foo", "r");
if (!p)
return;
fclose(p);
p = fopen("foo", "r");
if (!p)
return;
fclose(p);
}
void f_reopen_after_close(void) {
FILE *p = fopen("foo", "r");
if (!p)
return;
fclose(p);
// Allow reopen after close.
p = freopen("foo", "w", p);
if (!p)
return;
fclose(p);
}
void f_leak(int c) {
FILE *p = fopen("foo.c", "r");
if (!p)
return;
if(c)
return; // expected-warning {{Opened stream never closed. Potential resource leak}}
fclose(p);
}
FILE *f_null_checked(void) {
FILE *p = fopen("foo.c", "r");
if (p)
return p; // no-warning
else
return 0;
}
void pr7831(FILE *fp) {
fclose(fp); // no-warning
}
// PR 8081 - null pointer crash when 'whence' is not an integer constant
void pr8081(FILE *stream, long offset, int whence) {
fseek(stream, offset, whence);
}
void check_freopen_1(void) {
FILE *f1 = freopen("foo.c", "r", (FILE *)0); // expected-warning {{Stream pointer might be NULL}}
f1 = freopen(0, "w", (FILE *)0x123456); // Do not report this as error.
}
void check_freopen_2(void) {
FILE *f1 = fopen("foo.c", "r");
if (f1) {
FILE *f2 = freopen(0, "w", f1);
if (f2) {
// Check if f1 and f2 point to the same stream.
fclose(f1);
fclose(f2); // expected-warning {{Stream might be already closed.}}
} else {
// Reopen failed.
// f1 is non-NULL but points to a possibly invalid stream.
rewind(f1); // expected-warning {{Stream might be invalid}}
// f2 is NULL but the previous error stops the checker.
rewind(f2);
}
}
}
void check_freopen_3(void) {
FILE *f1 = fopen("foo.c", "r");
if (f1) {
// Unchecked result of freopen.
// The f1 may be invalid after this call.
freopen(0, "w", f1);
rewind(f1); // expected-warning {{Stream might be invalid}}
fclose(f1);
}
}
extern FILE *GlobalF;
extern void takeFile(FILE *);
void check_escape1(void) {
FILE *F = tmpfile();
if (!F)
return;
fwrite("1", 1, 1, F); // may fail
GlobalF = F;
fwrite("1", 1, 1, F); // no warning
}
void check_escape2(void) {
FILE *F = tmpfile();
if (!F)
return;
fwrite("1", 1, 1, F); // may fail
takeFile(F);
fwrite("1", 1, 1, F); // no warning
}
void check_escape3(void) {
FILE *F = tmpfile();
if (!F)
return;
takeFile(F);
F = freopen(0, "w", F);
if (!F)
return;
fwrite("1", 1, 1, F); // may fail
fwrite("1", 1, 1, F); // no warning
}
void check_escape4(void) {
FILE *F = tmpfile();
if (!F)
return;
fwrite("1", 1, 1, F); // may fail
// no escape at a non-StreamChecker-handled system call
setbuf(F, "0");
fwrite("1", 1, 1, F); // expected-warning {{might be 'indeterminate'}}
fclose(F);
}
int Test;
_Noreturn void handle_error(void);
void check_leak_noreturn_1(void) {
FILE *F1 = tmpfile();
if (!F1)
return;
if (Test == 1) {
handle_error(); // no warning
}
rewind(F1);
} // expected-warning {{Opened stream never closed. Potential resource leak}}
// Check that "location uniqueing" works.
// This results in reporting only one occurence of resource leak for a stream.
void check_leak_noreturn_2(void) {
FILE *F1 = tmpfile();
if (!F1)
return;
if (Test == 1) {
return; // no warning
}
rewind(F1);
} // expected-warning {{Opened stream never closed. Potential resource leak}}
// FIXME: This warning should be placed at the `return` above.
// See https://reviews.llvm.org/D83120 about details.
void fflush_after_fclose(void) {
FILE *F = tmpfile();
int Ret;
fflush(NULL); // no-warning
if (!F)
return;
if ((Ret = fflush(F)) != 0)
clang_analyzer_eval(Ret == EOF); // expected-warning {{TRUE}}
fclose(F);
fflush(F); // expected-warning {{Stream might be already closed}}
}
void fflush_on_open_failed_stream(void) {
FILE *F = tmpfile();
if (!F) {
fflush(F); // no-warning
return;
}
fclose(F);
}
void getline_null_file() {
char *buffer = NULL;
size_t n = 0;
getline(&buffer, &n, NULL); // expected-warning {{Stream pointer might be NULL}}
}
void getdelim_null_file() {
char *buffer = NULL;
size_t n = 0;
getdelim(&buffer, &n, '\n', NULL); // expected-warning {{Stream pointer might be NULL}}
}
void getline_buffer_on_error() {
FILE *file = fopen("file.txt", "r");
if (file == NULL) {
return;
}
char *line = NULL;
size_t len = 0;
if (getline(&line, &len, file) == -1) {
if (line[0] == '\0') {} // expected-warning {{The left operand of '==' is a garbage value}}
} else {
if (line[0] == '\0') {} // no warning
}
free(line);
fclose(file);
}
void getline_ret_value() {
FILE *file = fopen("file.txt", "r");
if (file == NULL) {
return;
}
size_t n = 0;
char *buffer = NULL;
ssize_t r = getline(&buffer, &n, file);
if (r > -1) {
// The return value does *not* include the terminating null byte.
// The buffer must be large enough to include it.
clang_analyzer_eval(n > r); // expected-warning{{TRUE}}
clang_analyzer_eval(buffer != NULL); // expected-warning{{TRUE}}
}
fclose(file);
free(buffer);
}
void getline_buffer_size_negative() {
FILE *file = fopen("file.txt", "r");
if (file == NULL) {
return;
}
size_t n = -1;
clang_analyzer_eval((ssize_t)n >= 0); // expected-warning{{FALSE}}
char *buffer = NULL;
ssize_t r = getline(&buffer, &n, file);
if (r > -1) {
clang_analyzer_eval((ssize_t)n > r); // expected-warning{{TRUE}}
clang_analyzer_eval(buffer != NULL); // expected-warning{{TRUE}}
}
free(buffer);
fclose(file);
}
void gh_93408_regression(void *buffer) {
FILE *f = fopen("/tmp/foo.txt", "r");
fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} no-crash
fclose(f);
}
typedef void VOID;
void gh_93408_regression_typedef(VOID *buffer) {
FILE *f = fopen("/tmp/foo.txt", "r");
fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} no-crash
fclose(f);
}
struct FAM {
int data;
int tail[];
};
struct FAM0 {
int data;
int tail[0];
};
void gh_93408_regression_FAM(struct FAM *p) {
FILE *f = fopen("/tmp/foo.txt", "r");
fread(p->tail, 1, 1, f); // expected-warning {{Stream pointer might be NULL}}
fclose(f);
}
void gh_93408_regression_FAM0(struct FAM0 *p) {
FILE *f = fopen("/tmp/foo.txt", "r");
fread(p->tail, 1, 1, f); // expected-warning {{Stream pointer might be NULL}}
fclose(f);
}
struct ZeroSized {
int data[0];
};
void gh_93408_regression_ZeroSized(struct ZeroSized *buffer) {
FILE *f = fopen("/tmp/foo.txt", "r");
fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} no-crash
fclose(f);
}
extern FILE *non_standard_stream_ptr;
void test_fopen_does_not_alias_with_standard_streams(void) {
FILE *f = fopen("file", "r");
if (!f) return;
clang_analyzer_eval(f == stdin); // expected-warning {{FALSE}} no-TRUE
clang_analyzer_eval(f == stdout); // expected-warning {{FALSE}} no-TRUE
clang_analyzer_eval(f == stderr); // expected-warning {{FALSE}} no-TRUE
clang_analyzer_eval(f == non_standard_stream_ptr); // expected-warning {{UNKNOWN}}
if (f != stdout) {
fclose(f);
}
} // no-leak: 'fclose()' is always called because 'f' cannot be 'stdout'.
void reopen_std_stream(void) {
FILE *oldStdout = stdout;
fclose(stdout);
FILE *fp = fopen("blah", "w");
if (!fp) return;
stdout = fp; // Let's make them alias.
clang_analyzer_eval(fp == oldStdout); // expected-warning {{UNKNOWN}}
clang_analyzer_eval(fp == stdout); // expected-warning {{TRUE}} no-FALSE
clang_analyzer_eval(oldStdout == stdout); // expected-warning {{UNKNOWN}}
}
void only_success_path_does_not_alias_with_stdout(void) {
if (stdout) return;
FILE *f = fopen("/tmp/foof", "r"); // no-crash
if (!f) return;
fclose(f);
}