[clang] 39670ae - [clang][analyzer] Add and change NoteTags in StdLibraryFunctionsChecker.
Balázs Kéri via cfe-commits
cfe-commits at lists.llvm.org
Tue Jul 18 00:29:48 PDT 2023
Author: Balázs Kéri
Date: 2023-07-18T09:29:15+02:00
New Revision: 39670ae3b93470b2d29fe78e6d40c5d82a05e4a1
URL: https://github.com/llvm/llvm-project/commit/39670ae3b93470b2d29fe78e6d40c5d82a05e4a1
DIFF: https://github.com/llvm/llvm-project/commit/39670ae3b93470b2d29fe78e6d40c5d82a05e4a1.diff
LOG: [clang][analyzer] Add and change NoteTags in StdLibraryFunctionsChecker.
Change 1: ErrnoChecker notes show only messages related to errno,
not to assumption of success or failure of functions.
Change 2: StdLibraryFunctionsChecker adds its own note about success
or failure of functions, and the errno related note, independently.
Change 3: Every modeled function in StdLibraryFunctionsChecker
should have a note tag message in all "cases". This is not implemented yet,
only for file (stream) related functions.
Reviewed By: donat.nagy
Differential Revision: https://reviews.llvm.org/D153612
Added:
Modified:
clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
clang/test/Analysis/errno-stdlibraryfunctions-notes.c
clang/test/Analysis/std-c-library-functions-arg-constraints-note-tags.cpp
clang/test/Analysis/std-c-library-functions-arg-constraints.c
clang/test/Analysis/std-c-library-functions-path-notes.c
clang/test/Analysis/stream-errno-note.c
clang/test/Analysis/stream-note.c
Removed:
################################################################################
diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
index 51f39c606d5c12..be2fa91b994a26 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
@@ -28,6 +28,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/FormatVariadic.h"
#include <optional>
using namespace clang;
@@ -269,12 +270,6 @@ bool isErrno(const Decl *D) {
return false;
}
-const char *describeErrnoCheckState(ErrnoCheckState CS) {
- assert(CS == errno_modeling::MustNotBeChecked &&
- "Errno description not applicable.");
- return "may be undefined after the call and should not be used";
-}
-
const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {
const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();
@@ -319,18 +314,14 @@ ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State,
const NoteTag *getNoteTagForStdSuccess(CheckerContext &C, llvm::StringRef Fn) {
return getErrnoNoteTag(
- C, (Twine("Assuming that function '") + Twine(Fn) +
- Twine("' is successful, in this case the value 'errno' ") +
- Twine(describeErrnoCheckState(MustNotBeChecked)))
- .str());
+ C, llvm::formatv(
+ "'errno' may be undefined after successful call to '{0}'", Fn));
}
const NoteTag *getNoteTagForStdMustBeChecked(CheckerContext &C,
llvm::StringRef Fn) {
return getErrnoNoteTag(
- C, (Twine("Function '") + Twine(Fn) +
- Twine("' indicates failure only by setting of 'errno'"))
- .str());
+ C, llvm::formatv("'{0}' indicates failure only by setting 'errno'", Fn));
}
} // namespace errno_modeling
diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
index 2ca3979944e365..0707fd16d6e60a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
@@ -78,14 +78,6 @@ ProgramStateRef clearErrnoState(ProgramStateRef State);
/// declaration.
bool isErrno(const Decl *D);
-/// Produce a textual description about how \c errno is allowed to be used
-/// (in a \c ErrnoCheckState).
-/// The returned string is insertable into a longer warning message in the form
-/// "the value 'errno' <...>".
-/// Currently only the \c errno_modeling::MustNotBeChecked state is supported,
-/// others are not used by the clients.
-const char *describeErrnoCheckState(ErrnoCheckState CS);
-
/// Create a NoteTag that displays the message if the 'errno' memory region is
/// marked as interesting, and resets the interestingness.
const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message);
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index 0228e8278f057a..8d4fb12061c3d6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -52,6 +52,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/FormatVariadic.h"
#include <optional>
#include <string>
@@ -1273,7 +1274,7 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
// Now apply the constraints.
const Summary &Summary = *FoundSummary;
ProgramStateRef State = C.getState();
- const ExplodedNode *Node = C.getPredecessor();
+ ExplodedNode *Node = C.getPredecessor();
// Apply case/branch specifications.
for (const SummaryCase &Case : Summary.getCases()) {
@@ -1287,35 +1288,59 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
if (NewState)
NewState = Case.getErrnoConstraint().apply(NewState, Call, Summary, C);
- if (NewState && NewState != State) {
- if (Case.getNote().empty()) {
- const NoteTag *NT = nullptr;
- if (const auto *D = dyn_cast_or_null<FunctionDecl>(Call.getDecl()))
- NT = Case.getErrnoConstraint().describe(C, D->getNameAsString());
- C.addTransition(NewState, NT);
- } else {
- StringRef Note = Case.getNote();
+ if (!NewState)
+ continue;
+
+ // It is possible that NewState == State is true.
+ // It can occur if another checker has applied the state before us.
+ // Still add these note tags, the other checker should add only its
+ // specialized note tags. These general note tags are handled always by
+ // StdLibraryFunctionsChecker.
+ ExplodedNode *Pred = Node;
+ if (!Case.getNote().empty()) {
+ // If there is a description for this execution branch (summary case),
+ // use it as a note tag.
+ std::string Note =
+ llvm::formatv(Case.getNote().str().c_str(),
+ cast<NamedDecl>(Call.getDecl())->getDeclName());
+ if (Summary.getInvalidationKd() == EvalCallAsPure) {
const NoteTag *Tag = C.getNoteTag(
- // Sorry couldn't help myself.
- [Node, Note]() -> std::string {
- // Don't emit "Assuming..." note when we ended up
- // knowing in advance which branch is taken.
- return (Node->succ_size() > 1) ? Note.str() : "";
+ [Node, Note](PathSensitiveBugReport &BR) -> std::string {
+ // Try to omit the note if we know in advance which branch is
+ // taken (this means, only one branch exists).
+ // This check is performed inside the lambda, after other
+ // (or this) checkers had a chance to add other successors.
+ // Dereferencing the saved node object is valid because it's part
+ // of a bug report call sequence.
+ // FIXME: This check is not exact. We may be here after a state
+ // split that was performed by another checker (and can not find
+ // the successors). This is why this check is only used in the
+ // EvalCallAsPure case.
+ if (Node->succ_size() > 1)
+ return Note;
+ return "";
},
/*IsPrunable=*/true);
- C.addTransition(NewState, Tag);
+ Pred = C.addTransition(NewState, Pred, Tag);
+ } else {
+ const NoteTag *Tag = C.getNoteTag(Note, /*IsPrunable=*/true);
+ Pred = C.addTransition(NewState, Pred, Tag);
}
- } else if (NewState == State) {
- // It is possible that the function was evaluated in a checker callback
- // where the state constraints are already applied, then no change happens
- // here to the state (if the ErrnoConstraint did not change it either).
- // If the evaluated function requires a NoteTag for errno change, it is
- // added here.
- if (const auto *D = dyn_cast_or_null<FunctionDecl>(Call.getDecl()))
- if (const NoteTag *NT =
- Case.getErrnoConstraint().describe(C, D->getNameAsString()))
- C.addTransition(NewState, NT);
+ if (!Pred)
+ break;
}
+
+ // If we can get a note tag for the errno change, add this additionally to
+ // the previous. This note is only about value of 'errno' and is displayed
+ // if 'errno' is interesting.
+ if (const auto *D = dyn_cast<FunctionDecl>(Call.getDecl()))
+ if (const NoteTag *NT =
+ Case.getErrnoConstraint().describe(C, D->getNameAsString()))
+ Pred = C.addTransition(NewState, Pred, NT);
+
+ // Add the transition if no note tag could be added.
+ if (Pred == Node && NewState != State)
+ C.addTransition(NewState);
}
}
@@ -1660,6 +1685,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
std::optional<QualType> ConstFPosTPtrTy = getPointerTy(getConstTy(FPosTTy));
std::optional<QualType> FPosTPtrRestrictTy = getRestrictTy(FPosTPtrTy);
+ constexpr llvm::StringLiteral GenericSuccessMsg(
+ "Assuming that '{0}' is successful");
+ constexpr llvm::StringLiteral GenericFailureMsg("Assuming that '{0}' fails");
+
// We are finally ready to define specifications for all supported functions.
//
// Argument ranges should always cover all variants. If return value
@@ -1892,14 +1921,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
ArgumentCondition(2U, WithinRange, Range(1, SizeMax)),
ReturnValueCondition(BO_LT, ArgNo(2)),
ReturnValueCondition(WithinRange, Range(0, SizeMax))},
- ErrnoNEZeroIrrelevant)
+ ErrnoNEZeroIrrelevant, GenericFailureMsg)
.Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)),
ReturnValueCondition(BO_EQ, ArgNo(2)),
ReturnValueCondition(WithinRange, Range(0, SizeMax))},
- ErrnoMustNotBeChecked)
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
.Case({ArgumentCondition(1U, WithinRange, SingleValue(0)),
ReturnValueCondition(WithinRange, SingleValue(0))},
- ErrnoMustNotBeChecked)
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(3)))
// FIXME: It should be allowed to have a null buffer if any of
@@ -2016,17 +2045,17 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy},
RetType{FilePtrTy}),
Summary(NoEvalCall)
- .Case({NotNull(Ret)}, ErrnoMustNotBeChecked)
- .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant)
+ .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
// FILE *tmpfile(void);
- addToFunctionSummaryMap("tmpfile",
- Signature(ArgTypes{}, RetType{FilePtrTy}),
- Summary(NoEvalCall)
- .Case({NotNull(Ret)}, ErrnoMustNotBeChecked)
- .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant));
+ addToFunctionSummaryMap(
+ "tmpfile", Signature(ArgTypes{}, RetType{FilePtrTy}),
+ Summary(NoEvalCall)
+ .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg));
// FILE *freopen(const char *restrict pathname, const char *restrict mode,
// FILE *restrict stream);
@@ -2037,8 +2066,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
RetType{FilePtrTy}),
Summary(NoEvalCall)
.Case({ReturnValueCondition(BO_EQ, ArgNo(2))},
- ErrnoMustNotBeChecked)
- .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant)
+ ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(1)))
.ArgConstraint(NotNull(ArgNo(2))));
@@ -2046,9 +2075,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
addToFunctionSummaryMap(
"fclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZero, ErrnoMustNotBeChecked)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
.Case({ReturnValueCondition(WithinRange, SingleValue(EOFv))},
- ErrnoNEZeroIrrelevant)
+ ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
// int fseek(FILE *stream, long offset, int whence);
@@ -2058,8 +2087,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
addToFunctionSummaryMap(
"fseek", Signature(ArgTypes{FilePtrTy, LongTy, IntTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZero, ErrnoMustNotBeChecked)
- .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(ArgumentCondition(2, WithinRange, {{0, 2}})));
@@ -2072,8 +2101,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{FilePtrRestrictTy, FPosTPtrRestrictTy},
RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZero, ErrnoUnchanged)
- .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant)
+ .Case(ReturnsZero, ErrnoUnchanged, GenericSuccessMsg)
+ .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
@@ -2085,8 +2114,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"fsetpos",
Signature(ArgTypes{FilePtrTy, ConstFPosTPtrTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZero, ErrnoUnchanged)
- .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant)
+ .Case(ReturnsZero, ErrnoUnchanged, GenericSuccessMsg)
+ .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
@@ -2098,16 +2127,17 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"ftell", Signature(ArgTypes{FilePtrTy}, RetType{LongTy}),
Summary(NoEvalCall)
.Case({ReturnValueCondition(WithinRange, Range(1, LongMax))},
- ErrnoUnchanged)
- .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
+ ErrnoUnchanged, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
// int fileno(FILE *stream);
addToFunctionSummaryMap(
"fileno", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked)
- .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
+ .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked,
+ GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
// void rewind(FILE *stream);
@@ -2149,8 +2179,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
addToFunctionSummaryMap(
"access", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}),
Summary(NoEvalCall)
- .Case(ReturnsZero, ErrnoMustNotBeChecked)
- .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));
// int faccessat(int dirfd, const char *pathname, int mode, int flags);
diff --git a/clang/test/Analysis/errno-stdlibraryfunctions-notes.c b/clang/test/Analysis/errno-stdlibraryfunctions-notes.c
index 4172935e1e3f6a..e520e4fa76497f 100644
--- a/clang/test/Analysis/errno-stdlibraryfunctions-notes.c
+++ b/clang/test/Analysis/errno-stdlibraryfunctions-notes.c
@@ -13,9 +13,11 @@ int access(const char *path, int amode);
void clang_analyzer_warnIfReached();
void test1() {
- access("path", 0); // no note here
access("path", 0);
- // expected-note at -1{{Assuming that function 'access' is successful, in this case the value 'errno' may be undefined after the call and should not be used}}
+ // expected-note at -1{{Assuming that 'access' fails}}
+ access("path", 0);
+ // expected-note at -1{{Assuming that 'access' is successful}}
+ // expected-note at -2{{'errno' may be undefined after successful call to 'access'}}
if (errno != 0) {
// expected-warning at -1{{An undefined value may be read from 'errno'}}
// expected-note at -2{{An undefined value may be read from 'errno'}}
@@ -24,7 +26,8 @@ void test1() {
void test2() {
if (access("path", 0) == -1) {
- // expected-note at -1{{Taking true branch}}
+ // expected-note at -1{{Assuming that 'access' fails}}
+ // expected-note at -2{{Taking true branch}}
// Failure path.
if (errno != 0) {
// expected-note at -1{{'errno' is not equal to 0}}
@@ -39,8 +42,9 @@ void test2() {
void test3() {
if (access("path", 0) != -1) {
// Success path.
- // expected-note at -2{{Assuming that function 'access' is successful, in this case the value 'errno' may be undefined after the call and should not be used}}
- // expected-note at -3{{Taking true branch}}
+ // expected-note at -2{{Assuming that 'access' is successful}}
+ // expected-note at -3{{'errno' may be undefined after successful call to 'access'}}
+ // expected-note at -4{{Taking true branch}}
if (errno != 0) {
// expected-warning at -1{{An undefined value may be read from 'errno'}}
// expected-note at -2{{An undefined value may be read from 'errno'}}
diff --git a/clang/test/Analysis/std-c-library-functions-arg-constraints-note-tags.cpp b/clang/test/Analysis/std-c-library-functions-arg-constraints-note-tags.cpp
index f31ce589072abc..fc7116d3e669ce 100644
--- a/clang/test/Analysis/std-c-library-functions-arg-constraints-note-tags.cpp
+++ b/clang/test/Analysis/std-c-library-functions-arg-constraints-note-tags.cpp
@@ -52,6 +52,7 @@ void test_buffer_size_note(char *buf, int y) {
int __test_case_note();
int test_case_note_1(int y) {
+ int x0 = __test_case_note(); // expected-note{{Function returns 1}}
int x = __test_case_note(); // expected-note{{Function returns 0}} \
// expected-note{{'x' initialized here}}
return y / x; // expected-warning{{Division by zero}} \
diff --git a/clang/test/Analysis/std-c-library-functions-arg-constraints.c b/clang/test/Analysis/std-c-library-functions-arg-constraints.c
index 6a5f9454fd1ee6..2fdea1fecd831e 100644
--- a/clang/test/Analysis/std-c-library-functions-arg-constraints.c
+++ b/clang/test/Analysis/std-c-library-functions-arg-constraints.c
@@ -170,7 +170,8 @@ void test_notnull_concrete(FILE *fp) {
// bugpath-note{{The 1st argument to 'fread' is NULL but should not be NULL}}
}
void test_notnull_symbolic(FILE *fp, int *buf) {
- fread(buf, sizeof(int), 10, fp);
+ fread(buf, sizeof(int), 10, fp); // \
+ // bugpath-note{{'fread' fails}}
clang_analyzer_eval(buf != 0); // \
// report-warning{{TRUE}} \
// bugpath-warning{{TRUE}} \
diff --git a/clang/test/Analysis/std-c-library-functions-path-notes.c b/clang/test/Analysis/std-c-library-functions-path-notes.c
index 98772fa6a11e59..33d6152376c57c 100644
--- a/clang/test/Analysis/std-c-library-functions-path-notes.c
+++ b/clang/test/Analysis/std-c-library-functions-path-notes.c
@@ -1,7 +1,9 @@
// RUN: %clang_analyze_cc1 -verify %s \
// RUN: -analyzer-checker=core,alpha.unix.StdCLibraryFunctions \
+// RUN: -analyzer-config alpha.unix.StdCLibraryFunctions:ModelPOSIX=true \
// RUN: -analyzer-output=text
+#include "Inputs/std-c-library-functions-POSIX.h"
#define NULL ((void *)0)
char *getenv(const char *);
@@ -19,10 +21,12 @@ char test_getenv() {
// expected-note {{Array access (from variable 'env') results in a null pointer dereference}}
}
-int test_isalpha(int *x, char c) {
+int test_isalpha(int *x, char c, char d) {
+ int iad = isalpha(d);// \
+ // expected-note{{Assuming the character is non-alphabetical}}
if (isalpha(c)) {// \
- // expected-note{{Assuming the character is alphabetical}} \
- // expected-note{{Taking true branch}}
+ // expected-note{{Taking true branch}} \
+ // expected-note{{Assuming the character is alphabetical}}
x = NULL; // \
// expected-note{{Null pointer value stored to 'x'}}
}
@@ -34,8 +38,8 @@ int test_isalpha(int *x, char c) {
int test_isdigit(int *x, char c) {
if (!isdigit(c)) {// \
- // expected-note{{Assuming the character is not a digit}} \
- // expected-note{{Taking true branch}}
+ // expected-note{{Assuming the character is not a digit}} \
+ // expected-note{{Taking true branch}}
x = NULL; // \
// expected-note{{Null pointer value stored to 'x'}}
}
@@ -58,3 +62,18 @@ int test_islower(int *x) {
// expected-warning{{Dereference of null pointer (loaded from variable 'x')}} \
// expected-note {{Dereference of null pointer (loaded from variable 'x')}}
}
+
+int test_bugpath_notes(FILE *f1, char c, FILE *f2) {
+ int f = fileno(f2); // \
+ // expected-note{{Assuming that 'fileno' is successful}}
+ if (f == -1) // \
+ // expected-note{{Taking false branch}}
+ return 0;
+ int l = islower(c);
+ f = fileno(f1); // \
+ // expected-note{{Value assigned to 'f'}} \
+ // expected-note{{Assuming that 'fileno' fails}}
+ return dup(f); // \
+ // expected-warning{{The 1st argument to 'dup' is -1 but should be >= 0}} \
+ // expected-note{{The 1st argument to 'dup' is -1 but should be >= 0}}
+}
diff --git a/clang/test/Analysis/stream-errno-note.c b/clang/test/Analysis/stream-errno-note.c
index 111841efe0e5a1..d8aefbabd0b81c 100644
--- a/clang/test/Analysis/stream-errno-note.c
+++ b/clang/test/Analysis/stream-errno-note.c
@@ -10,7 +10,8 @@
void check_fopen(void) {
FILE *F = fopen("xxx", "r");
- // expected-note at -1{{Assuming that function 'fopen' is successful, in this case the value 'errno' may be undefined after the call and should not be used}}
+ // expected-note at -1{{'errno' may be undefined after successful call to 'fopen'}}
+ // expected-note at -2{{'fopen' is successful}}
// expected-note at +2{{'F' is non-null}}
// expected-note at +1{{Taking false branch}}
if (!F)
@@ -22,7 +23,8 @@ void check_fopen(void) {
void check_tmpfile(void) {
FILE *F = tmpfile();
- // expected-note at -1{{Assuming that function 'tmpfile' is successful, in this case the value 'errno' may be undefined after the call and should not be used}}
+ // expected-note at -1{{'errno' may be undefined after successful call to 'tmpfile'}}
+ // expected-note at -2{{'tmpfile' is successful}}
// expected-note at +2{{'F' is non-null}}
// expected-note at +1{{Taking false branch}}
if (!F)
@@ -34,12 +36,14 @@ void check_tmpfile(void) {
void check_freopen(void) {
FILE *F = tmpfile();
+ // expected-note at -1{{'tmpfile' is successful}}
// expected-note at +2{{'F' is non-null}}
// expected-note at +1{{Taking false branch}}
if (!F)
return;
F = freopen("xxx", "w", F);
- // expected-note at -1{{Assuming that function 'freopen' is successful}}
+ // expected-note at -1{{'errno' may be undefined after successful call to 'freopen'}}
+ // expected-note at -2{{'freopen' is successful}}
// expected-note at +2{{'F' is non-null}}
// expected-note at +1{{Taking false branch}}
if (!F)
@@ -51,12 +55,14 @@ void check_freopen(void) {
void check_fclose(void) {
FILE *F = tmpfile();
+ // expected-note at -1{{'tmpfile' is successful}}
// expected-note at +2{{'F' is non-null}}
// expected-note at +1{{Taking false branch}}
if (!F)
return;
(void)fclose(F);
- // expected-note at -1{{Assuming that function 'fclose' is successful}}
+ // expected-note at -1{{'errno' may be undefined after successful call to 'fclose'}}
+ // expected-note at -2{{'fclose' is successful}}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
// expected-note at -1{{An undefined value may be read from 'errno'}}
}
@@ -64,26 +70,60 @@ void check_fclose(void) {
void check_fread(void) {
char Buf[10];
FILE *F = tmpfile();
+ // expected-note at -1{{'tmpfile' is successful}}
// expected-note at +2{{'F' is non-null}}
// expected-note at +1{{Taking false branch}}
if (!F)
return;
(void)fread(Buf, 1, 10, F);
- // expected-note at -1{{Assuming that function 'fread' is successful}}
+ // expected-note at -1{{'errno' may be undefined after successful call to 'fread'}}
+ // expected-note at -2{{'fread' is successful}}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
// expected-note at -1{{An undefined value may be read from 'errno'}}
(void)fclose(F);
}
+void check_fread_size0(void) {
+ char Buf[10];
+ FILE *F = tmpfile();
+ // expected-note at -1{{'tmpfile' is successful}}
+ // expected-note at +2{{'F' is non-null}}
+ // expected-note at +1{{Taking false branch}}
+ if (!F)
+ return;
+ fread(Buf, 0, 1, F);
+ // expected-note at -1{{'errno' may be undefined after successful call to 'fread'}}
+ // expected-note at -2{{'fread' is successful}}
+ if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
+ // expected-note at -1{{An undefined value may be read from 'errno'}}
+}
+
+void check_fread_nmemb0(void) {
+ char Buf[10];
+ FILE *F = tmpfile();
+ // expected-note at -1{{'tmpfile' is successful}}
+ // expected-note at +2{{'F' is non-null}}
+ // expected-note at +1{{Taking false branch}}
+ if (!F)
+ return;
+ fread(Buf, 1, 0, F);
+ // expected-note at -1{{'errno' may be undefined after successful call to 'fread'}}
+ // expected-note at -2{{'fread' is successful}}
+ if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
+ // expected-note at -1{{An undefined value may be read from 'errno'}}
+}
+
void check_fwrite(void) {
char Buf[] = "0123456789";
FILE *F = tmpfile();
+ // expected-note at -1{{'tmpfile' is successful}}
// expected-note at +2{{'F' is non-null}}
// expected-note at +1{{Taking false branch}}
if (!F)
return;
int R = fwrite(Buf, 1, 10, F);
- // expected-note at -1{{Assuming that function 'fwrite' is successful}}
+ // expected-note at -1{{'errno' may be undefined after successful call to 'fwrite'}}
+ // expected-note at -2{{'fwrite' is successful}}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
// expected-note at -1{{An undefined value may be read from 'errno'}}
(void)fclose(F);
@@ -91,12 +131,14 @@ void check_fwrite(void) {
void check_fseek(void) {
FILE *F = tmpfile();
+ // expected-note at -1{{'tmpfile' is successful}}
// expected-note at +2{{'F' is non-null}}
// expected-note at +1{{Taking false branch}}
if (!F)
return;
(void)fseek(F, 11, SEEK_SET);
- // expected-note at -1{{Assuming that function 'fseek' is successful}}
+ // expected-note at -1{{'errno' may be undefined after successful call to 'fseek'}}
+ // expected-note at -2{{'fseek' is successful}}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
// expected-note at -1{{An undefined value may be read from 'errno'}}
(void)fclose(F);
@@ -104,24 +146,27 @@ void check_fseek(void) {
void check_rewind_errnocheck(void) {
FILE *F = tmpfile();
+ // expected-note at -1{{'tmpfile' is successful}}
// expected-note at +2{{'F' is non-null}}
// expected-note at +1{{Taking false branch}}
if (!F)
return;
errno = 0;
- rewind(F); // expected-note{{Function 'rewind' indicates failure only by setting of 'errno'}}
+ rewind(F); // expected-note{{'rewind' indicates failure only by setting 'errno'}}
fclose(F); // expected-warning{{Value of 'errno' was not checked and may be overwritten by function 'fclose' [alpha.unix.Errno]}}
// expected-note at -1{{Value of 'errno' was not checked and may be overwritten by function 'fclose'}}
}
void check_fileno(void) {
FILE *F = tmpfile();
+ // expected-note at -1{{'tmpfile' is successful}}
// expected-note at +2{{'F' is non-null}}
// expected-note at +1{{Taking false branch}}
if (!F)
return;
fileno(F);
- // expected-note at -1{{Assuming that function 'fileno' is successful}}
+ // expected-note at -1{{Assuming that 'fileno' is successful}}
+ // expected-note at -2{{'errno' may be undefined after successful call to 'fileno'}}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
// expected-note at -1{{An undefined value may be read from 'errno'}}
(void)fclose(F);
diff --git a/clang/test/Analysis/stream-note.c b/clang/test/Analysis/stream-note.c
index 6466630782f5d8..8d297715b03a3a 100644
--- a/clang/test/Analysis/stream-note.c
+++ b/clang/test/Analysis/stream-note.c
@@ -7,11 +7,13 @@
void check_note_at_correct_open(void) {
FILE *F1 = tmpfile(); // expected-note {{Stream opened here}}
+ // stdargs-note at -1 {{'tmpfile' is successful}}
if (!F1)
// expected-note at -1 {{'F1' is non-null}}
// expected-note at -2 {{Taking false branch}}
return;
FILE *F2 = tmpfile();
+ // stdargs-note at -1 {{'tmpfile' is successful}}
if (!F2) {
// expected-note at -1 {{'F2' is non-null}}
// expected-note at -2 {{Taking false branch}}
@@ -20,6 +22,7 @@ void check_note_at_correct_open(void) {
}
rewind(F2);
fclose(F2);
+ // stdargs-note at -1 {{'fclose' fails}}
rewind(F1);
}
// expected-warning at -1 {{Opened stream never closed. Potential resource leak}}
@@ -27,6 +30,7 @@ void check_note_at_correct_open(void) {
void check_note_fopen(void) {
FILE *F = fopen("file", "r"); // expected-note {{Stream opened here}}
+ // stdargs-note at -1 {{'fopen' is successful}}
if (!F)
// expected-note at -1 {{'F' is non-null}}
// expected-note at -2 {{Taking false branch}}
@@ -37,11 +41,13 @@ void check_note_fopen(void) {
void check_note_freopen(void) {
FILE *F = fopen("file", "r"); // expected-note {{Stream opened here}}
+ // stdargs-note at -1 {{'fopen' is successful}}
if (!F)
// expected-note at -1 {{'F' is non-null}}
// expected-note at -2 {{Taking false branch}}
return;
F = freopen(0, "w", F); // expected-note {{Stream reopened here}}
+ // stdargs-note at -1 {{'freopen' is successful}}
if (!F)
// expected-note at -1 {{'F' is non-null}}
// expected-note at -2 {{Taking false branch}}
@@ -52,6 +58,8 @@ void check_note_freopen(void) {
void check_note_leak_2(int c) {
FILE *F1 = fopen("foo1.c", "r"); // expected-note {{Stream opened here}}
+ // stdargs-note at -1 {{'fopen' is successful}}
+ // stdargs-note at -2 {{'fopen' is successful}}
if (!F1)
// expected-note at -1 {{'F1' is non-null}}
// expected-note at -2 {{Taking false branch}}
@@ -59,6 +67,8 @@ void check_note_leak_2(int c) {
// expected-note at -4 {{Taking false branch}}
return;
FILE *F2 = fopen("foo2.c", "r"); // expected-note {{Stream opened here}}
+ // stdargs-note at -1 {{'fopen' is successful}}
+ // stdargs-note at -2 {{'fopen' is successful}}
if (!F2) {
// expected-note at -1 {{'F2' is non-null}}
// expected-note at -2 {{Taking false branch}}
@@ -84,6 +94,7 @@ void check_note_leak_2(int c) {
void check_track_null(void) {
FILE *F;
F = fopen("foo1.c", "r"); // expected-note {{Value assigned to 'F'}} expected-note {{Assuming pointer value is null}}
+ // stdargs-note at -1 {{'fopen' fails}}
if (F != NULL) { // expected-note {{Taking false branch}} expected-note {{'F' is equal to NULL}}
fclose(F);
return;
@@ -96,13 +107,16 @@ void check_eof_notes_feof_after_feof(void) {
FILE *F;
char Buf[10];
F = fopen("foo1.c", "r");
+ // stdargs-note at -1 {{'fopen' is successful}}
if (F == NULL) { // expected-note {{Taking false branch}} expected-note {{'F' is not equal to NULL}}
return;
}
fread(Buf, 1, 1, F);
+ // stdargs-note at -1 {{'fread' fails}}
if (feof(F)) { // expected-note {{Taking true branch}}
clearerr(F);
fread(Buf, 1, 1, F); // expected-note {{Assuming stream reaches end-of-file here}}
+ // stdargs-note at -1 {{'fread' fails}}
if (feof(F)) { // expected-note {{Taking true branch}}
fread(Buf, 1, 1, F); // expected-warning {{Read function called when stream is in EOF state. Function has no effect}}
// expected-note at -1 {{Read function called when stream is in EOF state. Function has no effect}}
@@ -115,10 +129,12 @@ void check_eof_notes_feof_after_no_feof(void) {
FILE *F;
char Buf[10];
F = fopen("foo1.c", "r");
+ // stdargs-note at -1 {{'fopen' is successful}}
if (F == NULL) { // expected-note {{Taking false branch}} expected-note {{'F' is not equal to NULL}}
return;
}
fread(Buf, 1, 1, F);
+ // stdargs-note at -1 {{'fread' is successful}}
if (feof(F)) { // expected-note {{Taking false branch}}
fclose(F);
return;
@@ -127,6 +143,7 @@ void check_eof_notes_feof_after_no_feof(void) {
return;
}
fread(Buf, 1, 1, F); // expected-note {{Assuming stream reaches end-of-file here}}
+ // stdargs-note at -1 {{'fread' fails}}
if (feof(F)) { // expected-note {{Taking true branch}}
fread(Buf, 1, 1, F); // expected-warning {{Read function called when stream is in EOF state. Function has no effect}}
// expected-note at -1 {{Read function called when stream is in EOF state. Function has no effect}}
@@ -138,9 +155,11 @@ void check_eof_notes_feof_or_no_error(void) {
FILE *F;
char Buf[10];
F = fopen("foo1.c", "r");
+ // stdargs-note at -1 {{'fopen' is successful}}
if (F == NULL) // expected-note {{Taking false branch}} expected-note {{'F' is not equal to NULL}}
return;
int RRet = fread(Buf, 1, 1, F); // expected-note {{Assuming stream reaches end-of-file here}}
+ // stdargs-note at -1 {{'fread' fails}}
if (ferror(F)) { // expected-note {{Taking false branch}}
} else {
fread(Buf, 1, 1, F); // expected-warning {{Read function called when stream is in EOF state. Function has no effect}}
More information about the cfe-commits
mailing list