Clang Project

clang_source_code/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
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 "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
10#include "clang/StaticAnalyzer/Checkers/SValExplainer.h"
11#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
12#include "clang/StaticAnalyzer/Core/Checker.h"
13#include "clang/StaticAnalyzer/Core/IssueHash.h"
14#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
15#include "llvm/ADT/StringSwitch.h"
16#include "llvm/Support/ScopedPrinter.h"
17
18using namespace clang;
19using namespace ento;
20
21namespace {
22class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols,
23                                             check::EndAnalysis> {
24  mutable std::unique_ptr<BugTypeBT;
25
26  // These stats are per-analysis, not per-branch, hence they shouldn't
27  // stay inside the program state.
28  struct ReachedStat {
29    ExplodedNode *ExampleNode;
30    unsigned NumTimesReached;
31  };
32  mutable llvm::DenseMap<const CallExpr *, ReachedStat> ReachedStats;
33
34  void analyzerEval(const CallExpr *CECheckerContext &Cconst;
35  void analyzerCheckInlined(const CallExpr *CECheckerContext &Cconst;
36  void analyzerWarnIfReached(const CallExpr *CECheckerContext &Cconst;
37  void analyzerNumTimesReached(const CallExpr *CECheckerContext &Cconst;
38  void analyzerCrash(const CallExpr *CECheckerContext &Cconst;
39  void analyzerWarnOnDeadSymbol(const CallExpr *CECheckerContext &Cconst;
40  void analyzerDump(const CallExpr *CECheckerContext &Cconst;
41  void analyzerExplain(const CallExpr *CECheckerContext &Cconst;
42  void analyzerPrintState(const CallExpr *CECheckerContext &Cconst;
43  void analyzerGetExtent(const CallExpr *CECheckerContext &Cconst;
44  void analyzerHashDump(const CallExpr *CECheckerContext &Cconst;
45  void analyzerDenote(const CallExpr *CECheckerContext &Cconst;
46  void analyzerExpress(const CallExpr *CECheckerContext &Cconst;
47
48  typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
49                                                 CheckerContext &Cconst;
50
51  ExplodedNode *reportBug(llvm::StringRef MsgCheckerContext &Cconst;
52  ExplodedNode *reportBug(llvm::StringRef MsgBugReporter &BR,
53                          ExplodedNode *Nconst;
54
55public:
56  bool evalCall(const CallExpr *CECheckerContext &Cconst;
57  void checkDeadSymbols(SymbolReaper &SymReaperCheckerContext &Cconst;
58  void checkEndAnalysis(ExplodedGraph &GBugReporter &BR,
59                        ExprEngine &Engconst;
60};
61}
62
63REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef)
64REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols, SymbolRef, const StringLiteral *)
65
66bool ExprInspectionChecker::evalCall(const CallExpr *CE,
67                                     CheckerContext &Cconst {
68  // These checks should have no effect on the surrounding environment
69  // (globals should not be invalidated, etc), hence the use of evalCall.
70  FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
71    .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
72    .Case("clang_analyzer_checkInlined",
73          &ExprInspectionChecker::analyzerCheckInlined)
74    .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
75    .Case("clang_analyzer_warnIfReached",
76          &ExprInspectionChecker::analyzerWarnIfReached)
77    .Case("clang_analyzer_warnOnDeadSymbol",
78          &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
79    .StartsWith("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain)
80    .StartsWith("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump)
81    .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent)
82    .Case("clang_analyzer_printState",
83          &ExprInspectionChecker::analyzerPrintState)
84    .Case("clang_analyzer_numTimesReached",
85          &ExprInspectionChecker::analyzerNumTimesReached)
86    .Case("clang_analyzer_hashDump", &ExprInspectionChecker::analyzerHashDump)
87    .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote)
88    .Case("clang_analyzer_express", &ExprInspectionChecker::analyzerExpress)
89    .Default(nullptr);
90
91  if (!Handler)
92    return false;
93
94  (this->*Handler)(CEC);
95  return true;
96}
97
98static const char *getArgumentValueString(const CallExpr *CE,
99                                          CheckerContext &C) {
100  if (CE->getNumArgs() == 0)
101    return "Missing assertion argument";
102
103  ExplodedNode *N = C.getPredecessor();
104  const LocationContext *LC = N->getLocationContext();
105  ProgramStateRef State = N->getState();
106
107  const Expr *Assertion = CE->getArg(0);
108  SVal AssertionVal = State->getSVal(Assertion, LC);
109
110  if (AssertionVal.isUndef())
111    return "UNDEFINED";
112
113  ProgramStateRef StTrueStFalse;
114  std::tie(StTrue, StFalse) =
115    State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
116
117  if (StTrue) {
118    if (StFalse)
119      return "UNKNOWN";
120    else
121      return "TRUE";
122  } else {
123    if (StFalse)
124      return "FALSE";
125    else
126      llvm_unreachable("Invalid constraint; neither true or false.");
127  }
128}
129
130ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
131                                               CheckerContext &Cconst {
132  ExplodedNode *N = C.generateNonFatalErrorNode();
133  reportBug(Msg, C.getBugReporter(), N);
134  return N;
135}
136
137ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
138                                               BugReporter &BR,
139                                               ExplodedNode *Nconst {
140  if (!N)
141    return nullptr;
142
143  if (!BT)
144    BT.reset(new BugType(this"Checking analyzer assumptions""debug"));
145
146  BR.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N));
147  return N;
148}
149
150void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
151                                         CheckerContext &Cconst {
152  const LocationContext *LC = C.getPredecessor()->getLocationContext();
153
154  // A specific instantiation of an inlined function may have more constrained
155  // values than can generally be assumed. Skip the check.
156  if (LC->getStackFrame()->getParent() != nullptr)
157    return;
158
159  reportBug(getArgumentValueString(CEC), C);
160}
161
162void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
163                                                  CheckerContext &Cconst {
164  reportBug("REACHABLE"C);
165}
166
167void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE,
168                                                    CheckerContext &Cconst {
169  ++ReachedStats[CE].NumTimesReached;
170  if (!ReachedStats[CE].ExampleNode) {
171    // Later, in checkEndAnalysis, we'd throw a report against it.
172    ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode();
173  }
174}
175
176void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
177                                                 CheckerContext &Cconst {
178  const LocationContext *LC = C.getPredecessor()->getLocationContext();
179
180  // An inlined function could conceivably also be analyzed as a top-level
181  // function. We ignore this case and only emit a message (TRUE or FALSE)
182  // when we are analyzing it as an inlined function. This means that
183  // clang_analyzer_checkInlined(true) should always print TRUE, but
184  // clang_analyzer_checkInlined(false) should never actually print anything.
185  if (LC->getStackFrame()->getParent() == nullptr)
186    return;
187
188  reportBug(getArgumentValueString(CEC), C);
189}
190
191void ExprInspectionChecker::analyzerExplain(const CallExpr *CE,
192                                            CheckerContext &Cconst {
193  if (CE->getNumArgs() == 0) {
194    reportBug("Missing argument for explaining"C);
195    return;
196  }
197
198  SVal V = C.getSVal(CE->getArg(0));
199  SValExplainer Ex(C.getASTContext());
200  reportBug(Ex.Visit(V), C);
201}
202
203void ExprInspectionChecker::analyzerDump(const CallExpr *CE,
204                                         CheckerContext &Cconst {
205  if (CE->getNumArgs() == 0) {
206    reportBug("Missing argument for dumping"C);
207    return;
208  }
209
210  SVal V = C.getSVal(CE->getArg(0));
211
212  llvm::SmallString<32Str;
213  llvm::raw_svector_ostream OS(Str);
214  V.dumpToStream(OS);
215  reportBug(OS.str(), C);
216}
217
218void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
219                                              CheckerContext &Cconst {
220  if (CE->getNumArgs() == 0) {
221    reportBug("Missing region for obtaining extent"C);
222    return;
223  }
224
225  auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion());
226  if (!MR) {
227    reportBug("Obtaining extent of a non-region"C);
228    return;
229  }
230
231  ProgramStateRef State = C.getState();
232  State = State->BindExpr(CE, C.getLocationContext(),
233                          MR->getExtent(C.getSValBuilder()));
234  C.addTransition(State);
235}
236
237void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE,
238                                               CheckerContext &Cconst {
239  C.getState()->dump();
240}
241
242void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
243                                                     CheckerContext &Cconst {
244  if (CE->getNumArgs() == 0)
245    return;
246  SVal Val = C.getSVal(CE->getArg(0));
247  SymbolRef Sym = Val.getAsSymbol();
248  if (!Sym)
249    return;
250
251  ProgramStateRef State = C.getState();
252  State = State->add<MarkedSymbols>(Sym);
253  C.addTransition(State);
254}
255
256void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
257                                             CheckerContext &Cconst {
258  ProgramStateRef State = C.getState();
259  const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
260  ExplodedNode *N = C.getPredecessor();
261  for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) {
262    SymbolRef Sym = *I;
263    if (!SymReaper.isDead(Sym))
264      continue;
265
266    // The non-fatal error node should be the same for all reports.
267    if (ExplodedNode *BugNode = reportBug("SYMBOL DEAD", C))
268      N = BugNode;
269    State = State->remove<MarkedSymbols>(Sym);
270  }
271
272  for (auto I : State->get<DenotedSymbols>()) {
273    SymbolRef Sym = I.first;
274    if (!SymReaper.isLive(Sym))
275      State = State->remove<DenotedSymbols>(Sym);
276  }
277
278  C.addTransition(State, N);
279}
280
281void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &GBugReporter &BR,
282                                             ExprEngine &Engconst {
283  for (auto Item: ReachedStats) {
284    unsigned NumTimesReached = Item.second.NumTimesReached;
285    ExplodedNode *N = Item.second.ExampleNode;
286
287    reportBug(llvm::to_string(NumTimesReached), BR, N);
288  }
289  ReachedStats.clear();
290}
291
292void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
293                                          CheckerContext &Cconst {
294  LLVM_BUILTIN_TRAP;
295}
296
297void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE,
298                                             CheckerContext &Cconst {
299  const LangOptions &Opts = C.getLangOpts();
300  const SourceManager &SM = C.getSourceManager();
301  FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM);
302  std::string HashContent =
303      GetIssueString(SMFL, getCheckName().getName(), "Category",
304                     C.getLocationContext()->getDecl(), Opts);
305
306  reportBug(HashContentC);
307}
308
309void ExprInspectionChecker::analyzerDenote(const CallExpr *CE,
310                                           CheckerContext &Cconst {
311  if (CE->getNumArgs() < 2) {
312    reportBug("clang_analyzer_denote() requires a symbol and a string literal",
313              C);
314    return;
315  }
316
317  SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
318  if (!Sym) {
319    reportBug("Not a symbol"C);
320    return;
321  }
322
323  const auto *E = dyn_cast<StringLiteral>(CE->getArg(1)->IgnoreParenCasts());
324  if (!E) {
325    reportBug("Not a string literal"C);
326    return;
327  }
328
329  ProgramStateRef State = C.getState();
330
331  C.addTransition(C.getState()->set<DenotedSymbols>(Sym, E));
332}
333
334namespace {
335class SymbolExpressor
336    : public SymExprVisitor<SymbolExpressorOptional<std::string>> {
337  ProgramStateRef State;
338
339public:
340  SymbolExpressor(ProgramStateRef State) : State(State) {}
341
342  Optional<std::stringlookup(const SymExpr *S) {
343    if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) {
344      const StringLiteral *SL = *SLPtr;
345      return std::string(SL->getBytes());
346    }
347    return None;
348  }
349
350  Optional<std::stringVisitSymExpr(const SymExpr *S) {
351    return lookup(S);
352  }
353
354  Optional<std::stringVisitSymIntExpr(const SymIntExpr *S) {
355    if (Optional<std::string> Str = lookup(S))
356      return Str;
357    if (Optional<std::string> Str = Visit(S->getLHS()))
358      return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " +
359              std::to_string(S->getRHS().getLimitedValue()) +
360              (S->getRHS().isUnsigned() ? "U" : ""))
361          .str();
362    return None;
363  }
364
365  Optional<std::stringVisitSymSymExpr(const SymSymExpr *S) {
366    if (Optional<std::string> Str = lookup(S))
367      return Str;
368    if (Optional<std::string> Str1 = Visit(S->getLHS()))
369      if (Optional<std::string> Str2 = Visit(S->getRHS()))
370        return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) +
371                " " + *Str2).str();
372    return None;
373  }
374
375  Optional<std::stringVisitSymbolCast(const SymbolCast *S) {
376    if (Optional<std::string> Str = lookup(S))
377      return Str;
378    if (Optional<std::string> Str = Visit(S->getOperand()))
379      return (Twine("(") + S->getType().getAsString() + ")" + *Str).str();
380    return None;
381  }
382};
383// namespace
384
385void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
386                                            CheckerContext &Cconst {
387  if (CE->getNumArgs() == 0) {
388    reportBug("clang_analyzer_express() requires a symbol"C);
389    return;
390  }
391
392  SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
393  if (!Sym) {
394    reportBug("Not a symbol"C);
395    return;
396  }
397
398  SymbolExpressor V(C.getState());
399  auto Str = V.Visit(Sym);
400  if (!Str) {
401    reportBug("Unable to express"C);
402    return;
403  }
404
405  reportBug(*Str, C);
406}
407
408void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
409  Mgr.registerChecker<ExprInspectionChecker>();
410}
411
412bool ento::shouldRegisterExprInspectionChecker(const LangOptions &LO) {
413  return true;
414}
415