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