xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp (revision a10d3d2a3a82a577625ee8877853b4c4c9e2bbdc)
160f3b071SBalázs Kéri //=== ErrnoChecker.cpp ------------------------------------------*- C++ -*-===//
260f3b071SBalázs Kéri //
360f3b071SBalázs Kéri // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
460f3b071SBalázs Kéri // See https://llvm.org/LICENSE.txt for license information.
560f3b071SBalázs Kéri // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
660f3b071SBalázs Kéri //
760f3b071SBalázs Kéri //===----------------------------------------------------------------------===//
860f3b071SBalázs Kéri //
960f3b071SBalázs Kéri // This defines an "errno checker" that can detect some invalid use of the
1060f3b071SBalázs Kéri // system-defined value 'errno'. This checker works together with the
1160f3b071SBalázs Kéri // ErrnoModeling checker and other checkers like StdCLibraryFunctions.
1260f3b071SBalázs Kéri //
1360f3b071SBalázs Kéri //===----------------------------------------------------------------------===//
1460f3b071SBalázs Kéri 
1560f3b071SBalázs Kéri #include "ErrnoModeling.h"
1660f3b071SBalázs Kéri #include "clang/AST/ParentMapContext.h"
1760f3b071SBalázs Kéri #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
1860f3b071SBalázs Kéri #include "clang/StaticAnalyzer/Core/Checker.h"
1960f3b071SBalázs Kéri #include "clang/StaticAnalyzer/Core/CheckerManager.h"
20afa8a2eeSNagyDonat #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
2160f3b071SBalázs Kéri #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2260f3b071SBalázs Kéri #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
2360f3b071SBalázs Kéri #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
2460f3b071SBalázs Kéri #include "llvm/ADT/STLExtras.h"
25a1580d7bSKazu Hirata #include <optional>
2660f3b071SBalázs Kéri 
2760f3b071SBalázs Kéri using namespace clang;
2860f3b071SBalázs Kéri using namespace ento;
2960f3b071SBalázs Kéri using namespace errno_modeling;
3060f3b071SBalázs Kéri 
3160f3b071SBalázs Kéri namespace {
3260f3b071SBalázs Kéri 
3360f3b071SBalázs Kéri class ErrnoChecker
3460f3b071SBalázs Kéri     : public Checker<check::Location, check::PreCall, check::RegionChanges> {
3560f3b071SBalázs Kéri public:
3660f3b071SBalázs Kéri   void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
3760f3b071SBalázs Kéri                      CheckerContext &) const;
3860f3b071SBalázs Kéri   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
3960f3b071SBalázs Kéri   ProgramStateRef
4060f3b071SBalázs Kéri   checkRegionChanges(ProgramStateRef State,
4160f3b071SBalázs Kéri                      const InvalidatedSymbols *Invalidated,
4260f3b071SBalázs Kéri                      ArrayRef<const MemRegion *> ExplicitRegions,
4360f3b071SBalázs Kéri                      ArrayRef<const MemRegion *> Regions,
4460f3b071SBalázs Kéri                      const LocationContext *LCtx, const CallEvent *Call) const;
4560f3b071SBalázs Kéri 
4660f3b071SBalázs Kéri   /// Indicates if a read (load) of \c errno is allowed in a non-condition part
4760f3b071SBalázs Kéri   /// of \c if, \c switch, loop and conditional statements when the errno
4860f3b071SBalázs Kéri   /// value may be undefined.
4960f3b071SBalázs Kéri   bool AllowErrnoReadOutsideConditions = true;
5060f3b071SBalázs Kéri 
5160f3b071SBalázs Kéri private:
5260f3b071SBalázs Kéri   void generateErrnoNotCheckedBug(CheckerContext &C, ProgramStateRef State,
5360f3b071SBalázs Kéri                                   const MemRegion *ErrnoRegion,
5460f3b071SBalázs Kéri                                   const CallEvent *CallMayChangeErrno) const;
5560f3b071SBalázs Kéri 
5660f3b071SBalázs Kéri   BugType BT_InvalidErrnoRead{this, "Value of 'errno' could be undefined",
5760f3b071SBalázs Kéri                               "Error handling"};
5860f3b071SBalázs Kéri   BugType BT_ErrnoNotChecked{this, "Value of 'errno' was not checked",
5960f3b071SBalázs Kéri                              "Error handling"};
6060f3b071SBalázs Kéri };
6160f3b071SBalázs Kéri 
6260f3b071SBalázs Kéri } // namespace
6360f3b071SBalázs Kéri 
6460f3b071SBalázs Kéri static ProgramStateRef setErrnoStateIrrelevant(ProgramStateRef State) {
6560f3b071SBalázs Kéri   return setErrnoState(State, Irrelevant);
6660f3b071SBalázs Kéri }
6760f3b071SBalázs Kéri 
6860f3b071SBalázs Kéri /// Check if a statement (expression) or an ancestor of it is in a condition
6960f3b071SBalázs Kéri /// part of a (conditional, loop, switch) statement.
7060f3b071SBalázs Kéri static bool isInCondition(const Stmt *S, CheckerContext &C) {
7160f3b071SBalázs Kéri   ParentMapContext &ParentCtx = C.getASTContext().getParentMapContext();
7260f3b071SBalázs Kéri   bool CondFound = false;
7360f3b071SBalázs Kéri   while (S && !CondFound) {
7460f3b071SBalázs Kéri     const DynTypedNodeList Parents = ParentCtx.getParents(*S);
7560f3b071SBalázs Kéri     if (Parents.empty())
7660f3b071SBalázs Kéri       break;
7760f3b071SBalázs Kéri     const auto *ParentS = Parents[0].get<Stmt>();
7860f3b071SBalázs Kéri     if (!ParentS || isa<CallExpr>(ParentS))
7960f3b071SBalázs Kéri       break;
8060f3b071SBalázs Kéri     switch (ParentS->getStmtClass()) {
8160f3b071SBalázs Kéri     case Expr::IfStmtClass:
8260f3b071SBalázs Kéri       CondFound = (S == cast<IfStmt>(ParentS)->getCond());
8360f3b071SBalázs Kéri       break;
8460f3b071SBalázs Kéri     case Expr::ForStmtClass:
8560f3b071SBalázs Kéri       CondFound = (S == cast<ForStmt>(ParentS)->getCond());
8660f3b071SBalázs Kéri       break;
8760f3b071SBalázs Kéri     case Expr::DoStmtClass:
8860f3b071SBalázs Kéri       CondFound = (S == cast<DoStmt>(ParentS)->getCond());
8960f3b071SBalázs Kéri       break;
9060f3b071SBalázs Kéri     case Expr::WhileStmtClass:
9160f3b071SBalázs Kéri       CondFound = (S == cast<WhileStmt>(ParentS)->getCond());
9260f3b071SBalázs Kéri       break;
9360f3b071SBalázs Kéri     case Expr::SwitchStmtClass:
9460f3b071SBalázs Kéri       CondFound = (S == cast<SwitchStmt>(ParentS)->getCond());
9560f3b071SBalázs Kéri       break;
9660f3b071SBalázs Kéri     case Expr::ConditionalOperatorClass:
9760f3b071SBalázs Kéri       CondFound = (S == cast<ConditionalOperator>(ParentS)->getCond());
9860f3b071SBalázs Kéri       break;
9960f3b071SBalázs Kéri     case Expr::BinaryConditionalOperatorClass:
10060f3b071SBalázs Kéri       CondFound = (S == cast<BinaryConditionalOperator>(ParentS)->getCommon());
10160f3b071SBalázs Kéri       break;
10260f3b071SBalázs Kéri     default:
10360f3b071SBalázs Kéri       break;
10460f3b071SBalázs Kéri     }
10560f3b071SBalázs Kéri     S = ParentS;
10660f3b071SBalázs Kéri   }
10760f3b071SBalázs Kéri   return CondFound;
10860f3b071SBalázs Kéri }
10960f3b071SBalázs Kéri 
11060f3b071SBalázs Kéri void ErrnoChecker::generateErrnoNotCheckedBug(
11160f3b071SBalázs Kéri     CheckerContext &C, ProgramStateRef State, const MemRegion *ErrnoRegion,
11260f3b071SBalázs Kéri     const CallEvent *CallMayChangeErrno) const {
11360f3b071SBalázs Kéri   if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
11460f3b071SBalázs Kéri     SmallString<100> StrBuf;
11560f3b071SBalázs Kéri     llvm::raw_svector_ostream OS(StrBuf);
11660f3b071SBalázs Kéri     if (CallMayChangeErrno) {
11760f3b071SBalázs Kéri       OS << "Value of 'errno' was not checked and may be overwritten by "
11860f3b071SBalázs Kéri             "function '";
11960f3b071SBalázs Kéri       const auto *CallD =
12060f3b071SBalázs Kéri           dyn_cast_or_null<FunctionDecl>(CallMayChangeErrno->getDecl());
12160f3b071SBalázs Kéri       assert(CallD && CallD->getIdentifier());
12260f3b071SBalázs Kéri       OS << CallD->getIdentifier()->getName() << "'";
12360f3b071SBalázs Kéri     } else {
12460f3b071SBalázs Kéri       OS << "Value of 'errno' was not checked and is overwritten here";
12560f3b071SBalázs Kéri     }
12660f3b071SBalázs Kéri     auto BR = std::make_unique<PathSensitiveBugReport>(BT_ErrnoNotChecked,
12760f3b071SBalázs Kéri                                                        OS.str(), N);
12860f3b071SBalázs Kéri     BR->markInteresting(ErrnoRegion);
12960f3b071SBalázs Kéri     C.emitReport(std::move(BR));
13060f3b071SBalázs Kéri   }
13160f3b071SBalázs Kéri }
13260f3b071SBalázs Kéri 
13360f3b071SBalázs Kéri void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
13460f3b071SBalázs Kéri                                  CheckerContext &C) const {
1356ad0788cSKazu Hirata   std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
13660f3b071SBalázs Kéri   if (!ErrnoLoc)
13760f3b071SBalázs Kéri     return;
13860f3b071SBalázs Kéri 
13960f3b071SBalázs Kéri   auto L = Loc.getAs<ento::Loc>();
14060f3b071SBalázs Kéri   if (!L || *ErrnoLoc != *L)
14160f3b071SBalázs Kéri     return;
14260f3b071SBalázs Kéri 
14360f3b071SBalázs Kéri   ProgramStateRef State = C.getState();
14460f3b071SBalázs Kéri   ErrnoCheckState EState = getErrnoState(State);
14560f3b071SBalázs Kéri 
14660f3b071SBalázs Kéri   if (IsLoad) {
14760f3b071SBalázs Kéri     switch (EState) {
14860f3b071SBalázs Kéri     case MustNotBeChecked:
14960f3b071SBalázs Kéri       // Read of 'errno' when it may have undefined value.
15060f3b071SBalázs Kéri       if (!AllowErrnoReadOutsideConditions || isInCondition(S, C)) {
15160f3b071SBalázs Kéri         if (ExplodedNode *N = C.generateErrorNode()) {
15260f3b071SBalázs Kéri           auto BR = std::make_unique<PathSensitiveBugReport>(
15360f3b071SBalázs Kéri               BT_InvalidErrnoRead,
15460f3b071SBalázs Kéri               "An undefined value may be read from 'errno'", N);
15560f3b071SBalázs Kéri           BR->markInteresting(ErrnoLoc->getAsRegion());
15660f3b071SBalázs Kéri           C.emitReport(std::move(BR));
15760f3b071SBalázs Kéri         }
15860f3b071SBalázs Kéri       }
15960f3b071SBalázs Kéri       break;
16060f3b071SBalázs Kéri     case MustBeChecked:
16160f3b071SBalázs Kéri       // 'errno' has to be checked. A load is required for this, with no more
16260f3b071SBalázs Kéri       // information we can assume that it is checked somehow.
16360f3b071SBalázs Kéri       // After this place 'errno' is allowed to be read and written.
16460f3b071SBalázs Kéri       State = setErrnoStateIrrelevant(State);
16560f3b071SBalázs Kéri       C.addTransition(State);
16660f3b071SBalázs Kéri       break;
16760f3b071SBalázs Kéri     default:
16860f3b071SBalázs Kéri       break;
16960f3b071SBalázs Kéri     }
17060f3b071SBalázs Kéri   } else {
17160f3b071SBalázs Kéri     switch (EState) {
17260f3b071SBalázs Kéri     case MustBeChecked:
17360f3b071SBalázs Kéri       // 'errno' is overwritten without a read before but it should have been
17460f3b071SBalázs Kéri       // checked.
17560f3b071SBalázs Kéri       generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(State),
17660f3b071SBalázs Kéri                                  ErrnoLoc->getAsRegion(), nullptr);
17760f3b071SBalázs Kéri       break;
17860f3b071SBalázs Kéri     case MustNotBeChecked:
17960f3b071SBalázs Kéri       // Write to 'errno' when it is not allowed to be read.
18060f3b071SBalázs Kéri       // After this place 'errno' is allowed to be read and written.
18160f3b071SBalázs Kéri       State = setErrnoStateIrrelevant(State);
18260f3b071SBalázs Kéri       C.addTransition(State);
18360f3b071SBalázs Kéri       break;
18460f3b071SBalázs Kéri     default:
18560f3b071SBalázs Kéri       break;
18660f3b071SBalázs Kéri     }
18760f3b071SBalázs Kéri   }
18860f3b071SBalázs Kéri }
18960f3b071SBalázs Kéri 
19060f3b071SBalázs Kéri void ErrnoChecker::checkPreCall(const CallEvent &Call,
19160f3b071SBalázs Kéri                                 CheckerContext &C) const {
19260f3b071SBalázs Kéri   const auto *CallF = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
19360f3b071SBalázs Kéri   if (!CallF)
19460f3b071SBalázs Kéri     return;
19560f3b071SBalázs Kéri 
19660f3b071SBalázs Kéri   CallF = CallF->getCanonicalDecl();
19760f3b071SBalázs Kéri   // If 'errno' must be checked, it should be done as soon as possible, and
19860f3b071SBalázs Kéri   // before any other call to a system function (something in a system header).
19960f3b071SBalázs Kéri   // To avoid use of a long list of functions that may change 'errno'
20060f3b071SBalázs Kéri   // (which may be different with standard library versions) assume that any
20160f3b071SBalázs Kéri   // function can change it.
20260f3b071SBalázs Kéri   // A list of special functions can be used that are allowed here without
20360f3b071SBalázs Kéri   // generation of diagnostic. For now the only such case is 'errno' itself.
20460f3b071SBalázs Kéri   // Probably 'strerror'?
20560f3b071SBalázs Kéri   if (CallF->isExternC() && CallF->isGlobal() &&
20660f3b071SBalázs Kéri       C.getSourceManager().isInSystemHeader(CallF->getLocation()) &&
207*ca4a4052SDonát Nagy       !isErrnoLocationCall(Call)) {
20860f3b071SBalázs Kéri     if (getErrnoState(C.getState()) == MustBeChecked) {
2096ad0788cSKazu Hirata       std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
21060f3b071SBalázs Kéri       assert(ErrnoLoc && "ErrnoLoc should exist if an errno state is set.");
21160f3b071SBalázs Kéri       generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(C.getState()),
21260f3b071SBalázs Kéri                                  ErrnoLoc->getAsRegion(), &Call);
21360f3b071SBalázs Kéri     }
21460f3b071SBalázs Kéri   }
21560f3b071SBalázs Kéri }
21660f3b071SBalázs Kéri 
21760f3b071SBalázs Kéri ProgramStateRef ErrnoChecker::checkRegionChanges(
21860f3b071SBalázs Kéri     ProgramStateRef State, const InvalidatedSymbols *Invalidated,
21960f3b071SBalázs Kéri     ArrayRef<const MemRegion *> ExplicitRegions,
22060f3b071SBalázs Kéri     ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
22160f3b071SBalázs Kéri     const CallEvent *Call) const {
2226ad0788cSKazu Hirata   std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(State);
22360f3b071SBalázs Kéri   if (!ErrnoLoc)
22460f3b071SBalázs Kéri     return State;
22560f3b071SBalázs Kéri   const MemRegion *ErrnoRegion = ErrnoLoc->getAsRegion();
22660f3b071SBalázs Kéri 
22760f3b071SBalázs Kéri   // If 'errno' is invalidated we can not know if it is checked or written into,
22860f3b071SBalázs Kéri   // allow read and write without bug reports.
22960f3b071SBalázs Kéri   if (llvm::is_contained(Regions, ErrnoRegion))
2303c7fe7d0SBalázs Kéri     return clearErrnoState(State);
23160f3b071SBalázs Kéri 
23260f3b071SBalázs Kéri   // Always reset errno state when the system memory space is invalidated.
23360f3b071SBalázs Kéri   // The ErrnoRegion is not always found in the list in this case.
23460f3b071SBalázs Kéri   if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace()))
2353c7fe7d0SBalázs Kéri     return clearErrnoState(State);
23660f3b071SBalázs Kéri 
23760f3b071SBalázs Kéri   return State;
23860f3b071SBalázs Kéri }
23960f3b071SBalázs Kéri 
24060f3b071SBalázs Kéri void ento::registerErrnoChecker(CheckerManager &mgr) {
24160f3b071SBalázs Kéri   const AnalyzerOptions &Opts = mgr.getAnalyzerOptions();
24260f3b071SBalázs Kéri   auto *Checker = mgr.registerChecker<ErrnoChecker>();
24360f3b071SBalázs Kéri   Checker->AllowErrnoReadOutsideConditions = Opts.getCheckerBooleanOption(
24460f3b071SBalázs Kéri       Checker, "AllowErrnoReadOutsideConditionExpressions");
24560f3b071SBalázs Kéri }
24660f3b071SBalázs Kéri 
24760f3b071SBalázs Kéri bool ento::shouldRegisterErrnoChecker(const CheckerManager &mgr) {
24860f3b071SBalázs Kéri   return true;
24960f3b071SBalázs Kéri }
250