balazske updated this revision to Diff 279248.
balazske added a comment.
NFC code improvements.
Detect expressions in conditions.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D72705/new/
https://reviews.llvm.org/D72705
Files:
clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
clang/lib/StaticAnalyzer/Checkers/ErrorReturnChecker.cpp
clang/test/Analysis/Inputs/system-header-simulator.h
clang/test/Analysis/error-return.c
Index: clang/test/Analysis/error-return.c
===================================================================
--- /dev/null
+++ clang/test/Analysis/error-return.c
@@ -0,0 +1,181 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.unix.ErrorReturn -verify %s
+
+#include "Inputs/system-header-simulator.h"
+
+FILE *file();
+
+void test_EOFOrNeg_LT_Good() {
+ if (fputs("str", file()) < 0) {
+ }
+}
+
+void test_EOFOrNeg_LT_Bad() {
+ if (fputs("str", file()) < -1) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_GT_Good() {
+ if (fputs("str", file()) > -1) {
+ }
+}
+
+void test_EOFOrNeg_GT_Bad() {
+ if (fputs("str", file()) > 0) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_LE_Good() {
+ if (fputs("str", file()) <= -1) {
+ }
+}
+
+void test_EOFOrNeg_LE_Bad() {
+ if (fputs("str", file()) <= 0) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_GE_Good() {
+ if (fputs("str", file()) >= 0) {
+ }
+}
+
+void test_EOFOrNeg_GE_Bad() {
+ if (fputs("str", file()) >= -1) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_EQ_Good() {
+ if (fputs("str", file()) == -1) {
+ }
+}
+
+void test_EOFOrNeg_EQ_Bad() {
+ if (fputs("str", file()) == 0) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_NE_Good() {
+ if (fputs("str", file()) != -1) {
+ }
+}
+
+void test_EOFOrNeg_NE_Bad() {
+ if (fputs("str", file()) != 0) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_EQ_BadVal() {
+ if (fputs("str", file()) == -2) { // expected-warning{{Use of return value that was not checked}}
+ }
+ if (fputs("str", file()) == 1) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_VarAssign() {
+ int X = fputs("str", file());
+ if (X != 0) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_VarAssignInCond() {
+ int X;
+ if ((X = fputs("str", file())) != 0) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_VarAssign1() {
+ int X = fputs("str", file());
+ int Y = X;
+ if (Y != 0) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void badcheck(int X) {
+ if (X == 0) { } // expected-warning{{Use of return value that was not checked}}
+}
+
+void test_EOFOrNeg_Call() {
+ int X = fputs("str", file());
+ badcheck(X);
+}
+
+void test_EOFOrNeg_Syscall() {
+ int X = fputs("str", file());
+ fakeSystemHeaderCallIntVal(X); // expected-warning{{Use of return value that was not checked}}
+ fakeSystemHeaderCallIntVal(fputs("str", file())); // expected-warning{{Use of return value that was not checked}}
+}
+
+void test_EOFOrNeg_Use_LNot() {
+ int X = fputs("str", file());
+ if (!X) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_Use_Add() {
+ int X = fputs("str", file());
+ int Y = X + 1; // expected-warning{{Use of return value that was not checked}}
+}
+
+void test_EOFOrNeg_If() {
+ int X = fputs("str", file());
+ if (X) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_IfCond() {
+ if (fputs("str", file())) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_ForInit() {
+ for (fputs("str", file());;) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_ForCond() {
+ for (; fputs("str", file());) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_ForInc() {
+ for (;; fputs("str", file())) { // expected-warning{{Use of return value that was not checked}}
+ }
+}
+
+void test_EOFOrNeg_DoCond() {
+ do {
+ } while (fputs("str", file())); // expected-warning{{Use of return value that was not checked}}
+}
+
+void test_EOFOrNeg_WhileCond() {
+ while (fputs("str", file())) {
+ }; // expected-warning{{Use of return value that was not checked}}
+}
+
+void unknown1(int);
+
+void test_EOFOrNeg_EscapeCall() {
+ int X = fputs("str", file());
+ unknown1(X);
+ int Y = X + 1;
+}
+
+int GlobalInt;
+
+void test_EOFOrNeg_EscapeGlobalAssign() {
+ GlobalInt = fputs("str", file());
+ int X = GlobalInt + 1;
+}
+
+void test_EOFOrNeg_NoErrorAfterGoodCheck() {
+ int X = fputs("str", file());
+ if (X < 0) {
+ }
+ if (X < 1) {
+ }
+}
+
+void test_EOFOrNeg_Unused() {
+ // FIXME: Detect this
+ fputs("str", file());
+}
Index: clang/test/Analysis/Inputs/system-header-simulator.h
===================================================================
--- clang/test/Analysis/Inputs/system-header-simulator.h
+++ clang/test/Analysis/Inputs/system-header-simulator.h
@@ -49,6 +49,7 @@
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
int fputc(int ch, FILE *stream);
+int fputs(const char *restrict s, FILE *restrict stream);
int fseek(FILE *__stream, long int __off, int __whence);
long int ftell(FILE *__stream);
void rewind(FILE *__stream);
@@ -100,6 +101,7 @@
//The following are fake system header functions for generic testing.
void fakeSystemHeaderCallInt(int *);
void fakeSystemHeaderCallIntPtr(int **);
+void fakeSystemHeaderCallIntVal(int);
// Some data strauctures may hold onto the pointer and free it later.
void fake_insque(void *, void *);
Index: clang/lib/StaticAnalyzer/Checkers/ErrorReturnChecker.cpp
===================================================================
--- /dev/null
+++ clang/lib/StaticAnalyzer/Checkers/ErrorReturnChecker.cpp
@@ -0,0 +1,510 @@
+//===-- ErrorReturnChecker.cpp ------------------------------------*- C++ -*--//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ErrorReturnChecker, a builtin checker that checks for
+// error checking of certain C API function return values.
+// This check is taken from SEI CERT ERR33-C:
+// https://wiki.sei.cmu.edu/confluence/display/c/ERR33-C.+Detect+and+handle+standard+library+errors
+//
+// About the checker:
+// It involves a predefined set of system call functions that can fail and
+// return a specific error code on failure. (This list is provided by the CERT
+// rule.) The checker tries to verify if there is a statement in the code that
+// checks the returned value. For different kinds of error return values
+// different kinds of check statements are accepted. The first use (that is not
+// assignment or pass to function) of the return value of the function call that
+// is checked should be the check statement. Any other use of the returned value
+// (except assignment or pass to function call) is taken as use before check and
+// reported as checker warning. Additionally, if the return value is not used at
+// all a warning is generated for unchecked return value. Passing the return
+// value to a system function results in warning too.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Expr.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include <functional>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+/// Interface and abstraction for various kinds of error return value and way of
+/// checking it. The "test" functions are called if a corresponding construct in
+/// the code is found. The functions should return true the code is found to be
+/// acceptable as error check.
+class ErrorReturnCheckKind {
+public:
+ /// Test if an encountered binary operator where the return value is involved
+ /// is a valid check statement. The return value appears in one side of the
+ /// operator (`ChildIsLHS` indicates if it is on the LHS). If the other side
+ /// contains a known (mostly constant) value, it is already calculated in
+ /// `KnownValue`. `RetTy` is the type of the return value (return type of the
+ /// function call in the code to check).
+ virtual bool testBinOpForCheckStatement(BasicValueFactory &BVF,
+ const BinaryOperator *BinOp,
+ const llvm::APSInt *KnownValue,
+ QualType RetTy,
+ bool ChildIsLHS) const = 0;
+};
+
+/// Error return is a -1 or any negative value (both is accepted).
+/// More precise, the check for error return value should be comparison to -1
+/// or relational comparison to 0.
+/// This is to be used with signed types only.
+class EOFOrNegativeErrorReturn : public ErrorReturnCheckKind {
+public:
+ bool testBinOpForCheckStatement(BasicValueFactory &BVF,
+ const BinaryOperator *BinOp,
+ const llvm::APSInt *KnownValue,
+ QualType RetTy,
+ bool ChildIsLHS) const override {
+ if (!KnownValue)
+ return false;
+
+ bool KnownNull = KnownValue->isNullValue();
+ bool KnownEOF = ((*KnownValue) == BVF.getValue(-1, RetTy));
+
+ if (ChildIsLHS) {
+ switch (BinOp->getOpcode()) {
+ case BO_EQ: // 'X == -1'
+ case BO_NE: // 'X != -1'
+ return KnownEOF;
+ case BO_LT: // 'X < 0'
+ return KnownNull;
+ case BO_GE: // 'X >= 0'
+ return KnownNull;
+ case BO_LE: // 'X <= -1'
+ return KnownEOF;
+ case BO_GT: // 'X > -1'
+ return KnownEOF;
+ default:
+ return false;
+ }
+ } else {
+ switch (BinOp->getOpcode()) {
+ case BO_EQ: // '-1 == X'
+ case BO_NE: // '-1 != X'
+ return KnownEOF;
+ case BO_GT: // '0 > X'
+ return KnownNull;
+ case BO_LE: // '0 <= X'
+ return KnownNull;
+ case BO_GE: // '-1 >= X'
+ return KnownEOF;
+ case BO_LT: // '-1 < X'
+ return KnownEOF;
+ default:
+ return false;
+ }
+ }
+ return false;
+ }
+};
+
+/// Description of an API function to check.
+struct FnInfo {
+ /// Error return check kind for the function.
+ ErrorReturnCheckKind *ErrorReturnKind;
+
+ /// Is an unchecked use of return value error.
+ bool UncheckedUseIsError;
+
+ /// Return type of the function (initialized at runtime).
+ mutable QualType RetTy;
+
+ FnInfo(ErrorReturnCheckKind *ErrorReturnKind)
+ : ErrorReturnKind(ErrorReturnKind) {
+ ;
+ }
+};
+
+/// Information about a specific function call that has an error return code to
+/// check. This data is stored in a map and indexed by the SymbolRef that stands
+/// for the result of the function call.
+struct CalledFunctionData {
+ /// Point out the kind of the function that was called.
+ const FnInfo *Info;
+ /// Source range of the calling statement.
+ SourceRange CallLocation;
+
+ CalledFunctionData(const CalledFunctionData &CFD)
+ : Info(CFD.Info), CallLocation(CFD.CallLocation) {}
+ CalledFunctionData(const FnInfo *Info, const SourceRange &CallLocation)
+ : Info{Info}, CallLocation{CallLocation} {}
+
+ CalledFunctionData &operator=(const CalledFunctionData &CFD) {
+ Info = CFD.Info;
+ CallLocation = CFD.CallLocation;
+ return *this;
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddPointer(Info);
+ ID.AddInteger(CallLocation.getBegin().getRawEncoding());
+ }
+
+ bool operator==(const CalledFunctionData &CFD) const {
+ return Info == CFD.Info && CallLocation == CFD.CallLocation;
+ }
+};
+
+class ErrorReturnChecker
+ : public Checker<check::PostCall, check::Location,
+ check::PointerEscape> {
+ mutable std::unique_ptr<BuiltinBug> BT_UncheckedUse;
+
+ void checkAccess(CheckerContext &C, ProgramStateRef State, const Stmt *LoadS,
+ SymbolRef CallSym, const CalledFunctionData *CFD) const;
+ ProgramStateRef processEscapedParams(CheckerContext &C, const CallEvent &Call,
+ ProgramStateRef State) const;
+ const FnInfo *findFunctionToCheck(CheckerContext &C,
+ const CallEvent &Call) const;
+
+public:
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkLocation(SVal L, bool IsLoad, const Stmt *S,
+ CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+ ProgramStateRef checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const;
+
+private:
+ EOFOrNegativeErrorReturn CheckForEOFOrNegative;
+
+ CallDescriptionMap<FnInfo> CheckedFunctions = {
+ {{"fputs", 2}, FnInfo{&CheckForEOFOrNegative}},
+ {{"fputws", 2}, FnInfo{&CheckForEOFOrNegative}},
+ };
+};
+
+/// Result of the ErrorCheckTestStmtVisitor.
+enum VisitResult {
+ // Value use found that is not an error check.
+ NoCheckUseFound,
+ // An error check was found.
+ CheckFound,
+ // Assignment (like) condition was found (something that returns the same
+ // value as `Child`), the check should continue on upper level of the
+ // expression (with the current parent as new child).
+ ContinueAtParentStmt,
+ // Value appeared in any other way.
+ StopExamineNoError
+};
+
+/// Determine if a statement is an error-check for a return value.
+/// The statement to check is stored in `Child`.
+/// This can be the actual load statement that uses the return value,
+/// or a parent statement of it that stands for the same value.
+/// See `ErrorReturnChecker::checkAccess` for the calling algorithm.
+class ErrorCheckTestStmtVisitor
+ : public ConstStmtVisitor<ErrorCheckTestStmtVisitor, VisitResult> {
+ CheckerContext &C;
+ /// Currently examined child statement.
+ const Stmt *Child;
+ /// Data about function whose return value is checked.
+ const CalledFunctionData *CFD;
+ const ParentMap &PM;
+
+ /// Check if `Child` is a child of `Parent` in `ParentMap`.
+ bool isChildOf(const Stmt *Parent) const {
+ for (const Stmt *C = Child; C; C = PM.getParent(C))
+ if (C == Parent)
+ return true;
+ return false;
+ };
+
+ /// Determine the "known value" (if a constant) of an Expr.
+ const llvm::APSInt *getKnownConstantVal(const Expr *E) const {
+ Optional<SVal> ConstantVal = C.getSValBuilder().getConstantVal(E);
+ if (ConstantVal)
+ return C.getSValBuilder().getKnownValue(C.getState(), *ConstantVal);
+ return nullptr;
+ }
+
+public:
+ ErrorCheckTestStmtVisitor(CheckerContext &C, const Stmt *Child,
+ const CalledFunctionData *CFD)
+ : C(C), Child(Child), CFD(CFD),
+ PM(C.getLocationContext()->getParentMap()) {}
+
+ VisitResult VisitBinaryOperator(const BinaryOperator *BO) {
+ // Try to find the child expression on one side of the operator.
+ Expr *OtherS = nullptr;
+ for (const Stmt *P = Child; P && !OtherS; P = PM.getParent(P)) {
+ if (P == BO->getLHS())
+ OtherS = BO->getRHS();
+ else if (P == BO->getRHS())
+ OtherS = BO->getLHS();
+ }
+ assert(OtherS && "Invalid parent at binary operator.");
+
+ BinaryOperatorKind Op = BO->getOpcode();
+ if (Op == BO_Assign) {
+ // Value of child is transferred to parent, can continue with parent.
+ assert(OtherS == BO->getLHS() && "Loaded value not on assignment RHS.");
+ return ContinueAtParentStmt;
+ }
+
+ // `Child` appears in the binary operator.
+ // Perform a specific check on the binary operator to determine if it is
+ // an error-check statement.
+ if (CFD->Info->ErrorReturnKind->testBinOpForCheckStatement(
+ C.getSValBuilder().getBasicValueFactory(), BO,
+ getKnownConstantVal(OtherS), CFD->Info->RetTy,
+ OtherS == BO->getRHS()))
+ return CheckFound;
+ // Value appears in binary operator in other way than error check.
+ return NoCheckUseFound;
+ }
+
+ VisitResult VisitCallExpr(const CallExpr *CE) {
+ const FunctionDecl *CalledF = C.getCalleeDecl(CE);
+ SourceLocation Loc = CalledF->getLocation();
+ // Check if system function is called.
+ // It is assumed that any system function does not accept value that is
+ // an error return value from any other function.
+ // In other words error check is required before system function is called
+ // with the value.
+ if (Loc.isValid() && C.getSourceManager().isInSystemHeader(Loc))
+ return NoCheckUseFound;
+ // Non-system function call is no error, the call may be inlined and can
+ // contain error check.
+ return StopExamineNoError;
+ }
+
+ // Value appears in other expression.
+ VisitResult VisitExpr(const Stmt *S) { return NoCheckUseFound; }
+
+ VisitResult VisitDeclStmt(const DeclStmt *Decl) {
+ // Value is used at initialization.
+ return StopExamineNoError;
+ }
+
+ VisitResult VisitIfStmt(const IfStmt *S) {
+ if (isChildOf(S->getCond()))
+ return NoCheckUseFound;
+ return StopExamineNoError;
+ }
+
+ VisitResult VisitForStmt(const ForStmt *S) {
+ if (isChildOf(S->getInit()) || isChildOf(S->getCond()) ||
+ isChildOf(S->getInc()))
+ return NoCheckUseFound;
+ return StopExamineNoError;
+ }
+
+ VisitResult VisitDoStmt(const DoStmt *S) {
+ if (isChildOf(S->getCond()))
+ return NoCheckUseFound;
+ return StopExamineNoError;
+ }
+
+ VisitResult VisitWhileStmt(const WhileStmt *S) {
+ if (isChildOf(S->getCond()))
+ return NoCheckUseFound;
+ return StopExamineNoError;
+ }
+
+ // Value appears in other statement.
+ // FIXME: Statements that affects control-flow should be checked separately.
+ // For example `Child` may appear as a condition of `if`.
+ VisitResult VisitStmt(const Stmt *S) { return StopExamineNoError; }
+};
+
+} // end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(CalledFunctionDataMap, SymbolRef,
+ CalledFunctionData)
+
+void ErrorReturnChecker::checkAccess(CheckerContext &C, ProgramStateRef State,
+ const Stmt *LoadS, SymbolRef CallSym,
+ const CalledFunctionData *CFD) const {
+ const ParentMap &PM = C.getLocationContext()->getParentMap();
+ //llvm::errs()<<"LoadS\n";
+ //LoadS->dumpColor();
+
+ while (LoadS) {
+ const Stmt *ParentS = PM.getParentIgnoreParenCasts(LoadS);
+ //llvm::errs()<<"ParentS\n";
+ //ParentS->dumpColor();
+
+ ErrorCheckTestStmtVisitor FindErrorCheck{C, LoadS, CFD};
+ switch (FindErrorCheck.Visit(ParentS)) {
+ case NoCheckUseFound: {
+ if (!BT_UncheckedUse)
+ BT_UncheckedUse.reset(
+ new BuiltinBug(this, "Use of unchecked return value",
+ "Use of return value that was not checked for error"));
+
+ SourceRange CallLocation = CFD->CallLocation;
+ State = State->remove<CalledFunctionDataMap>(CallSym);
+
+ ExplodedNode *N = C.generateNonFatalErrorNode(State);
+ if (!N) {
+ C.addTransition(State);
+ return;
+ }
+
+ auto Report = std::make_unique<PathSensitiveBugReport>(
+ *BT_UncheckedUse, BT_UncheckedUse->getDescription(), N);
+ // Report->markInteresting(CallSym);
+ Report->addRange(CallLocation);
+ C.emitReport(std::move(Report));
+
+ //auto Report = std::make_unique<BasicBugReport>(*BT_UncheckedUse, BT_UncheckedUse->getDescription(), PathDiagnosticLocation{LoadS, C.getSourceManager(), C.getLocationContext()});
+ //Report->addRange(CallLocation);
+ //C.emitReport(std::move(Report));
+
+ return;
+ }
+ case CheckFound:
+ // A correct error check was found, remove from state.
+ State = State->remove<CalledFunctionDataMap>(CallSym);
+ C.addTransition(State);
+ return;
+
+ case ContinueAtParentStmt:
+ // Continue checking at upper level (check with result of assignment).
+ LoadS = ParentS;
+ continue;
+
+ case StopExamineNoError:
+ C.addTransition(State);
+ return;
+ };
+ }
+}
+
+ProgramStateRef ErrorReturnChecker::processEscapedParams(
+ CheckerContext &C, const CallEvent &Call, ProgramStateRef State) const {
+ for (unsigned int I = 0, E = Call.getNumArgs(); I < E; ++I) {
+ SVal V = Call.getArgSVal(I);
+ SymbolRef Sym = V.getAsSymbol();
+ if (Sym) {
+ State = State->remove<CalledFunctionDataMap>(Sym);
+ }
+ }
+ return State;
+}
+
+const FnInfo *
+ErrorReturnChecker::findFunctionToCheck(CheckerContext &C,
+ const CallEvent &Call) const {
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ const ParentMap &PM = C.getLocationContext()->getParentMap();
+
+ if (!FD || FD->getKind() != Decl::Function)
+ return nullptr;
+
+ if (!Call.isGlobalCFunction() || !Call.isInSystemHeader())
+ return nullptr;
+
+ const FnInfo *Fn = CheckedFunctions.lookup(Call);
+ if (!Fn)
+ return nullptr;
+
+ const Stmt *S = PM.getParent(Call.getOriginExpr());
+
+ // Check for explicit cast to void.
+ if (auto *Cast = dyn_cast<const CStyleCastExpr>(S)) {
+ if (Cast->getTypeAsWritten().getTypePtr()->isVoidType())
+ return nullptr;
+ }
+
+ // The call should have a symbolic return value to analyze it.
+ SVal RetSV = Call.getReturnValue();
+ if (RetSV.isUnknownOrUndef())
+ return nullptr;
+ SymbolRef RetSym = RetSV.getAsSymbol();
+ if (!RetSym)
+ return nullptr;
+
+ // Lazy-init the return type when the function is found.
+ if (Fn->RetTy.isNull())
+ Fn->RetTy = FD->getReturnType();
+
+ return Fn;
+}
+
+void ErrorReturnChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ State = processEscapedParams(C, Call, State);
+
+ const FnInfo *Fn = findFunctionToCheck(C, Call);
+ if (!Fn) {
+ C.addTransition(State);
+ return;
+ }
+
+ SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
+
+ CalledFunctionData CFD{Fn, Call.getSourceRange()};
+ State = State->set<CalledFunctionDataMap>(RetSym, CFD);
+
+ checkAccess(C, State, Call.getOriginExpr(), RetSym, &CFD);
+}
+
+void ErrorReturnChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S,
+ CheckerContext &C) const {
+ if (!IsLoad)
+ return;
+ if (L.isUnknownOrUndef())
+ return;
+
+ auto Location = L.castAs<DefinedOrUnknownSVal>().getAs<Loc>();
+ if (!Location)
+ return;
+
+ ProgramStateRef State = C.getState();
+ SymbolRef Sym = State->getSVal(*Location).getAsSymbol();
+ if (!Sym)
+ return;
+
+ const CalledFunctionData *CFD = State->get<CalledFunctionDataMap>(Sym);
+ if (!CFD)
+ return;
+
+ checkAccess(C, State, S, Sym, CFD);
+}
+
+ProgramStateRef ErrorReturnChecker::checkPointerEscape(
+ ProgramStateRef State, const InvalidatedSymbols &Escaped,
+ const CallEvent *Call, PointerEscapeKind Kind) const {
+ for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
+ E = Escaped.end();
+ I != E; ++I) {
+ SymbolRef Sym = *I;
+ State = State->remove<CalledFunctionDataMap>(Sym);
+ }
+ return State;
+}
+
+void ento::registerErrorReturnChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<ErrorReturnChecker>();
+}
+
+bool ento::shouldRegisterErrorReturnChecker(const CheckerManager &Mgr) {
+ return true;
+}
Index: clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -40,6 +40,7 @@
DynamicTypePropagation.cpp
DynamicTypeChecker.cpp
EnumCastOutOfRangeChecker.cpp
+ ErrorReturnChecker.cpp
ExprInspectionChecker.cpp
FixedAddressChecker.cpp
FuchsiaHandleChecker.cpp
Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -516,6 +516,10 @@
HelpText<"Check improper use of chroot">,
Documentation<HasAlphaDocumentation>;
+def ErrorReturnChecker : Checker<"ErrorReturn">,
+ HelpText<"Check for unchecked error return values">,
+ Documentation<HasAlphaDocumentation>;
+
def PthreadLockChecker : Checker<"PthreadLock">,
HelpText<"Simple lock -> unlock checker">,
Dependencies<[PthreadLockBase]>,
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits