Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//==- ExprInspectionChecker.cpp - Used for regression tests ------*- 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
#include "Taint.h"
10
#include "clang/Analysis/IssueHash.h"
11
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
12
#include "clang/StaticAnalyzer/Checkers/SValExplainer.h"
13
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
14
#include "clang/StaticAnalyzer/Core/Checker.h"
15
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
16
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
17
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
18
#include "llvm/ADT/StringSwitch.h"
19
#include "llvm/Support/ScopedPrinter.h"
20
21
using namespace clang;
22
using namespace ento;
23
24
namespace {
25
class ExprInspectionChecker
26
    : public Checker<eval::Call, check::DeadSymbols, check::EndAnalysis> {
27
  mutable std::unique_ptr<BugType> BT;
28
29
  // These stats are per-analysis, not per-branch, hence they shouldn't
30
  // stay inside the program state.
31
  struct ReachedStat {
32
    ExplodedNode *ExampleNode;
33
    unsigned NumTimesReached;
34
  };
35
  mutable llvm::DenseMap<const CallExpr *, ReachedStat> ReachedStats;
36
37
  void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
38
  void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
39
  void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;
40
  void analyzerNumTimesReached(const CallExpr *CE, CheckerContext &C) const;
41
  void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
42
  void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;
43
  void analyzerDump(const CallExpr *CE, CheckerContext &C) const;
44
  void analyzerExplain(const CallExpr *CE, CheckerContext &C) const;
45
  void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const;
46
  void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const;
47
  void analyzerDumpExtent(const CallExpr *CE, CheckerContext &C) const;
48
  void analyzerDumpElementCount(const CallExpr *CE, CheckerContext &C) const;
49
  void analyzerHashDump(const CallExpr *CE, CheckerContext &C) const;
50
  void analyzerDenote(const CallExpr *CE, CheckerContext &C) const;
51
  void analyzerExpress(const CallExpr *CE, CheckerContext &C) const;
52
  void analyzerIsTainted(const CallExpr *CE, CheckerContext &C) const;
53
54
  typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
55
                                                 CheckerContext &C) const;
56
57
  // Optional parameter `ExprVal` for expression value to be marked interesting.
58
  ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C,
59
                          Optional<SVal> ExprVal = None) const;
60
  ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N,
61
                          Optional<SVal> ExprVal = None) const;
62
63
  const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const;
64
  const MemRegion *getArgRegion(const CallExpr *CE, CheckerContext &C) const;
65
66
public:
67
  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
68
  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
69
  void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
70
                        ExprEngine &Eng) const;
71
};
72
} // namespace
73
74
REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef)
75
REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols, SymbolRef, const StringLiteral *)
76
77
bool ExprInspectionChecker::evalCall(const CallEvent &Call,
78
52.0k
                                     CheckerContext &C) const {
79
52.0k
  const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
80
52.0k
  if (!CE)
81
8.35k
    return false;
82
83
  // These checks should have no effect on the surrounding environment
84
  // (globals should not be invalidated, etc), hence the use of evalCall.
85
43.6k
  FnCheck Handler =
86
43.6k
      llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
87
43.6k
          .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
88
43.6k
          .Case("clang_analyzer_checkInlined",
89
43.6k
                &ExprInspectionChecker::analyzerCheckInlined)
90
43.6k
          .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
91
43.6k
          .Case("clang_analyzer_warnIfReached",
92
43.6k
                &ExprInspectionChecker::analyzerWarnIfReached)
93
43.6k
          .Case("clang_analyzer_warnOnDeadSymbol",
94
43.6k
                &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
95
43.6k
          .StartsWith("clang_analyzer_explain",
96
43.6k
                      &ExprInspectionChecker::analyzerExplain)
97
43.6k
          .Case("clang_analyzer_dumpExtent",
98
43.6k
                &ExprInspectionChecker::analyzerDumpExtent)
99
43.6k
          .Case("clang_analyzer_dumpElementCount",
100
43.6k
                &ExprInspectionChecker::analyzerDumpElementCount)
101
43.6k
          .StartsWith("clang_analyzer_dump",
102
43.6k
                      &ExprInspectionChecker::analyzerDump)
103
43.6k
          .Case("clang_analyzer_getExtent",
104
43.6k
                &ExprInspectionChecker::analyzerGetExtent)
105
43.6k
          .Case("clang_analyzer_printState",
106
43.6k
                &ExprInspectionChecker::analyzerPrintState)
107
43.6k
          .Case("clang_analyzer_numTimesReached",
108
43.6k
                &ExprInspectionChecker::analyzerNumTimesReached)
109
43.6k
          .Case("clang_analyzer_hashDump",
110
43.6k
                &ExprInspectionChecker::analyzerHashDump)
111
43.6k
          .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote)
112
43.6k
          .Case("clang_analyzer_express",
113
43.6k
                &ExprInspectionChecker::analyzerExpress)
114
43.6k
          .StartsWith("clang_analyzer_isTainted",
115
43.6k
                      &ExprInspectionChecker::analyzerIsTainted)
116
43.6k
          .Default(nullptr);
117
118
43.6k
  if (!Handler)
119
29.6k
    return false;
120
121
14.0k
  (this->*Handler)(CE, C);
122
14.0k
  return true;
123
43.6k
}
124
125
static const char *getArgumentValueString(const CallExpr *CE,
126
8.58k
                                          CheckerContext &C) {
127
8.58k
  if (CE->getNumArgs() == 0)
128
0
    return "Missing assertion argument";
129
130
8.58k
  ExplodedNode *N = C.getPredecessor();
131
8.58k
  const LocationContext *LC = N->getLocationContext();
132
8.58k
  ProgramStateRef State = N->getState();
133
134
8.58k
  const Expr *Assertion = CE->getArg(0);
135
8.58k
  SVal AssertionVal = State->getSVal(Assertion, LC);
136
137
8.58k
  if (AssertionVal.isUndef())
138
11
    return "UNDEFINED";
139
140
8.57k
  ProgramStateRef StTrue, StFalse;
141
8.57k
  std::tie(StTrue, StFalse) =
142
8.57k
      State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
143
144
8.57k
  if (StTrue) {
145
6.92k
    if (StFalse)
146
1.56k
      return "UNKNOWN";
147
5.35k
    else
148
5.35k
      return "TRUE";
149
6.92k
  } else {
150
1.65k
    if (StFalse)
151
1.65k
      return "FALSE";
152
0
    else
153
0
      llvm_unreachable("Invalid constraint; neither true or false.");
154
1.65k
  }
155
8.57k
}
156
157
ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
158
                                               CheckerContext &C,
159
10.5k
                                               Optional<SVal> ExprVal) const {
160
10.5k
  ExplodedNode *N = C.generateNonFatalErrorNode();
161
10.5k
  reportBug(Msg, C.getBugReporter(), N, ExprVal);
162
10.5k
  return N;
163
10.5k
}
164
165
ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
166
                                               BugReporter &BR, ExplodedNode *N,
167
10.6k
                                               Optional<SVal> ExprVal) const {
168
10.6k
  if (!N)
169
0
    return nullptr;
170
171
10.6k
  if (!BT)
172
286
    BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
173
174
10.6k
  auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
175
10.6k
  if (ExprVal) {
176
1.10k
    R->markInteresting(*ExprVal);
177
1.10k
  }
178
10.6k
  BR.emitReport(std::move(R));
179
10.6k
  return N;
180
10.6k
}
181
182
const Expr *ExprInspectionChecker::getArgExpr(const CallExpr *CE,
183
1.95k
                                              CheckerContext &C) const {
184
1.95k
  if (CE->getNumArgs() == 0) {
185
1
    reportBug("Missing argument", C);
186
1
    return nullptr;
187
1
  }
188
1.95k
  return CE->getArg(0);
189
1.95k
}
190
191
const MemRegion *ExprInspectionChecker::getArgRegion(const CallExpr *CE,
192
241
                                                     CheckerContext &C) const {
193
241
  const Expr *Arg = getArgExpr(CE, C);
194
241
  if (!Arg)
195
0
    return nullptr;
196
197
241
  const MemRegion *MR = C.getSVal(Arg).getAsRegion();
198
241
  if (!MR) {
199
0
    reportBug("Cannot obtain the region", C);
200
0
    return nullptr;
201
0
  }
202
203
241
  return MR;
204
241
}
205
206
void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
207
8.46k
                                         CheckerContext &C) const {
208
8.46k
  const LocationContext *LC = C.getPredecessor()->getLocationContext();
209
210
  // A specific instantiation of an inlined function may have more constrained
211
  // values than can generally be assumed. Skip the check.
212
8.46k
  if (LC->getStackFrame()->getParent() != nullptr)
213
7
    return;
214
215
8.46k
  reportBug(getArgumentValueString(CE, C), C);
216
8.46k
}
217
218
void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
219
184
                                                  CheckerContext &C) const {
220
184
  reportBug("REACHABLE", C);
221
184
}
222
223
void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE,
224
1.84k
                                                    CheckerContext &C) const {
225
1.84k
  ++ReachedStats[CE].NumTimesReached;
226
1.84k
  if (!ReachedStats[CE].ExampleNode) {
227
    // Later, in checkEndAnalysis, we'd throw a report against it.
228
147
    ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode();
229
147
  }
230
1.84k
}
231
232
void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
233
137
                                                 CheckerContext &C) const {
234
137
  const LocationContext *LC = C.getPredecessor()->getLocationContext();
235
236
  // An inlined function could conceivably also be analyzed as a top-level
237
  // function. We ignore this case and only emit a message (TRUE or FALSE)
238
  // when we are analyzing it as an inlined function. This means that
239
  // clang_analyzer_checkInlined(true) should always print TRUE, but
240
  // clang_analyzer_checkInlined(false) should never actually print anything.
241
137
  if (LC->getStackFrame()->getParent() == nullptr)
242
14
    return;
243
244
123
  reportBug(getArgumentValueString(CE, C), C);
245
123
}
246
247
void ExprInspectionChecker::analyzerExplain(const CallExpr *CE,
248
61
                                            CheckerContext &C) const {
249
61
  const Expr *Arg = getArgExpr(CE, C);
250
61
  if (!Arg)
251
0
    return;
252
253
61
  SVal V = C.getSVal(Arg);
254
61
  SValExplainer Ex(C.getASTContext());
255
61
  reportBug(Ex.Visit(V), C);
256
61
}
257
258
void ExprInspectionChecker::analyzerDump(const CallExpr *CE,
259
309
                                         CheckerContext &C) const {
260
309
  const Expr *Arg = getArgExpr(CE, C);
261
309
  if (!Arg)
262
0
    return;
263
264
309
  SVal V = C.getSVal(Arg);
265
266
309
  llvm::SmallString<32> Str;
267
309
  llvm::raw_svector_ostream OS(Str);
268
309
  V.dumpToStream(OS);
269
309
  reportBug(OS.str(), C);
270
309
}
271
272
void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
273
208
                                              CheckerContext &C) const {
274
208
  const MemRegion *MR = getArgRegion(CE, C);
275
208
  if (!MR)
276
0
    return;
277
278
208
  ProgramStateRef State = C.getState();
279
208
  DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, C.getSValBuilder());
280
281
208
  State = State->BindExpr(CE, C.getLocationContext(), Size);
282
208
  C.addTransition(State);
283
208
}
284
285
void ExprInspectionChecker::analyzerDumpExtent(const CallExpr *CE,
286
17
                                               CheckerContext &C) const {
287
17
  const MemRegion *MR = getArgRegion(CE, C);
288
17
  if (!MR)
289
0
    return;
290
291
17
  DefinedOrUnknownSVal Size =
292
17
      getDynamicExtent(C.getState(), MR, C.getSValBuilder());
293
294
17
  SmallString<64> Msg;
295
17
  llvm::raw_svector_ostream Out(Msg);
296
17
  Out << Size;
297
17
  reportBug(Out.str(), C);
298
17
}
299
300
void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE,
301
16
                                                     CheckerContext &C) const {
302
16
  const MemRegion *MR = getArgRegion(CE, C);
303
16
  if (!MR)
304
0
    return;
305
306
16
  QualType ElementTy;
307
16
  if (const auto *TVR = MR->getAs<TypedValueRegion>()) {
308
14
    ElementTy = TVR->getValueType();
309
14
  } else {
310
2
    ElementTy =
311
2
        MR->castAs<SymbolicRegion>()->getSymbol()->getType()->getPointeeType();
312
2
  }
313
314
16
  assert(!ElementTy->isPointerType());
315
316
0
  DefinedOrUnknownSVal ElementCount =
317
16
      getDynamicElementCount(C.getState(), MR, C.getSValBuilder(), ElementTy);
318
319
16
  SmallString<128> Msg;
320
16
  llvm::raw_svector_ostream Out(Msg);
321
16
  Out << ElementCount;
322
16
  reportBug(Out.str(), C);
323
16
}
324
325
void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE,
326
42
                                               CheckerContext &C) const {
327
42
  C.getState()->dump();
328
42
}
329
330
void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
331
21
                                                     CheckerContext &C) const {
332
21
  const Expr *Arg = getArgExpr(CE, C);
333
21
  if (!Arg)
334
0
    return;
335
336
21
  SVal Val = C.getSVal(Arg);
337
21
  SymbolRef Sym = Val.getAsSymbol();
338
21
  if (!Sym)
339
0
    return;
340
341
21
  ProgramStateRef State = C.getState();
342
21
  State = State->add<MarkedSymbols>(Sym);
343
21
  C.addTransition(State);
344
21
}
345
346
void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
347
178k
                                             CheckerContext &C) const {
348
178k
  ProgramStateRef State = C.getState();
349
178k
  const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
350
178k
  ExplodedNode *N = C.getPredecessor();
351
178k
  for (auto I = Syms.begin(), E = Syms.end(); I != E; 
++I96
) {
352
96
    SymbolRef Sym = *I;
353
96
    if (!SymReaper.isDead(Sym))
354
81
      continue;
355
356
    // The non-fatal error node should be the same for all reports.
357
15
    if (ExplodedNode *BugNode = reportBug("SYMBOL DEAD", C))
358
15
      N = BugNode;
359
15
    State = State->remove<MarkedSymbols>(Sym);
360
15
  }
361
362
178k
  for (auto I : State->get<DenotedSymbols>()) {
363
14.1k
    SymbolRef Sym = I.first;
364
14.1k
    if (!SymReaper.isLive(Sym))
365
617
      State = State->remove<DenotedSymbols>(Sym);
366
14.1k
  }
367
368
178k
  C.addTransition(State, N);
369
178k
}
370
371
void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
372
7.44k
                                             ExprEngine &Eng) const {
373
7.44k
  for (auto Item : ReachedStats) {
374
147
    unsigned NumTimesReached = Item.second.NumTimesReached;
375
147
    ExplodedNode *N = Item.second.ExampleNode;
376
377
147
    reportBug(llvm::to_string(NumTimesReached), BR, N);
378
147
  }
379
7.44k
  ReachedStats.clear();
380
7.44k
}
381
382
void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
383
0
                                          CheckerContext &C) const {
384
0
  LLVM_BUILTIN_TRAP;
385
0
}
386
387
void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE,
388
21
                                             CheckerContext &C) const {
389
21
  const LangOptions &Opts = C.getLangOpts();
390
21
  const SourceManager &SM = C.getSourceManager();
391
21
  FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM);
392
21
  std::string HashContent =
393
21
      getIssueString(FL, getCheckerName().getName(), "Category",
394
21
                     C.getLocationContext()->getDecl(), Opts);
395
396
21
  reportBug(HashContent, C);
397
21
}
398
399
void ExprInspectionChecker::analyzerDenote(const CallExpr *CE,
400
1.37k
                                           CheckerContext &C) const {
401
1.37k
  if (CE->getNumArgs() < 2) {
402
2
    reportBug("clang_analyzer_denote() requires a symbol and a string literal",
403
2
              C);
404
2
    return;
405
2
  }
406
407
1.37k
  SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
408
1.37k
  if (!Sym) {
409
2
    reportBug("Not a symbol", C);
410
2
    return;
411
2
  }
412
413
1.37k
  const auto *E = dyn_cast<StringLiteral>(CE->getArg(1)->IgnoreParenCasts());
414
1.37k
  if (!E) {
415
0
    reportBug("Not a string literal", C);
416
0
    return;
417
0
  }
418
419
1.37k
  ProgramStateRef State = C.getState();
420
421
1.37k
  C.addTransition(C.getState()->set<DenotedSymbols>(Sym, E));
422
1.37k
}
423
424
namespace {
425
class SymbolExpressor
426
    : public SymExprVisitor<SymbolExpressor, Optional<std::string>> {
427
  ProgramStateRef State;
428
429
public:
430
1.32k
  SymbolExpressor(ProgramStateRef State) : State(State) {}
431
432
1.99k
  Optional<std::string> lookup(const SymExpr *S) {
433
1.99k
    if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) {
434
1.18k
      const StringLiteral *SL = *SLPtr;
435
1.18k
      return std::string(SL->getBytes());
436
1.18k
    }
437
812
    return None;
438
1.99k
  }
439
440
1.39k
  Optional<std::string> VisitSymExpr(const SymExpr *S) { return lookup(S); }
441
442
524
  Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) {
443
524
    if (Optional<std::string> Str = lookup(S))
444
1
      return Str;
445
523
    if (Optional<std::string> Str = Visit(S->getLHS()))
446
523
      return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " +
447
523
              std::to_string(S->getRHS().getLimitedValue()) +
448
523
              (S->getRHS().isUnsigned() ? 
"U"64
:
""459
))
449
523
          .str();
450
0
    return None;
451
523
  }
452
453
74
  Optional<std::string> VisitSymSymExpr(const SymSymExpr *S) {
454
74
    if (Optional<std::string> Str = lookup(S))
455
0
      return Str;
456
74
    if (Optional<std::string> Str1 = Visit(S->getLHS()))
457
74
      if (Optional<std::string> Str2 = Visit(S->getRHS()))
458
74
        return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) +
459
74
                " " + *Str2)
460
74
            .str();
461
0
    return None;
462
74
  }
463
464
0
  Optional<std::string> VisitSymbolCast(const SymbolCast *S) {
465
0
    if (Optional<std::string> Str = lookup(S))
466
0
      return Str;
467
0
    if (Optional<std::string> Str = Visit(S->getOperand()))
468
0
      return (Twine("(") + S->getType().getAsString() + ")" + *Str).str();
469
0
    return None;
470
0
  }
471
};
472
} // namespace
473
474
void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
475
1.32k
                                            CheckerContext &C) const {
476
1.32k
  const Expr *Arg = getArgExpr(CE, C);
477
1.32k
  if (!Arg)
478
1
    return;
479
480
1.32k
  SVal ArgVal = C.getSVal(CE->getArg(0));
481
1.32k
  SymbolRef Sym = ArgVal.getAsSymbol();
482
1.32k
  if (!Sym) {
483
2
    reportBug("Not a symbol", C);
484
2
    return;
485
2
  }
486
487
1.32k
  SymbolExpressor V(C.getState());
488
1.32k
  auto Str = V.Visit(Sym);
489
1.32k
  if (!Str) {
490
215
    reportBug("Unable to express", C);
491
215
    return;
492
215
  }
493
494
1.10k
  reportBug(*Str, C, ArgVal);
495
1.10k
}
496
497
void ExprInspectionChecker::analyzerIsTainted(const CallExpr *CE,
498
5
                                              CheckerContext &C) const {
499
5
  if (CE->getNumArgs() != 1) {
500
1
    reportBug("clang_analyzer_isTainted() requires exactly one argument", C);
501
1
    return;
502
1
  }
503
4
  const bool IsTainted =
504
4
      taint::isTainted(C.getState(), CE->getArg(0), C.getLocationContext());
505
4
  reportBug(IsTainted ? 
"YES"2
:
"NO"2
, C);
506
4
}
507
508
349
void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
509
349
  Mgr.registerChecker<ExprInspectionChecker>();
510
349
}
511
512
698
bool ento::shouldRegisterExprInspectionChecker(const CheckerManager &mgr) {
513
698
  return true;
514
698
}