class StreamChecker : public Checker<check::PreCall, eval::Call,
check::DeadSymbols, check::PointerEscape> {
+ BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
BugType BT_UseAfterOpenFailed{this, "Invalid stream",
"Stream handling error"};
const StreamErrorState &ErrorKind) const;
/// Check that the stream (in StreamVal) is not NULL.
- /// If it can only be NULL a sink node is generated and nullptr returned.
+ /// If it can only be NULL a fatal error is emitted and nullptr returned.
/// Otherwise the return value is a new state where the stream is constrained
/// to be non-null.
ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream);
if (!StateNotNull && StateNull) {
- // Stream argument is NULL, stop analysis on this path.
- // This case should occur only if StdLibraryFunctionsChecker (or ModelPOSIX
- // option of it) is not turned on, otherwise that checker ensures non-null
- // argument.
- C.generateSink(StateNull, C.getPredecessor());
+ if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ BT_FileNull, "Stream pointer might be NULL.", N);
+ if (StreamE)
+ bugreporter::trackExpressionValue(N, StreamE, *R);
+ C.emitReport(std::move(R));
+ }
return nullptr;
}
// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.unix.Stream,alpha.unix.StdCLibraryFunctions,debug.ExprInspection \
-// RUN: -analyzer-config alpha.unix.StdCLibraryFunctions:ModelPOSIX=true -verify=stdargs,any %s
+// RUN: -analyzer-config alpha.unix.StdCLibraryFunctions:ModelPOSIX=true -verify=stream,any %s
// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.unix.Stream,debug.ExprInspection \
-// RUN: -analyzer-config alpha.unix.StdCLibraryFunctions:ModelPOSIX=true -verify=any %s
+// RUN: -analyzer-config alpha.unix.StdCLibraryFunctions:ModelPOSIX=true -verify=stream,any %s
// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.unix.StdCLibraryFunctions,debug.ExprInspection \
-// RUN: -analyzer-config alpha.unix.StdCLibraryFunctions:ModelPOSIX=true -verify=stdargs,any %s
+// RUN: -analyzer-config alpha.unix.StdCLibraryFunctions:ModelPOSIX=true -verify=stdfunc,any %s
#include "Inputs/system-header-simulator.h"
void test_fopen(void) {
FILE *fp = fopen("path", "r");
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} any-warning{{FALSE}}
- fclose(fp); // stdargs-warning{{should not be NULL}}
+ fclose(fp); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
}
void test_tmpfile(void) {
FILE *fp = tmpfile();
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} any-warning{{FALSE}}
- fclose(fp); // stdargs-warning{{should not be NULL}}
+ fclose(fp); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
}
void test_fclose(void) {
FILE *fp = tmpfile();
- fclose(fp); // stdargs-warning{{should not be NULL}}
+ fclose(fp); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}}
}
void test_freopen(void) {
FILE *fp = tmpfile();
- fp = freopen("file", "w", fp); // stdargs-warning{{should not be NULL}}
- fclose(fp); // stdargs-warning{{should not be NULL}}
+ fp = freopen("file", "w", fp); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
+ fclose(fp); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}}
}
void test_fread(void) {
FILE *fp = tmpfile();
- size_t ret = fread(buf, size, n, fp); // stdargs-warning{{The 4th argument to 'fread' is NULL but should not be NULL}}
+ size_t ret = fread(buf, size, n, fp); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{The 4th argument to 'fread' is NULL but should not be NULL}}
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}}
clang_analyzer_eval(ret <= n); // any-warning{{TRUE}}
clang_analyzer_eval(ret == n); // any-warning{{TRUE}} any-warning{{FALSE}}
void test_fwrite(void) {
FILE *fp = tmpfile();
- size_t ret = fwrite(buf, size, n, fp); // stdargs-warning{{The 4th argument to 'fwrite' is NULL but should not be NULL}}
+ size_t ret = fwrite(buf, size, n, fp); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{The 4th argument to 'fwrite' is NULL but should not be NULL}}
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}}
clang_analyzer_eval(ret <= n); // any-warning{{TRUE}}
clang_analyzer_eval(ret == n); // any-warning{{TRUE}} any-warning{{FALSE}}
void test_fseek(void) {
FILE *fp = tmpfile();
- fseek(fp, 0, 0); // stdargs-warning{{should not be NULL}}
+ fseek(fp, 0, 0); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}}
fclose(fp);
}
void test_ftell(void) {
FILE *fp = tmpfile();
- ftell(fp); // stdargs-warning{{should not be NULL}}
+ ftell(fp); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}}
fclose(fp);
}
void test_rewind(void) {
FILE *fp = tmpfile();
- rewind(fp); // stdargs-warning{{should not be NULL}}
+ rewind(fp); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}}
fclose(fp);
}
void test_fgetpos(void) {
FILE *fp = tmpfile();
fpos_t pos;
- fgetpos(fp, &pos); // stdargs-warning{{should not be NULL}}
+ fgetpos(fp, &pos); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}}
fclose(fp);
}
void test_fsetpos(void) {
FILE *fp = tmpfile();
fpos_t pos;
- fsetpos(fp, &pos); // stdargs-warning{{should not be NULL}}
+ fsetpos(fp, &pos); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}}
fclose(fp);
}
void test_clearerr(void) {
FILE *fp = tmpfile();
- clearerr(fp); // stdargs-warning{{should not be NULL}}
+ clearerr(fp); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}}
fclose(fp);
}
void test_feof(void) {
FILE *fp = tmpfile();
- feof(fp); // stdargs-warning{{should not be NULL}}
+ feof(fp); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}}
fclose(fp);
}
void test_ferror(void) {
FILE *fp = tmpfile();
- ferror(fp); // stdargs-warning{{should not be NULL}}
+ ferror(fp); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}}
fclose(fp);
}
void test_fileno(void) {
FILE *fp = tmpfile();
- fileno(fp); // stdargs-warning{{should not be NULL}}
+ fileno(fp); // \
+ // stream-warning{{Stream pointer might be NULL}} \
+ // stdfunc-warning{{should not be NULL}}
clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}}
fclose(fp);
}
#include "Inputs/system-header-simulator.h"
+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_fseek(void) {
+ FILE *fp = tmpfile();
+ fseek(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_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_seek(void) {
FILE *p = fopen("foo", "r");
if (!p)
}
void check_freopen_1(void) {
- FILE *f1 = freopen("foo.c", "r", (FILE *)0); // Not reported by the stream checker.
+ 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.
}