clang  19.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 
27 using namespace clang;
28 using namespace ento;
29 using namespace errno_modeling;
30 
31 namespace {
32 
33 class ErrnoChecker
34  : public Checker<check::Location, check::PreCall, check::RegionChanges> {
35 public:
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  void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const;
46 
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;
51 
52 private:
53  void generateErrnoNotCheckedBug(CheckerContext &C, ProgramStateRef State,
54  const MemRegion *ErrnoRegion,
55  const CallEvent *CallMayChangeErrno) const;
56 
57  BugType BT_InvalidErrnoRead{this, "Value of 'errno' could be undefined",
58  "Error handling"};
59  BugType BT_ErrnoNotChecked{this, "Value of 'errno' was not checked",
60  "Error handling"};
61 };
62 
63 } // namespace
64 
67 }
68 
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);
76  if (Parents.empty())
77  break;
78  const auto *ParentS = Parents[0].get<Stmt>();
79  if (!ParentS || isa<CallExpr>(ParentS))
80  break;
81  switch (ParentS->getStmtClass()) {
82  case Expr::IfStmtClass:
83  CondFound = (S == cast<IfStmt>(ParentS)->getCond());
84  break;
85  case Expr::ForStmtClass:
86  CondFound = (S == cast<ForStmt>(ParentS)->getCond());
87  break;
88  case Expr::DoStmtClass:
89  CondFound = (S == cast<DoStmt>(ParentS)->getCond());
90  break;
91  case Expr::WhileStmtClass:
92  CondFound = (S == cast<WhileStmt>(ParentS)->getCond());
93  break;
94  case Expr::SwitchStmtClass:
95  CondFound = (S == cast<SwitchStmt>(ParentS)->getCond());
96  break;
97  case Expr::ConditionalOperatorClass:
98  CondFound = (S == cast<ConditionalOperator>(ParentS)->getCond());
99  break;
100  case Expr::BinaryConditionalOperatorClass:
101  CondFound = (S == cast<BinaryConditionalOperator>(ParentS)->getCommon());
102  break;
103  default:
104  break;
105  }
106  S = ParentS;
107  }
108  return CondFound;
109 }
110 
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 "
119  "function '";
120  const auto *CallD =
121  dyn_cast_or_null<FunctionDecl>(CallMayChangeErrno->getDecl());
122  assert(CallD && CallD->getIdentifier());
123  OS << CallD->getIdentifier()->getName() << "'";
124  } else {
125  OS << "Value of 'errno' was not checked and is overwritten here";
126  }
127  auto BR = std::make_unique<PathSensitiveBugReport>(BT_ErrnoNotChecked,
128  OS.str(), N);
129  BR->markInteresting(ErrnoRegion);
130  C.emitReport(std::move(BR));
131  }
132 }
133 
134 void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
135  CheckerContext &C) const {
136  std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
137  if (!ErrnoLoc)
138  return;
139 
140  auto L = Loc.getAs<ento::Loc>();
141  if (!L || *ErrnoLoc != *L)
142  return;
143 
144  ProgramStateRef State = C.getState();
146 
147  if (IsLoad) {
148  switch (EState) {
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>(
154  BT_InvalidErrnoRead,
155  "An undefined value may be read from 'errno'", N);
156  BR->markInteresting(ErrnoLoc->getAsRegion());
157  C.emitReport(std::move(BR));
158  }
159  }
160  break;
161  case MustBeChecked:
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.
166  C.addTransition(State);
167  break;
168  default:
169  break;
170  }
171  } else {
172  switch (EState) {
173  case MustBeChecked:
174  // 'errno' is overwritten without a read before but it should have been
175  // checked.
176  generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(State),
177  ErrnoLoc->getAsRegion(), nullptr);
178  break;
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.
183  C.addTransition(State);
184  break;
185  default:
186  break;
187  }
188  }
189 }
190 
191 void ErrnoChecker::checkPreCall(const CallEvent &Call,
192  CheckerContext &C) const {
193  const auto *CallF = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
194  if (!CallF)
195  return;
196 
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()) &&
208  !isErrnoLocationCall(Call)) {
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);
214  }
215  }
216 }
217 
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);
224  if (!ErrnoLoc)
225  return State;
226  const MemRegion *ErrnoRegion = ErrnoLoc->getAsRegion();
227 
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);
232 
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);
237 
238  return State;
239 }
240 
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");
246 }
247 
248 bool ento::shouldRegisterErrnoChecker(const CheckerManager &mgr) {
249  return true;
250 }
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)
LineState 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:96
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemSpaceRegion * getMemorySpace() const
Definition: MemRegion.cpp:1317
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:55
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:86
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...
bool Call(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize)
Definition: Interp.h:2179
The JSON file list parser is used to communicate input to InstallAPI.