181ad6265SDimitry Andric //=== ErrnoChecker.cpp ------------------------------------------*- C++ -*-===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric //
981ad6265SDimitry Andric // This defines an "errno checker" that can detect some invalid use of the
1081ad6265SDimitry Andric // system-defined value 'errno'. This checker works together with the
1181ad6265SDimitry Andric // ErrnoModeling checker and other checkers like StdCLibraryFunctions.
1281ad6265SDimitry Andric //
1381ad6265SDimitry Andric //===----------------------------------------------------------------------===//
1481ad6265SDimitry Andric 
1581ad6265SDimitry Andric #include "ErrnoModeling.h"
1681ad6265SDimitry Andric #include "clang/AST/ParentMapContext.h"
1781ad6265SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
1881ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
1981ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
2081ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
2181ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2281ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
2381ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
2481ad6265SDimitry Andric #include "llvm/ADT/STLExtras.h"
25*bdd1243dSDimitry Andric #include <optional>
2681ad6265SDimitry Andric 
2781ad6265SDimitry Andric using namespace clang;
2881ad6265SDimitry Andric using namespace ento;
2981ad6265SDimitry Andric using namespace errno_modeling;
3081ad6265SDimitry Andric 
3181ad6265SDimitry Andric namespace {
3281ad6265SDimitry Andric 
3381ad6265SDimitry Andric class ErrnoChecker
3481ad6265SDimitry Andric     : public Checker<check::Location, check::PreCall, check::RegionChanges> {
3581ad6265SDimitry Andric public:
3681ad6265SDimitry Andric   void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
3781ad6265SDimitry Andric                      CheckerContext &) const;
3881ad6265SDimitry Andric   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
3981ad6265SDimitry Andric   ProgramStateRef
4081ad6265SDimitry Andric   checkRegionChanges(ProgramStateRef State,
4181ad6265SDimitry Andric                      const InvalidatedSymbols *Invalidated,
4281ad6265SDimitry Andric                      ArrayRef<const MemRegion *> ExplicitRegions,
4381ad6265SDimitry Andric                      ArrayRef<const MemRegion *> Regions,
4481ad6265SDimitry Andric                      const LocationContext *LCtx, const CallEvent *Call) const;
4581ad6265SDimitry Andric   void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const;
4681ad6265SDimitry Andric 
4781ad6265SDimitry Andric   /// Indicates if a read (load) of \c errno is allowed in a non-condition part
4881ad6265SDimitry Andric   /// of \c if, \c switch, loop and conditional statements when the errno
4981ad6265SDimitry Andric   /// value may be undefined.
5081ad6265SDimitry Andric   bool AllowErrnoReadOutsideConditions = true;
5181ad6265SDimitry Andric 
5281ad6265SDimitry Andric private:
5381ad6265SDimitry Andric   void generateErrnoNotCheckedBug(CheckerContext &C, ProgramStateRef State,
5481ad6265SDimitry Andric                                   const MemRegion *ErrnoRegion,
5581ad6265SDimitry Andric                                   const CallEvent *CallMayChangeErrno) const;
5681ad6265SDimitry Andric 
5781ad6265SDimitry Andric   BugType BT_InvalidErrnoRead{this, "Value of 'errno' could be undefined",
5881ad6265SDimitry Andric                               "Error handling"};
5981ad6265SDimitry Andric   BugType BT_ErrnoNotChecked{this, "Value of 'errno' was not checked",
6081ad6265SDimitry Andric                              "Error handling"};
6181ad6265SDimitry Andric };
6281ad6265SDimitry Andric 
6381ad6265SDimitry Andric } // namespace
6481ad6265SDimitry Andric 
setErrnoStateIrrelevant(ProgramStateRef State)6581ad6265SDimitry Andric static ProgramStateRef setErrnoStateIrrelevant(ProgramStateRef State) {
6681ad6265SDimitry Andric   return setErrnoState(State, Irrelevant);
6781ad6265SDimitry Andric }
6881ad6265SDimitry Andric 
6981ad6265SDimitry Andric /// Check if a statement (expression) or an ancestor of it is in a condition
7081ad6265SDimitry Andric /// part of a (conditional, loop, switch) statement.
isInCondition(const Stmt * S,CheckerContext & C)7181ad6265SDimitry Andric static bool isInCondition(const Stmt *S, CheckerContext &C) {
7281ad6265SDimitry Andric   ParentMapContext &ParentCtx = C.getASTContext().getParentMapContext();
7381ad6265SDimitry Andric   bool CondFound = false;
7481ad6265SDimitry Andric   while (S && !CondFound) {
7581ad6265SDimitry Andric     const DynTypedNodeList Parents = ParentCtx.getParents(*S);
7681ad6265SDimitry Andric     if (Parents.empty())
7781ad6265SDimitry Andric       break;
7881ad6265SDimitry Andric     const auto *ParentS = Parents[0].get<Stmt>();
7981ad6265SDimitry Andric     if (!ParentS || isa<CallExpr>(ParentS))
8081ad6265SDimitry Andric       break;
8181ad6265SDimitry Andric     switch (ParentS->getStmtClass()) {
8281ad6265SDimitry Andric     case Expr::IfStmtClass:
8381ad6265SDimitry Andric       CondFound = (S == cast<IfStmt>(ParentS)->getCond());
8481ad6265SDimitry Andric       break;
8581ad6265SDimitry Andric     case Expr::ForStmtClass:
8681ad6265SDimitry Andric       CondFound = (S == cast<ForStmt>(ParentS)->getCond());
8781ad6265SDimitry Andric       break;
8881ad6265SDimitry Andric     case Expr::DoStmtClass:
8981ad6265SDimitry Andric       CondFound = (S == cast<DoStmt>(ParentS)->getCond());
9081ad6265SDimitry Andric       break;
9181ad6265SDimitry Andric     case Expr::WhileStmtClass:
9281ad6265SDimitry Andric       CondFound = (S == cast<WhileStmt>(ParentS)->getCond());
9381ad6265SDimitry Andric       break;
9481ad6265SDimitry Andric     case Expr::SwitchStmtClass:
9581ad6265SDimitry Andric       CondFound = (S == cast<SwitchStmt>(ParentS)->getCond());
9681ad6265SDimitry Andric       break;
9781ad6265SDimitry Andric     case Expr::ConditionalOperatorClass:
9881ad6265SDimitry Andric       CondFound = (S == cast<ConditionalOperator>(ParentS)->getCond());
9981ad6265SDimitry Andric       break;
10081ad6265SDimitry Andric     case Expr::BinaryConditionalOperatorClass:
10181ad6265SDimitry Andric       CondFound = (S == cast<BinaryConditionalOperator>(ParentS)->getCommon());
10281ad6265SDimitry Andric       break;
10381ad6265SDimitry Andric     default:
10481ad6265SDimitry Andric       break;
10581ad6265SDimitry Andric     }
10681ad6265SDimitry Andric     S = ParentS;
10781ad6265SDimitry Andric   }
10881ad6265SDimitry Andric   return CondFound;
10981ad6265SDimitry Andric }
11081ad6265SDimitry Andric 
generateErrnoNotCheckedBug(CheckerContext & C,ProgramStateRef State,const MemRegion * ErrnoRegion,const CallEvent * CallMayChangeErrno) const11181ad6265SDimitry Andric void ErrnoChecker::generateErrnoNotCheckedBug(
11281ad6265SDimitry Andric     CheckerContext &C, ProgramStateRef State, const MemRegion *ErrnoRegion,
11381ad6265SDimitry Andric     const CallEvent *CallMayChangeErrno) const {
11481ad6265SDimitry Andric   if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
11581ad6265SDimitry Andric     SmallString<100> StrBuf;
11681ad6265SDimitry Andric     llvm::raw_svector_ostream OS(StrBuf);
11781ad6265SDimitry Andric     if (CallMayChangeErrno) {
11881ad6265SDimitry Andric       OS << "Value of 'errno' was not checked and may be overwritten by "
11981ad6265SDimitry Andric             "function '";
12081ad6265SDimitry Andric       const auto *CallD =
12181ad6265SDimitry Andric           dyn_cast_or_null<FunctionDecl>(CallMayChangeErrno->getDecl());
12281ad6265SDimitry Andric       assert(CallD && CallD->getIdentifier());
12381ad6265SDimitry Andric       OS << CallD->getIdentifier()->getName() << "'";
12481ad6265SDimitry Andric     } else {
12581ad6265SDimitry Andric       OS << "Value of 'errno' was not checked and is overwritten here";
12681ad6265SDimitry Andric     }
12781ad6265SDimitry Andric     auto BR = std::make_unique<PathSensitiveBugReport>(BT_ErrnoNotChecked,
12881ad6265SDimitry Andric                                                        OS.str(), N);
12981ad6265SDimitry Andric     BR->markInteresting(ErrnoRegion);
13081ad6265SDimitry Andric     C.emitReport(std::move(BR));
13181ad6265SDimitry Andric   }
13281ad6265SDimitry Andric }
13381ad6265SDimitry Andric 
checkLocation(SVal Loc,bool IsLoad,const Stmt * S,CheckerContext & C) const13481ad6265SDimitry Andric void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
13581ad6265SDimitry Andric                                  CheckerContext &C) const {
136*bdd1243dSDimitry Andric   std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
13781ad6265SDimitry Andric   if (!ErrnoLoc)
13881ad6265SDimitry Andric     return;
13981ad6265SDimitry Andric 
14081ad6265SDimitry Andric   auto L = Loc.getAs<ento::Loc>();
14181ad6265SDimitry Andric   if (!L || *ErrnoLoc != *L)
14281ad6265SDimitry Andric     return;
14381ad6265SDimitry Andric 
14481ad6265SDimitry Andric   ProgramStateRef State = C.getState();
14581ad6265SDimitry Andric   ErrnoCheckState EState = getErrnoState(State);
14681ad6265SDimitry Andric 
14781ad6265SDimitry Andric   if (IsLoad) {
14881ad6265SDimitry Andric     switch (EState) {
14981ad6265SDimitry Andric     case MustNotBeChecked:
15081ad6265SDimitry Andric       // Read of 'errno' when it may have undefined value.
15181ad6265SDimitry Andric       if (!AllowErrnoReadOutsideConditions || isInCondition(S, C)) {
15281ad6265SDimitry Andric         if (ExplodedNode *N = C.generateErrorNode()) {
15381ad6265SDimitry Andric           auto BR = std::make_unique<PathSensitiveBugReport>(
15481ad6265SDimitry Andric               BT_InvalidErrnoRead,
15581ad6265SDimitry Andric               "An undefined value may be read from 'errno'", N);
15681ad6265SDimitry Andric           BR->markInteresting(ErrnoLoc->getAsRegion());
15781ad6265SDimitry Andric           C.emitReport(std::move(BR));
15881ad6265SDimitry Andric         }
15981ad6265SDimitry Andric       }
16081ad6265SDimitry Andric       break;
16181ad6265SDimitry Andric     case MustBeChecked:
16281ad6265SDimitry Andric       // 'errno' has to be checked. A load is required for this, with no more
16381ad6265SDimitry Andric       // information we can assume that it is checked somehow.
16481ad6265SDimitry Andric       // After this place 'errno' is allowed to be read and written.
16581ad6265SDimitry Andric       State = setErrnoStateIrrelevant(State);
16681ad6265SDimitry Andric       C.addTransition(State);
16781ad6265SDimitry Andric       break;
16881ad6265SDimitry Andric     default:
16981ad6265SDimitry Andric       break;
17081ad6265SDimitry Andric     }
17181ad6265SDimitry Andric   } else {
17281ad6265SDimitry Andric     switch (EState) {
17381ad6265SDimitry Andric     case MustBeChecked:
17481ad6265SDimitry Andric       // 'errno' is overwritten without a read before but it should have been
17581ad6265SDimitry Andric       // checked.
17681ad6265SDimitry Andric       generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(State),
17781ad6265SDimitry Andric                                  ErrnoLoc->getAsRegion(), nullptr);
17881ad6265SDimitry Andric       break;
17981ad6265SDimitry Andric     case MustNotBeChecked:
18081ad6265SDimitry Andric       // Write to 'errno' when it is not allowed to be read.
18181ad6265SDimitry Andric       // After this place 'errno' is allowed to be read and written.
18281ad6265SDimitry Andric       State = setErrnoStateIrrelevant(State);
18381ad6265SDimitry Andric       C.addTransition(State);
18481ad6265SDimitry Andric       break;
18581ad6265SDimitry Andric     default:
18681ad6265SDimitry Andric       break;
18781ad6265SDimitry Andric     }
18881ad6265SDimitry Andric   }
18981ad6265SDimitry Andric }
19081ad6265SDimitry Andric 
checkPreCall(const CallEvent & Call,CheckerContext & C) const19181ad6265SDimitry Andric void ErrnoChecker::checkPreCall(const CallEvent &Call,
19281ad6265SDimitry Andric                                 CheckerContext &C) const {
19381ad6265SDimitry Andric   const auto *CallF = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
19481ad6265SDimitry Andric   if (!CallF)
19581ad6265SDimitry Andric     return;
19681ad6265SDimitry Andric 
19781ad6265SDimitry Andric   CallF = CallF->getCanonicalDecl();
19881ad6265SDimitry Andric   // If 'errno' must be checked, it should be done as soon as possible, and
19981ad6265SDimitry Andric   // before any other call to a system function (something in a system header).
20081ad6265SDimitry Andric   // To avoid use of a long list of functions that may change 'errno'
20181ad6265SDimitry Andric   // (which may be different with standard library versions) assume that any
20281ad6265SDimitry Andric   // function can change it.
20381ad6265SDimitry Andric   // A list of special functions can be used that are allowed here without
20481ad6265SDimitry Andric   // generation of diagnostic. For now the only such case is 'errno' itself.
20581ad6265SDimitry Andric   // Probably 'strerror'?
20681ad6265SDimitry Andric   if (CallF->isExternC() && CallF->isGlobal() &&
20781ad6265SDimitry Andric       C.getSourceManager().isInSystemHeader(CallF->getLocation()) &&
20881ad6265SDimitry Andric       !isErrno(CallF)) {
20981ad6265SDimitry Andric     if (getErrnoState(C.getState()) == MustBeChecked) {
210*bdd1243dSDimitry Andric       std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
21181ad6265SDimitry Andric       assert(ErrnoLoc && "ErrnoLoc should exist if an errno state is set.");
21281ad6265SDimitry Andric       generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(C.getState()),
21381ad6265SDimitry Andric                                  ErrnoLoc->getAsRegion(), &Call);
21481ad6265SDimitry Andric     }
21581ad6265SDimitry Andric   }
21681ad6265SDimitry Andric }
21781ad6265SDimitry Andric 
checkRegionChanges(ProgramStateRef State,const InvalidatedSymbols * Invalidated,ArrayRef<const MemRegion * > ExplicitRegions,ArrayRef<const MemRegion * > Regions,const LocationContext * LCtx,const CallEvent * Call) const21881ad6265SDimitry Andric ProgramStateRef ErrnoChecker::checkRegionChanges(
21981ad6265SDimitry Andric     ProgramStateRef State, const InvalidatedSymbols *Invalidated,
22081ad6265SDimitry Andric     ArrayRef<const MemRegion *> ExplicitRegions,
22181ad6265SDimitry Andric     ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
22281ad6265SDimitry Andric     const CallEvent *Call) const {
223*bdd1243dSDimitry Andric   std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(State);
22481ad6265SDimitry Andric   if (!ErrnoLoc)
22581ad6265SDimitry Andric     return State;
22681ad6265SDimitry Andric   const MemRegion *ErrnoRegion = ErrnoLoc->getAsRegion();
22781ad6265SDimitry Andric 
22881ad6265SDimitry Andric   // If 'errno' is invalidated we can not know if it is checked or written into,
22981ad6265SDimitry Andric   // allow read and write without bug reports.
23081ad6265SDimitry Andric   if (llvm::is_contained(Regions, ErrnoRegion))
231*bdd1243dSDimitry Andric     return clearErrnoState(State);
23281ad6265SDimitry Andric 
23381ad6265SDimitry Andric   // Always reset errno state when the system memory space is invalidated.
23481ad6265SDimitry Andric   // The ErrnoRegion is not always found in the list in this case.
23581ad6265SDimitry Andric   if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace()))
236*bdd1243dSDimitry Andric     return clearErrnoState(State);
23781ad6265SDimitry Andric 
23881ad6265SDimitry Andric   return State;
23981ad6265SDimitry Andric }
24081ad6265SDimitry Andric 
registerErrnoChecker(CheckerManager & mgr)24181ad6265SDimitry Andric void ento::registerErrnoChecker(CheckerManager &mgr) {
24281ad6265SDimitry Andric   const AnalyzerOptions &Opts = mgr.getAnalyzerOptions();
24381ad6265SDimitry Andric   auto *Checker = mgr.registerChecker<ErrnoChecker>();
24481ad6265SDimitry Andric   Checker->AllowErrnoReadOutsideConditions = Opts.getCheckerBooleanOption(
24581ad6265SDimitry Andric       Checker, "AllowErrnoReadOutsideConditionExpressions");
24681ad6265SDimitry Andric }
24781ad6265SDimitry Andric 
shouldRegisterErrnoChecker(const CheckerManager & mgr)24881ad6265SDimitry Andric bool ento::shouldRegisterErrnoChecker(const CheckerManager &mgr) {
24981ad6265SDimitry Andric   return true;
25081ad6265SDimitry Andric }
251