1//==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "ClangSACheckers.h"
11#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
12#include "clang/StaticAnalyzer/Core/Checker.h"
13#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
14#include "llvm/ADT/StringSwitch.h"
15
16using namespace clang;
17using namespace ento;
18
19namespace {
20class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols> {
21  mutable std::unique_ptr<BugType> BT;
22
23  void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
24  void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
25  void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;
26  void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
27  void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;
28
29  typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
30                                                 CheckerContext &C) const;
31
32public:
33  bool evalCall(const CallExpr *CE, CheckerContext &C) const;
34  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
35};
36}
37
38REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, const void *)
39
40bool ExprInspectionChecker::evalCall(const CallExpr *CE,
41                                     CheckerContext &C) const {
42  // These checks should have no effect on the surrounding environment
43  // (globals should not be invalidated, etc), hence the use of evalCall.
44  FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
45    .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
46    .Case("clang_analyzer_checkInlined",
47          &ExprInspectionChecker::analyzerCheckInlined)
48    .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
49    .Case("clang_analyzer_warnIfReached",
50          &ExprInspectionChecker::analyzerWarnIfReached)
51    .Case("clang_analyzer_warnOnDeadSymbol",
52          &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
53    .Default(nullptr);
54
55  if (!Handler)
56    return false;
57
58  (this->*Handler)(CE, C);
59  return true;
60}
61
62static const char *getArgumentValueString(const CallExpr *CE,
63                                          CheckerContext &C) {
64  if (CE->getNumArgs() == 0)
65    return "Missing assertion argument";
66
67  ExplodedNode *N = C.getPredecessor();
68  const LocationContext *LC = N->getLocationContext();
69  ProgramStateRef State = N->getState();
70
71  const Expr *Assertion = CE->getArg(0);
72  SVal AssertionVal = State->getSVal(Assertion, LC);
73
74  if (AssertionVal.isUndef())
75    return "UNDEFINED";
76
77  ProgramStateRef StTrue, StFalse;
78  std::tie(StTrue, StFalse) =
79    State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
80
81  if (StTrue) {
82    if (StFalse)
83      return "UNKNOWN";
84    else
85      return "TRUE";
86  } else {
87    if (StFalse)
88      return "FALSE";
89    else
90      llvm_unreachable("Invalid constraint; neither true or false.");
91  }
92}
93
94void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
95                                         CheckerContext &C) const {
96  const LocationContext *LC = C.getPredecessor()->getLocationContext();
97
98  // A specific instantiation of an inlined function may have more constrained
99  // values than can generally be assumed. Skip the check.
100  if (LC->getCurrentStackFrame()->getParent() != nullptr)
101    return;
102
103  if (!BT)
104    BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
105
106  ExplodedNode *N = C.generateNonFatalErrorNode();
107  if (!N)
108    return;
109  C.emitReport(
110      llvm::make_unique<BugReport>(*BT, getArgumentValueString(CE, C), N));
111}
112
113void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
114                                                  CheckerContext &C) const {
115
116  if (!BT)
117    BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
118
119  ExplodedNode *N = C.generateNonFatalErrorNode();
120  if (!N)
121    return;
122  C.emitReport(llvm::make_unique<BugReport>(*BT, "REACHABLE", N));
123}
124
125void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
126                                                 CheckerContext &C) const {
127  const LocationContext *LC = C.getPredecessor()->getLocationContext();
128
129  // An inlined function could conceivably also be analyzed as a top-level
130  // function. We ignore this case and only emit a message (TRUE or FALSE)
131  // when we are analyzing it as an inlined function. This means that
132  // clang_analyzer_checkInlined(true) should always print TRUE, but
133  // clang_analyzer_checkInlined(false) should never actually print anything.
134  if (LC->getCurrentStackFrame()->getParent() == nullptr)
135    return;
136
137  if (!BT)
138    BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
139
140  ExplodedNode *N = C.generateNonFatalErrorNode();
141  if (!N)
142    return;
143  C.emitReport(
144      llvm::make_unique<BugReport>(*BT, getArgumentValueString(CE, C), N));
145}
146
147void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
148                                                     CheckerContext &C) const {
149  if (CE->getNumArgs() == 0)
150    return;
151  SVal Val = C.getSVal(CE->getArg(0));
152  SymbolRef Sym = Val.getAsSymbol();
153  if (!Sym)
154    return;
155
156  ProgramStateRef State = C.getState();
157  State = State->add<MarkedSymbols>(Sym);
158  C.addTransition(State);
159}
160
161void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
162                                             CheckerContext &C) const {
163  ProgramStateRef State = C.getState();
164  const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
165  for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) {
166    SymbolRef Sym = static_cast<SymbolRef>(*I);
167    if (!SymReaper.isDead(Sym))
168      continue;
169
170    if (!BT)
171      BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
172
173    ExplodedNode *N = C.generateNonFatalErrorNode();
174    if (!N)
175      return;
176
177    C.emitReport(llvm::make_unique<BugReport>(*BT, "SYMBOL DEAD", N));
178    C.addTransition(State->remove<MarkedSymbols>(Sym), N);
179  }
180}
181
182void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
183                                          CheckerContext &C) const {
184  LLVM_BUILTIN_TRAP;
185}
186
187void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
188  Mgr.registerChecker<ExprInspectionChecker>();
189}
190
191