diff options
author | Balázs Kéri <balazs.keri@ericsson.com> | 2023-07-18 08:47:05 +0200 |
---|---|---|
committer | Balázs Kéri <balazs.keri@ericsson.com> | 2023-07-18 09:29:15 +0200 |
commit | 39670ae3b93470b2d29fe78e6d40c5d82a05e4a1 (patch) | |
tree | 23f92d2cec945e44c2ba76235538d5ba4b6d4890 /clang/lib/StaticAnalyzer/Checkers | |
parent | 4214f15660a68fcd0dbfe39fce101d148b8aa3f0 (diff) | |
download | llvm-39670ae3b93470b2d29fe78e6d40c5d82a05e4a1.zip llvm-39670ae3b93470b2d29fe78e6d40c5d82a05e4a1.tar.gz llvm-39670ae3b93470b2d29fe78e6d40c5d82a05e4a1.tar.bz2 |
[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
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers')
3 files changed, 85 insertions, 72 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp index 51f39c6..be2fa91 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 2ca3979..0707fd1 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 0228e82..8d4fb12 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); |