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
setErrnoStateIrrelevant(ProgramStateRef State)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.
isInCondition(const Stmt * S,CheckerContext & C)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
generateErrnoNotCheckedBug(CheckerContext & C,ProgramStateRef State,const MemRegion * ErrnoRegion,const CallEvent * CallMayChangeErrno) const110 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
checkLocation(SVal Loc,bool IsLoad,const Stmt * S,CheckerContext & C) const133 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
checkPreCall(const CallEvent & Call,CheckerContext & C) const190 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
checkRegionChanges(ProgramStateRef State,const InvalidatedSymbols * Invalidated,ArrayRef<const MemRegion * > ExplicitRegions,ArrayRef<const MemRegion * > Regions,const LocationContext * LCtx,const CallEvent * Call) const217 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
registerErrnoChecker(CheckerManager & mgr)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
shouldRegisterErrnoChecker(const CheckerManager & mgr)247 bool ento::shouldRegisterErrnoChecker(const CheckerManager &mgr) {
248 return true;
249 }
250