Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp
Line
Count
Source (jump to first uncovered line)
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
#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;
39
  ProgramStateRef
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;
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
65
30
static ProgramStateRef setErrnoStateIrrelevant(ProgramStateRef State) {
66
30
  return setErrnoState(State, Irrelevant);
67
30
}
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
81
static bool isInCondition(const Stmt *S, CheckerContext &C) {
72
81
  ParentMapContext &ParentCtx = C.getASTContext().getParentMapContext();
73
81
  bool CondFound = false;
74
175
  while (S && !CondFound) {
75
102
    const DynTypedNodeList Parents = ParentCtx.getParents(*S);
76
102
    if (Parents.empty())
77
0
      break;
78
102
    const auto *ParentS = Parents[0].get<Stmt>();
79
102
    if (!ParentS || 
isa<CallExpr>(ParentS)96
)
80
8
      break;
81
94
    switch (ParentS->getStmtClass()) {
82
57
    case Expr::IfStmtClass:
83
57
      CondFound = (S == cast<IfStmt>(ParentS)->getCond());
84
57
      break;
85
2
    case Expr::ForStmtClass:
86
2
      CondFound = (S == cast<ForStmt>(ParentS)->getCond());
87
2
      break;
88
2
    case Expr::DoStmtClass:
89
2
      CondFound = (S == cast<DoStmt>(ParentS)->getCond());
90
2
      break;
91
2
    case Expr::WhileStmtClass:
92
2
      CondFound = (S == cast<WhileStmt>(ParentS)->getCond());
93
2
      break;
94
2
    case Expr::SwitchStmtClass:
95
2
      CondFound = (S == cast<SwitchStmt>(ParentS)->getCond());
96
2
      break;
97
6
    case Expr::ConditionalOperatorClass:
98
6
      CondFound = (S == cast<ConditionalOperator>(ParentS)->getCond());
99
6
      break;
100
2
    case Expr::BinaryConditionalOperatorClass:
101
2
      CondFound = (S == cast<BinaryConditionalOperator>(ParentS)->getCommon());
102
2
      break;
103
21
    default:
104
21
      break;
105
94
    }
106
94
    S = ParentS;
107
94
  }
108
81
  return CondFound;
109
81
}
110
111
void ErrnoChecker::generateErrnoNotCheckedBug(
112
    CheckerContext &C, ProgramStateRef State, const MemRegion *ErrnoRegion,
113
11
    const CallEvent *CallMayChangeErrno) const {
114
11
  if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
115
11
    SmallString<100> StrBuf;
116
11
    llvm::raw_svector_ostream OS(StrBuf);
117
11
    if (CallMayChangeErrno) {
118
5
      OS << "Value of 'errno' was not checked and may be overwritten by "
119
5
            "function '";
120
5
      const auto *CallD =
121
5
          dyn_cast_or_null<FunctionDecl>(CallMayChangeErrno->getDecl());
122
5
      assert(CallD && CallD->getIdentifier());
123
5
      OS << CallD->getIdentifier()->getName() << "'";
124
6
    } else {
125
6
      OS << "Value of 'errno' was not checked and is overwritten here";
126
6
    }
127
11
    auto BR = std::make_unique<PathSensitiveBugReport>(BT_ErrnoNotChecked,
128
11
                                                       OS.str(), N);
129
11
    BR->markInteresting(ErrnoRegion);
130
11
    C.emitReport(std::move(BR));
131
11
  }
132
11
}
133
134
void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
135
972
                                 CheckerContext &C) const {
136
972
  std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
137
972
  if (!ErrnoLoc)
138
36
    return;
139
140
936
  auto L = Loc.getAs<ento::Loc>();
141
936
  if (!L || *ErrnoLoc != *L)
142
629
    return;
143
144
307
  ProgramStateRef State = C.getState();
145
307
  ErrnoCheckState EState = getErrnoState(State);
146
147
307
  if (IsLoad) {
148
267
    switch (EState) {
149
91
    case MustNotBeChecked:
150
      // Read of 'errno' when it may have undefined value.
151
91
      if (!AllowErrnoReadOutsideConditions || 
isInCondition(S, C)81
) {
152
83
        if (ExplodedNode *N = C.generateErrorNode()) {
153
83
          auto BR = std::make_unique<PathSensitiveBugReport>(
154
83
              BT_InvalidErrnoRead,
155
83
              "An undefined value may be read from 'errno'", N);
156
83
          BR->markInteresting(ErrnoLoc->getAsRegion());
157
83
          C.emitReport(std::move(BR));
158
83
        }
159
83
      }
160
91
      break;
161
9
    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.
165
9
      State = setErrnoStateIrrelevant(State);
166
9
      C.addTransition(State);
167
9
      break;
168
167
    default:
169
167
      break;
170
267
    }
171
267
  } else {
172
40
    switch (EState) {
173
6
    case MustBeChecked:
174
      // 'errno' is overwritten without a read before but it should have been
175
      // checked.
176
6
      generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(State),
177
6
                                 ErrnoLoc->getAsRegion(), nullptr);
178
6
      break;
179
10
    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
10
      State = setErrnoStateIrrelevant(State);
183
10
      C.addTransition(State);
184
10
      break;
185
24
    default:
186
24
      break;
187
40
    }
188
40
  }
189
307
}
190
191
void ErrnoChecker::checkPreCall(const CallEvent &Call,
192
686
                                CheckerContext &C) const {
193
686
  const auto *CallF = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
194
686
  if (!CallF)
195
0
    return;
196
197
686
  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
686
  if (CallF->isExternC() && 
CallF->isGlobal()680
&&
207
686
      
C.getSourceManager().isInSystemHeader(CallF->getLocation())680
&&
208
686
      
!isErrno(CallF)331
) {
209
195
    if (getErrnoState(C.getState()) == MustBeChecked) {
210
5
      std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
211
5
      assert(ErrnoLoc && "ErrnoLoc should exist if an errno state is set.");
212
5
      generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(C.getState()),
213
5
                                 ErrnoLoc->getAsRegion(), &Call);
214
5
    }
215
195
  }
216
686
}
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
1.18k
    const CallEvent *Call) const {
223
1.18k
  std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(State);
224
1.18k
  if (!ErrnoLoc)
225
48
    return State;
226
1.13k
  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
1.13k
  if (llvm::is_contained(Regions, ErrnoRegion))
231
535
    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
599
  if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace()))
236
65
    return clearErrnoState(State);
237
238
534
  return State;
239
599
}
240
241
15
void ento::registerErrnoChecker(CheckerManager &mgr) {
242
15
  const AnalyzerOptions &Opts = mgr.getAnalyzerOptions();
243
15
  auto *Checker = mgr.registerChecker<ErrnoChecker>();
244
15
  Checker->AllowErrnoReadOutsideConditions = Opts.getCheckerBooleanOption(
245
15
      Checker, "AllowErrnoReadOutsideConditionExpressions");
246
15
}
247
248
30
bool ento::shouldRegisterErrnoChecker(const CheckerManager &mgr) {
249
30
  return true;
250
30
}