1 //=== ErrnoChecker.cpp ------------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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.
13 //===----------------------------------------------------------------------===//
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"
27 using namespace clang
;
29 using namespace errno_modeling
;
34 : public Checker
<check::Location
, check::PreCall
, check::RegionChanges
> {
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
,
43 ArrayRef
<const MemRegion
*> Regions
,
44 const LocationContext
*LCtx
, const CallEvent
*Call
) const;
45 void checkBranchCondition(const Stmt
*Condition
, CheckerContext
&Ctx
) const;
47 /// Indicates if a read (load) of \c errno is allowed in a non-condition part
48 /// of \c if, \c switch, loop and conditional statements when the errno
49 /// value may be undefined.
50 bool AllowErrnoReadOutsideConditions
= true;
53 void generateErrnoNotCheckedBug(CheckerContext
&C
, ProgramStateRef State
,
54 const MemRegion
*ErrnoRegion
,
55 const CallEvent
*CallMayChangeErrno
) const;
57 BugType BT_InvalidErrnoRead
{this, "Value of 'errno' could be undefined",
59 BugType BT_ErrnoNotChecked
{this, "Value of 'errno' was not checked",
65 static ProgramStateRef
setErrnoStateIrrelevant(ProgramStateRef State
) {
66 return setErrnoState(State
, Irrelevant
);
69 /// Check if a statement (expression) or an ancestor of it is in a condition
70 /// part of a (conditional, loop, switch) statement.
71 static bool isInCondition(const Stmt
*S
, CheckerContext
&C
) {
72 ParentMapContext
&ParentCtx
= C
.getASTContext().getParentMapContext();
73 bool CondFound
= false;
74 while (S
&& !CondFound
) {
75 const DynTypedNodeList Parents
= ParentCtx
.getParents(*S
);
78 const auto *ParentS
= Parents
[0].get
<Stmt
>();
79 if (!ParentS
|| isa
<CallExpr
>(ParentS
))
81 switch (ParentS
->getStmtClass()) {
82 case Expr::IfStmtClass
:
83 CondFound
= (S
== cast
<IfStmt
>(ParentS
)->getCond());
85 case Expr::ForStmtClass
:
86 CondFound
= (S
== cast
<ForStmt
>(ParentS
)->getCond());
88 case Expr::DoStmtClass
:
89 CondFound
= (S
== cast
<DoStmt
>(ParentS
)->getCond());
91 case Expr::WhileStmtClass
:
92 CondFound
= (S
== cast
<WhileStmt
>(ParentS
)->getCond());
94 case Expr::SwitchStmtClass
:
95 CondFound
= (S
== cast
<SwitchStmt
>(ParentS
)->getCond());
97 case Expr::ConditionalOperatorClass
:
98 CondFound
= (S
== cast
<ConditionalOperator
>(ParentS
)->getCond());
100 case Expr::BinaryConditionalOperatorClass
:
101 CondFound
= (S
== cast
<BinaryConditionalOperator
>(ParentS
)->getCommon());
111 void ErrnoChecker::generateErrnoNotCheckedBug(
112 CheckerContext
&C
, ProgramStateRef State
, const MemRegion
*ErrnoRegion
,
113 const CallEvent
*CallMayChangeErrno
) const {
114 if (ExplodedNode
*N
= C
.generateNonFatalErrorNode(State
)) {
115 SmallString
<100> StrBuf
;
116 llvm::raw_svector_ostream
OS(StrBuf
);
117 if (CallMayChangeErrno
) {
118 OS
<< "Value of 'errno' was not checked and may be overwritten by "
121 dyn_cast_or_null
<FunctionDecl
>(CallMayChangeErrno
->getDecl());
122 assert(CallD
&& CallD
->getIdentifier());
123 OS
<< CallD
->getIdentifier()->getName() << "'";
125 OS
<< "Value of 'errno' was not checked and is overwritten here";
127 auto BR
= std::make_unique
<PathSensitiveBugReport
>(BT_ErrnoNotChecked
,
129 BR
->markInteresting(ErrnoRegion
);
130 C
.emitReport(std::move(BR
));
134 void ErrnoChecker::checkLocation(SVal Loc
, bool IsLoad
, const Stmt
*S
,
135 CheckerContext
&C
) const {
136 std::optional
<ento::Loc
> ErrnoLoc
= getErrnoLoc(C
.getState());
140 auto L
= Loc
.getAs
<ento::Loc
>();
141 if (!L
|| *ErrnoLoc
!= *L
)
144 ProgramStateRef State
= C
.getState();
145 ErrnoCheckState EState
= getErrnoState(State
);
149 case MustNotBeChecked
:
150 // Read of 'errno' when it may have undefined value.
151 if (!AllowErrnoReadOutsideConditions
|| isInCondition(S
, C
)) {
152 if (ExplodedNode
*N
= C
.generateErrorNode()) {
153 auto BR
= std::make_unique
<PathSensitiveBugReport
>(
155 "An undefined value may be read from 'errno'", N
);
156 BR
->markInteresting(ErrnoLoc
->getAsRegion());
157 C
.emitReport(std::move(BR
));
162 // 'errno' has to be checked. A load is required for this, with no more
163 // information we can assume that it is checked somehow.
164 // After this place 'errno' is allowed to be read and written.
165 State
= setErrnoStateIrrelevant(State
);
166 C
.addTransition(State
);
174 // 'errno' is overwritten without a read before but it should have been
176 generateErrnoNotCheckedBug(C
, setErrnoStateIrrelevant(State
),
177 ErrnoLoc
->getAsRegion(), nullptr);
179 case MustNotBeChecked
:
180 // Write to 'errno' when it is not allowed to be read.
181 // After this place 'errno' is allowed to be read and written.
182 State
= setErrnoStateIrrelevant(State
);
183 C
.addTransition(State
);
191 void ErrnoChecker::checkPreCall(const CallEvent
&Call
,
192 CheckerContext
&C
) const {
193 const auto *CallF
= dyn_cast_or_null
<FunctionDecl
>(Call
.getDecl());
197 CallF
= CallF
->getCanonicalDecl();
198 // If 'errno' must be checked, it should be done as soon as possible, and
199 // before any other call to a system function (something in a system header).
200 // To avoid use of a long list of functions that may change 'errno'
201 // (which may be different with standard library versions) assume that any
202 // function can change it.
203 // A list of special functions can be used that are allowed here without
204 // generation of diagnostic. For now the only such case is 'errno' itself.
205 // Probably 'strerror'?
206 if (CallF
->isExternC() && CallF
->isGlobal() &&
207 C
.getSourceManager().isInSystemHeader(CallF
->getLocation()) &&
209 if (getErrnoState(C
.getState()) == MustBeChecked
) {
210 std::optional
<ento::Loc
> ErrnoLoc
= getErrnoLoc(C
.getState());
211 assert(ErrnoLoc
&& "ErrnoLoc should exist if an errno state is set.");
212 generateErrnoNotCheckedBug(C
, setErrnoStateIrrelevant(C
.getState()),
213 ErrnoLoc
->getAsRegion(), &Call
);
218 ProgramStateRef
ErrnoChecker::checkRegionChanges(
219 ProgramStateRef State
, const InvalidatedSymbols
*Invalidated
,
220 ArrayRef
<const MemRegion
*> ExplicitRegions
,
221 ArrayRef
<const MemRegion
*> Regions
, const LocationContext
*LCtx
,
222 const CallEvent
*Call
) const {
223 std::optional
<ento::Loc
> ErrnoLoc
= getErrnoLoc(State
);
226 const MemRegion
*ErrnoRegion
= ErrnoLoc
->getAsRegion();
228 // If 'errno' is invalidated we can not know if it is checked or written into,
229 // allow read and write without bug reports.
230 if (llvm::is_contained(Regions
, ErrnoRegion
))
231 return clearErrnoState(State
);
233 // Always reset errno state when the system memory space is invalidated.
234 // The ErrnoRegion is not always found in the list in this case.
235 if (llvm::is_contained(Regions
, ErrnoRegion
->getMemorySpace()))
236 return clearErrnoState(State
);
241 void ento::registerErrnoChecker(CheckerManager
&mgr
) {
242 const AnalyzerOptions
&Opts
= mgr
.getAnalyzerOptions();
243 auto *Checker
= mgr
.registerChecker
<ErrnoChecker
>();
244 Checker
->AllowErrnoReadOutsideConditions
= Opts
.getCheckerBooleanOption(
245 Checker
, "AllowErrnoReadOutsideConditionExpressions");
248 bool ento::shouldRegisterErrnoChecker(const CheckerManager
&mgr
) {