xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
1*81ad6265SDimitry Andric //=== ErrnoChecker.cpp ------------------------------------------*- C++ -*-===//
2*81ad6265SDimitry Andric //
3*81ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*81ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*81ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*81ad6265SDimitry Andric //
7*81ad6265SDimitry Andric //===----------------------------------------------------------------------===//
8*81ad6265SDimitry Andric //
9*81ad6265SDimitry Andric // This defines an "errno checker" that can detect some invalid use of the
10*81ad6265SDimitry Andric // system-defined value 'errno'. This checker works together with the
11*81ad6265SDimitry Andric // ErrnoModeling checker and other checkers like StdCLibraryFunctions.
12*81ad6265SDimitry Andric //
13*81ad6265SDimitry Andric //===----------------------------------------------------------------------===//
14*81ad6265SDimitry Andric 
15*81ad6265SDimitry Andric #include "ErrnoModeling.h"
16*81ad6265SDimitry Andric #include "clang/AST/ParentMapContext.h"
17*81ad6265SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18*81ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
19*81ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
20*81ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
21*81ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22*81ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
23*81ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
24*81ad6265SDimitry Andric #include "llvm/ADT/STLExtras.h"
25*81ad6265SDimitry Andric 
26*81ad6265SDimitry Andric using namespace clang;
27*81ad6265SDimitry Andric using namespace ento;
28*81ad6265SDimitry Andric using namespace errno_modeling;
29*81ad6265SDimitry Andric 
30*81ad6265SDimitry Andric namespace {
31*81ad6265SDimitry Andric 
32*81ad6265SDimitry Andric class ErrnoChecker
33*81ad6265SDimitry Andric     : public Checker<check::Location, check::PreCall, check::RegionChanges> {
34*81ad6265SDimitry Andric public:
35*81ad6265SDimitry Andric   void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
36*81ad6265SDimitry Andric                      CheckerContext &) const;
37*81ad6265SDimitry Andric   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
38*81ad6265SDimitry Andric   ProgramStateRef
39*81ad6265SDimitry Andric   checkRegionChanges(ProgramStateRef State,
40*81ad6265SDimitry Andric                      const InvalidatedSymbols *Invalidated,
41*81ad6265SDimitry Andric                      ArrayRef<const MemRegion *> ExplicitRegions,
42*81ad6265SDimitry Andric                      ArrayRef<const MemRegion *> Regions,
43*81ad6265SDimitry Andric                      const LocationContext *LCtx, const CallEvent *Call) const;
44*81ad6265SDimitry Andric   void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const;
45*81ad6265SDimitry Andric 
46*81ad6265SDimitry Andric   /// Indicates if a read (load) of \c errno is allowed in a non-condition part
47*81ad6265SDimitry Andric   /// of \c if, \c switch, loop and conditional statements when the errno
48*81ad6265SDimitry Andric   /// value may be undefined.
49*81ad6265SDimitry Andric   bool AllowErrnoReadOutsideConditions = true;
50*81ad6265SDimitry Andric 
51*81ad6265SDimitry Andric private:
52*81ad6265SDimitry Andric   void generateErrnoNotCheckedBug(CheckerContext &C, ProgramStateRef State,
53*81ad6265SDimitry Andric                                   const MemRegion *ErrnoRegion,
54*81ad6265SDimitry Andric                                   const CallEvent *CallMayChangeErrno) const;
55*81ad6265SDimitry Andric 
56*81ad6265SDimitry Andric   BugType BT_InvalidErrnoRead{this, "Value of 'errno' could be undefined",
57*81ad6265SDimitry Andric                               "Error handling"};
58*81ad6265SDimitry Andric   BugType BT_ErrnoNotChecked{this, "Value of 'errno' was not checked",
59*81ad6265SDimitry Andric                              "Error handling"};
60*81ad6265SDimitry Andric };
61*81ad6265SDimitry Andric 
62*81ad6265SDimitry Andric } // namespace
63*81ad6265SDimitry Andric 
64*81ad6265SDimitry Andric static ProgramStateRef setErrnoStateIrrelevant(ProgramStateRef State) {
65*81ad6265SDimitry Andric   return setErrnoState(State, Irrelevant);
66*81ad6265SDimitry Andric }
67*81ad6265SDimitry Andric 
68*81ad6265SDimitry Andric /// Check if a statement (expression) or an ancestor of it is in a condition
69*81ad6265SDimitry Andric /// part of a (conditional, loop, switch) statement.
70*81ad6265SDimitry Andric static bool isInCondition(const Stmt *S, CheckerContext &C) {
71*81ad6265SDimitry Andric   ParentMapContext &ParentCtx = C.getASTContext().getParentMapContext();
72*81ad6265SDimitry Andric   bool CondFound = false;
73*81ad6265SDimitry Andric   while (S && !CondFound) {
74*81ad6265SDimitry Andric     const DynTypedNodeList Parents = ParentCtx.getParents(*S);
75*81ad6265SDimitry Andric     if (Parents.empty())
76*81ad6265SDimitry Andric       break;
77*81ad6265SDimitry Andric     const auto *ParentS = Parents[0].get<Stmt>();
78*81ad6265SDimitry Andric     if (!ParentS || isa<CallExpr>(ParentS))
79*81ad6265SDimitry Andric       break;
80*81ad6265SDimitry Andric     switch (ParentS->getStmtClass()) {
81*81ad6265SDimitry Andric     case Expr::IfStmtClass:
82*81ad6265SDimitry Andric       CondFound = (S == cast<IfStmt>(ParentS)->getCond());
83*81ad6265SDimitry Andric       break;
84*81ad6265SDimitry Andric     case Expr::ForStmtClass:
85*81ad6265SDimitry Andric       CondFound = (S == cast<ForStmt>(ParentS)->getCond());
86*81ad6265SDimitry Andric       break;
87*81ad6265SDimitry Andric     case Expr::DoStmtClass:
88*81ad6265SDimitry Andric       CondFound = (S == cast<DoStmt>(ParentS)->getCond());
89*81ad6265SDimitry Andric       break;
90*81ad6265SDimitry Andric     case Expr::WhileStmtClass:
91*81ad6265SDimitry Andric       CondFound = (S == cast<WhileStmt>(ParentS)->getCond());
92*81ad6265SDimitry Andric       break;
93*81ad6265SDimitry Andric     case Expr::SwitchStmtClass:
94*81ad6265SDimitry Andric       CondFound = (S == cast<SwitchStmt>(ParentS)->getCond());
95*81ad6265SDimitry Andric       break;
96*81ad6265SDimitry Andric     case Expr::ConditionalOperatorClass:
97*81ad6265SDimitry Andric       CondFound = (S == cast<ConditionalOperator>(ParentS)->getCond());
98*81ad6265SDimitry Andric       break;
99*81ad6265SDimitry Andric     case Expr::BinaryConditionalOperatorClass:
100*81ad6265SDimitry Andric       CondFound = (S == cast<BinaryConditionalOperator>(ParentS)->getCommon());
101*81ad6265SDimitry Andric       break;
102*81ad6265SDimitry Andric     default:
103*81ad6265SDimitry Andric       break;
104*81ad6265SDimitry Andric     }
105*81ad6265SDimitry Andric     S = ParentS;
106*81ad6265SDimitry Andric   }
107*81ad6265SDimitry Andric   return CondFound;
108*81ad6265SDimitry Andric }
109*81ad6265SDimitry Andric 
110*81ad6265SDimitry Andric void ErrnoChecker::generateErrnoNotCheckedBug(
111*81ad6265SDimitry Andric     CheckerContext &C, ProgramStateRef State, const MemRegion *ErrnoRegion,
112*81ad6265SDimitry Andric     const CallEvent *CallMayChangeErrno) const {
113*81ad6265SDimitry Andric   if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
114*81ad6265SDimitry Andric     SmallString<100> StrBuf;
115*81ad6265SDimitry Andric     llvm::raw_svector_ostream OS(StrBuf);
116*81ad6265SDimitry Andric     if (CallMayChangeErrno) {
117*81ad6265SDimitry Andric       OS << "Value of 'errno' was not checked and may be overwritten by "
118*81ad6265SDimitry Andric             "function '";
119*81ad6265SDimitry Andric       const auto *CallD =
120*81ad6265SDimitry Andric           dyn_cast_or_null<FunctionDecl>(CallMayChangeErrno->getDecl());
121*81ad6265SDimitry Andric       assert(CallD && CallD->getIdentifier());
122*81ad6265SDimitry Andric       OS << CallD->getIdentifier()->getName() << "'";
123*81ad6265SDimitry Andric     } else {
124*81ad6265SDimitry Andric       OS << "Value of 'errno' was not checked and is overwritten here";
125*81ad6265SDimitry Andric     }
126*81ad6265SDimitry Andric     auto BR = std::make_unique<PathSensitiveBugReport>(BT_ErrnoNotChecked,
127*81ad6265SDimitry Andric                                                        OS.str(), N);
128*81ad6265SDimitry Andric     BR->markInteresting(ErrnoRegion);
129*81ad6265SDimitry Andric     C.emitReport(std::move(BR));
130*81ad6265SDimitry Andric   }
131*81ad6265SDimitry Andric }
132*81ad6265SDimitry Andric 
133*81ad6265SDimitry Andric void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
134*81ad6265SDimitry Andric                                  CheckerContext &C) const {
135*81ad6265SDimitry Andric   Optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
136*81ad6265SDimitry Andric   if (!ErrnoLoc)
137*81ad6265SDimitry Andric     return;
138*81ad6265SDimitry Andric 
139*81ad6265SDimitry Andric   auto L = Loc.getAs<ento::Loc>();
140*81ad6265SDimitry Andric   if (!L || *ErrnoLoc != *L)
141*81ad6265SDimitry Andric     return;
142*81ad6265SDimitry Andric 
143*81ad6265SDimitry Andric   ProgramStateRef State = C.getState();
144*81ad6265SDimitry Andric   ErrnoCheckState EState = getErrnoState(State);
145*81ad6265SDimitry Andric 
146*81ad6265SDimitry Andric   if (IsLoad) {
147*81ad6265SDimitry Andric     switch (EState) {
148*81ad6265SDimitry Andric     case MustNotBeChecked:
149*81ad6265SDimitry Andric       // Read of 'errno' when it may have undefined value.
150*81ad6265SDimitry Andric       if (!AllowErrnoReadOutsideConditions || isInCondition(S, C)) {
151*81ad6265SDimitry Andric         if (ExplodedNode *N = C.generateErrorNode()) {
152*81ad6265SDimitry Andric           auto BR = std::make_unique<PathSensitiveBugReport>(
153*81ad6265SDimitry Andric               BT_InvalidErrnoRead,
154*81ad6265SDimitry Andric               "An undefined value may be read from 'errno'", N);
155*81ad6265SDimitry Andric           BR->markInteresting(ErrnoLoc->getAsRegion());
156*81ad6265SDimitry Andric           C.emitReport(std::move(BR));
157*81ad6265SDimitry Andric         }
158*81ad6265SDimitry Andric       }
159*81ad6265SDimitry Andric       break;
160*81ad6265SDimitry Andric     case MustBeChecked:
161*81ad6265SDimitry Andric       // 'errno' has to be checked. A load is required for this, with no more
162*81ad6265SDimitry Andric       // information we can assume that it is checked somehow.
163*81ad6265SDimitry Andric       // After this place 'errno' is allowed to be read and written.
164*81ad6265SDimitry Andric       State = setErrnoStateIrrelevant(State);
165*81ad6265SDimitry Andric       C.addTransition(State);
166*81ad6265SDimitry Andric       break;
167*81ad6265SDimitry Andric     default:
168*81ad6265SDimitry Andric       break;
169*81ad6265SDimitry Andric     }
170*81ad6265SDimitry Andric   } else {
171*81ad6265SDimitry Andric     switch (EState) {
172*81ad6265SDimitry Andric     case MustBeChecked:
173*81ad6265SDimitry Andric       // 'errno' is overwritten without a read before but it should have been
174*81ad6265SDimitry Andric       // checked.
175*81ad6265SDimitry Andric       generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(State),
176*81ad6265SDimitry Andric                                  ErrnoLoc->getAsRegion(), nullptr);
177*81ad6265SDimitry Andric       break;
178*81ad6265SDimitry Andric     case MustNotBeChecked:
179*81ad6265SDimitry Andric       // Write to 'errno' when it is not allowed to be read.
180*81ad6265SDimitry Andric       // After this place 'errno' is allowed to be read and written.
181*81ad6265SDimitry Andric       State = setErrnoStateIrrelevant(State);
182*81ad6265SDimitry Andric       C.addTransition(State);
183*81ad6265SDimitry Andric       break;
184*81ad6265SDimitry Andric     default:
185*81ad6265SDimitry Andric       break;
186*81ad6265SDimitry Andric     }
187*81ad6265SDimitry Andric   }
188*81ad6265SDimitry Andric }
189*81ad6265SDimitry Andric 
190*81ad6265SDimitry Andric void ErrnoChecker::checkPreCall(const CallEvent &Call,
191*81ad6265SDimitry Andric                                 CheckerContext &C) const {
192*81ad6265SDimitry Andric   const auto *CallF = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
193*81ad6265SDimitry Andric   if (!CallF)
194*81ad6265SDimitry Andric     return;
195*81ad6265SDimitry Andric 
196*81ad6265SDimitry Andric   CallF = CallF->getCanonicalDecl();
197*81ad6265SDimitry Andric   // If 'errno' must be checked, it should be done as soon as possible, and
198*81ad6265SDimitry Andric   // before any other call to a system function (something in a system header).
199*81ad6265SDimitry Andric   // To avoid use of a long list of functions that may change 'errno'
200*81ad6265SDimitry Andric   // (which may be different with standard library versions) assume that any
201*81ad6265SDimitry Andric   // function can change it.
202*81ad6265SDimitry Andric   // A list of special functions can be used that are allowed here without
203*81ad6265SDimitry Andric   // generation of diagnostic. For now the only such case is 'errno' itself.
204*81ad6265SDimitry Andric   // Probably 'strerror'?
205*81ad6265SDimitry Andric   if (CallF->isExternC() && CallF->isGlobal() &&
206*81ad6265SDimitry Andric       C.getSourceManager().isInSystemHeader(CallF->getLocation()) &&
207*81ad6265SDimitry Andric       !isErrno(CallF)) {
208*81ad6265SDimitry Andric     if (getErrnoState(C.getState()) == MustBeChecked) {
209*81ad6265SDimitry Andric       Optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
210*81ad6265SDimitry Andric       assert(ErrnoLoc && "ErrnoLoc should exist if an errno state is set.");
211*81ad6265SDimitry Andric       generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(C.getState()),
212*81ad6265SDimitry Andric                                  ErrnoLoc->getAsRegion(), &Call);
213*81ad6265SDimitry Andric     }
214*81ad6265SDimitry Andric   }
215*81ad6265SDimitry Andric }
216*81ad6265SDimitry Andric 
217*81ad6265SDimitry Andric ProgramStateRef ErrnoChecker::checkRegionChanges(
218*81ad6265SDimitry Andric     ProgramStateRef State, const InvalidatedSymbols *Invalidated,
219*81ad6265SDimitry Andric     ArrayRef<const MemRegion *> ExplicitRegions,
220*81ad6265SDimitry Andric     ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
221*81ad6265SDimitry Andric     const CallEvent *Call) const {
222*81ad6265SDimitry Andric   Optional<ento::Loc> ErrnoLoc = getErrnoLoc(State);
223*81ad6265SDimitry Andric   if (!ErrnoLoc)
224*81ad6265SDimitry Andric     return State;
225*81ad6265SDimitry Andric   const MemRegion *ErrnoRegion = ErrnoLoc->getAsRegion();
226*81ad6265SDimitry Andric 
227*81ad6265SDimitry Andric   // If 'errno' is invalidated we can not know if it is checked or written into,
228*81ad6265SDimitry Andric   // allow read and write without bug reports.
229*81ad6265SDimitry Andric   if (llvm::is_contained(Regions, ErrnoRegion))
230*81ad6265SDimitry Andric     return setErrnoStateIrrelevant(State);
231*81ad6265SDimitry Andric 
232*81ad6265SDimitry Andric   // Always reset errno state when the system memory space is invalidated.
233*81ad6265SDimitry Andric   // The ErrnoRegion is not always found in the list in this case.
234*81ad6265SDimitry Andric   if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace()))
235*81ad6265SDimitry Andric     return setErrnoStateIrrelevant(State);
236*81ad6265SDimitry Andric 
237*81ad6265SDimitry Andric   return State;
238*81ad6265SDimitry Andric }
239*81ad6265SDimitry Andric 
240*81ad6265SDimitry Andric void ento::registerErrnoChecker(CheckerManager &mgr) {
241*81ad6265SDimitry Andric   const AnalyzerOptions &Opts = mgr.getAnalyzerOptions();
242*81ad6265SDimitry Andric   auto *Checker = mgr.registerChecker<ErrnoChecker>();
243*81ad6265SDimitry Andric   Checker->AllowErrnoReadOutsideConditions = Opts.getCheckerBooleanOption(
244*81ad6265SDimitry Andric       Checker, "AllowErrnoReadOutsideConditionExpressions");
245*81ad6265SDimitry Andric }
246*81ad6265SDimitry Andric 
247*81ad6265SDimitry Andric bool ento::shouldRegisterErrnoChecker(const CheckerManager &mgr) {
248*81ad6265SDimitry Andric   return true;
249*81ad6265SDimitry Andric }
250