clang 20.0.0git
ErrnoChecker.cpp
Go to the documentation of this file.
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"
24#include "llvm/ADT/STLExtras.h"
25#include <optional>
26
27using namespace clang;
28using namespace ento;
29using namespace errno_modeling;
30
31namespace {
32
33class ErrnoChecker
34 : public Checker<check::Location, check::PreCall, check::RegionChanges> {
35public:
36 void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
37 CheckerContext &) const;
38 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
40 checkRegionChanges(ProgramStateRef State,
41 const InvalidatedSymbols *Invalidated,
42 ArrayRef<const MemRegion *> ExplicitRegions,
44 const LocationContext *LCtx, const CallEvent *Call) 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
51private:
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
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.
70static 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
110void 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
133void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
134 CheckerContext &C) const {
135 std::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
190void 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()) &&
208 if (getErrnoState(C.getState()) == MustBeChecked) {
209 std::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
217ProgramStateRef 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 std::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 clearErrnoState(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 clearErrnoState(State);
236
237 return State;
238}
239
240void 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
247bool ento::shouldRegisterErrnoChecker(const CheckerManager &mgr) {
248 return true;
249}
static bool isInCondition(const Stmt *S, CheckerContext &C)
Check if a statement (expression) or an ancestor of it is in a condition part of a (conditional,...
static ProgramStateRef setErrnoStateIrrelevant(ProgramStateRef State)
Stores options for the analyzer from the command line.
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
DynTypedNodeList getParents(const NodeT &Node)
Returns the parents of the given node (within the traversal scope).
Stmt - This represents one statement.
Definition: Stmt.h:84
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
virtual const Decl * getDecl() const
Returns the declaration of the function or method that will be called.
Definition: CallEvent.h:224
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:97
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemSpaceRegion * getMemorySpace() const
Definition: MemRegion.cpp:1351
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:56
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition: SVals.h:87
std::optional< Loc > getErrnoLoc(ProgramStateRef State)
Returns the location that points to the MemoryRegion where the 'errno' value is stored.
ProgramStateRef clearErrnoState(ProgramStateRef State)
Clear state of errno (make it irrelevant).
ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState)
Set the errno check state, do not modify the errno value.
bool isErrnoLocationCall(const CallEvent &CE)
Determine if Call is a call to an internal function that returns the location of errno (in environmen...
ErrnoCheckState
Describe how reads and writes of errno are handled by the checker.
Definition: ErrnoModeling.h:26
@ MustBeChecked
Value of 'errno' should be checked to find out if a previous function call has failed.
Definition: ErrnoModeling.h:36
@ Irrelevant
We do not know anything about 'errno'.
Definition: ErrnoModeling.h:29
@ MustNotBeChecked
Value of 'errno' is not allowed to be read, it can contain an unspecified value.
Definition: ErrnoModeling.h:42
ErrnoCheckState getErrnoState(ProgramStateRef State)
Returns the errno check state, Errno_Irrelevant if 'errno' was not found (this is not the only case f...
llvm::DenseSet< SymbolRef > InvalidatedSymbols
Definition: Store.h:51
The JSON file list parser is used to communicate input to InstallAPI.