clang  16.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 
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;
39  checkRegionChanges(ProgramStateRef State,
40  const InvalidatedSymbols *Invalidated,
41  ArrayRef<const MemRegion *> ExplicitRegions,
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 
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();
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.
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.
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 {
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))
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()))
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 }
clang::DynTypedNodeList
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
Definition: ParentMapContext.h:92
clang::LocationContext
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
Definition: AnalysisDeclContext.h:215
CallDescription.h
SVals.h
clang::ento::errno_modeling::Irrelevant
@ Irrelevant
We do not know anything about 'errno'.
Definition: ErrnoModeling.h:28
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:55
clang::ento::errno_modeling::MustBeChecked
@ MustBeChecked
Value of 'errno' should be checked to find out if a previous function call has failed.
Definition: ErrnoModeling.h:35
setErrnoStateIrrelevant
static ProgramStateRef setErrnoStateIrrelevant(ProgramStateRef State)
Definition: ErrnoChecker.cpp:64
llvm::Optional
Definition: LLVM.h:40
clang::index::SymbolRole::Call
@ Call
ErrnoModeling.h
ParentMapContext.h
isInCondition
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,...
Definition: ErrnoChecker.cpp:70
BuiltinCheckerRegistration.h
CheckerManager.h
clang::ento::errno_modeling::setErrnoState
ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState)
Set the errno check state, do not modify the errno value.
Definition: ErrnoModeling.cpp:249
llvm::SmallString
Definition: LLVM.h:37
clang::ento::errno_modeling::MustNotBeChecked
@ MustNotBeChecked
Value of 'errno' is not allowed to be read, it can contain an unspecified value.
Definition: ErrnoModeling.h:41
clang::ento::Loc
Definition: SVals.h:282
clang::DynTypedNodeList::empty
bool empty() const
Definition: ParentMapContext.h:117
clang::ento::errno_modeling::ErrnoCheckState
ErrnoCheckState
Describe how reads and writes of errno are handled by the checker.
Definition: ErrnoModeling.h:25
clang::ParentMapContext
Definition: ParentMapContext.h:23
clang::ento::errno_modeling::isErrno
bool isErrno(const Decl *D)
Determine if a Decl node related to 'errno'.
Definition: ErrnoModeling.cpp:257
llvm::ArrayRef
Definition: LLVM.h:34
clang::AnalyzerOptions
Stores options for the analyzer from the command line.
Definition: AnalyzerOptions.h:150
State
LineState State
Definition: UnwrappedLineFormatter.cpp:1139
clang::ParentMapContext::getParents
DynTypedNodeList getParents(const NodeT &Node)
Returns the parents of the given node (within the traversal scope).
Definition: ParentMapContext.h:126
CheckerContext.h
ProgramState.h
clang::AnalyzerOptions::getCheckerBooleanOption
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
Definition: AnalyzerOptions.cpp:162
Checker.h
clang
Definition: CalledOnceCheck.h:17
clang::Stmt
Stmt - This represents one statement.
Definition: Stmt.h:70
clang::ento::errno_modeling::getErrnoLoc
Optional< Loc > getErrnoLoc(ProgramStateRef State)
Returns the location that points to the MemoryRegion where the 'errno' value is stored.
Definition: ErrnoModeling.cpp:242
clang::ento::InvalidatedSymbols
llvm::DenseSet< SymbolRef > InvalidatedSymbols
Definition: Store.h:51
clang::ento::errno_modeling::getErrnoState
ErrnoCheckState getErrnoState(ProgramStateRef State)
Returns the errno check state, Errno_Irrelevant if 'errno' was not found (this is not the only case f...
Definition: ErrnoModeling.cpp:253
clang::ento::ObjKind::OS
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...