aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp108
1 files changed, 63 insertions, 45 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index 54a41b8..2163689 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -533,13 +533,11 @@ class StdLibraryFunctionsChecker
virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
const Summary &Summary,
CheckerContext &C) const = 0;
- /// Get a NoteTag about the changes made to 'errno' and the possible bug.
- /// It may return \c nullptr (if no bug report from \c ErrnoChecker is
- /// expected).
- virtual const NoteTag *describe(CheckerContext &C,
- StringRef FunctionName) const {
- return nullptr;
- }
+ /// Get a description about what happens with 'errno' here and how it causes
+ /// a later bug report created by ErrnoChecker.
+ /// Empty return value means that 'errno' related bug may not happen from
+ /// the current analyzed function.
+ virtual const std::string describe(CheckerContext &C) const { return ""; }
virtual ~ErrnoConstraintBase() {}
@@ -596,7 +594,8 @@ class StdLibraryFunctionsChecker
};
/// Set errno constraint at success cases of standard functions.
- /// Success case: 'errno' is not allowed to be used.
+ /// Success case: 'errno' is not allowed to be used because the value is
+ /// undefined after successful call.
/// \c ErrnoChecker can emit bug report after such a function call if errno
/// is used.
class SuccessErrnoConstraint : public ErrnoConstraintBase {
@@ -607,12 +606,15 @@ class StdLibraryFunctionsChecker
return errno_modeling::setErrnoForStdSuccess(State, C);
}
- const NoteTag *describe(CheckerContext &C,
- StringRef FunctionName) const override {
- return errno_modeling::getNoteTagForStdSuccess(C, FunctionName);
+ const std::string describe(CheckerContext &C) const {
+ return "'errno' becomes undefined after the call";
}
};
+ /// Set errno constraint at functions that indicate failure only with 'errno'.
+ /// In this case 'errno' is required to be observed.
+ /// \c ErrnoChecker can emit bug report after such a function call if errno
+ /// is overwritten without a read before.
class ErrnoMustBeCheckedConstraint : public ErrnoConstraintBase {
public:
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
@@ -622,9 +624,8 @@ class StdLibraryFunctionsChecker
Call.getOriginExpr());
}
- const NoteTag *describe(CheckerContext &C,
- StringRef FunctionName) const override {
- return errno_modeling::getNoteTagForStdMustBeChecked(C, FunctionName);
+ const std::string describe(CheckerContext &C) const {
+ return "reading 'errno' is required to find out if the call has failed";
}
};
@@ -1392,17 +1393,28 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
// 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()) {
- const SVal RV = Call.getReturnValue();
- // 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) {
+ DeclarationName FunctionName =
+ cast<NamedDecl>(Call.getDecl())->getDeclName();
+
+ std::string ErrnoNote = Case.getErrnoConstraint().describe(C);
+ std::string CaseNote;
+ if (Case.getNote().empty()) {
+ if (!ErrnoNote.empty())
+ ErrnoNote =
+ llvm::formatv("After calling '{0}' {1}", FunctionName, ErrnoNote);
+ } else {
+ CaseNote = llvm::formatv(Case.getNote().str().c_str(), FunctionName);
+ }
+ const SVal RV = Call.getReturnValue();
+
+ if (Summary.getInvalidationKd() == EvalCallAsPure) {
+ // Do not expect that errno is interesting (the "pure" functions do not
+ // affect it).
+ if (!CaseNote.empty()) {
const NoteTag *Tag = C.getNoteTag(
- [Node, Note, RV](PathSensitiveBugReport &BR) -> std::string {
+ [Node, CaseNote, RV](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
@@ -1414,37 +1426,42 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
// the successors). This is why this check is only used in the
// EvalCallAsPure case.
if (BR.isInteresting(RV) && Node->succ_size() > 1)
- return Note;
+ return CaseNote;
return "";
});
Pred = C.addTransition(NewState, Pred, Tag);
- } else {
+ }
+ } else {
+ if (!CaseNote.empty() || !ErrnoNote.empty()) {
const NoteTag *Tag =
- C.getNoteTag([Note, RV](PathSensitiveBugReport &BR) -> std::string {
- if (BR.isInteresting(RV))
- return Note;
+ C.getNoteTag([CaseNote, ErrnoNote,
+ RV](PathSensitiveBugReport &BR) -> std::string {
+ // If 'errno' is interesting, show the user a note about the case
+ // (what happened at the function call) and about how 'errno'
+ // causes the problem. ErrnoChecker sets the errno (but not RV) to
+ // interesting.
+ // If only the return value is interesting, show only the case
+ // note.
+ std::optional<Loc> ErrnoLoc =
+ errno_modeling::getErrnoLoc(BR.getErrorNode()->getState());
+ bool ErrnoImportant = !ErrnoNote.empty() && ErrnoLoc &&
+ BR.isInteresting(ErrnoLoc->getAsRegion());
+ if (ErrnoImportant) {
+ BR.markNotInteresting(ErrnoLoc->getAsRegion());
+ if (CaseNote.empty())
+ return ErrnoNote;
+ return llvm::formatv("{0}; {1}", CaseNote, ErrnoNote);
+ } else {
+ if (BR.isInteresting(RV))
+ return CaseNote;
+ }
return "";
});
Pred = C.addTransition(NewState, Pred, Tag);
}
-
- // Pred may be:
- // - a nullpointer, if we reach an already existing node (theoretically);
- // - a sink, when NewState is posteriorly overconstrained.
- // In these situations we cannot add the errno note tag.
- if (!Pred || Pred->isSink())
- continue;
}
- // 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.
+ // Add the transition if no note tag was added.
if (Pred == Node && NewState != State)
C.addTransition(NewState);
}
@@ -2038,7 +2055,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
ErrnoMustNotBeChecked, GenericSuccessMsg)
.Case({ArgumentCondition(1U, WithinRange, SingleValue(0)),
ReturnValueCondition(WithinRange, SingleValue(0))},
- ErrnoMustNotBeChecked, GenericSuccessMsg)
+ ErrnoMustNotBeChecked,
+ "Assuming that argument 'size' to '{0}' is 0")
.ArgConstraint(NotNullBuffer(ArgNo(0), ArgNo(1), ArgNo(2)))
.ArgConstraint(NotNull(ArgNo(3)))
.ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1),