BugReporterVisitors.cpp revision 882998923889a2fcce9b49696506c499e22cf38f
1// BugReporterVisitors.cpp - Helpers for reporting bugs -----------*- 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//  This file defines a set of BugReporter "visitors" which can be used to
11//  enhance the diagnostics reported for a bug.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/AST/Expr.h"
16#include "clang/AST/ExprObjC.h"
17#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
18#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
19#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/GRState.h"
21
22using namespace clang;
23using namespace ento;
24
25//===----------------------------------------------------------------------===//
26// Utility functions.
27//===----------------------------------------------------------------------===//
28
29const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) {
30  // Pattern match for a few useful cases (do something smarter later):
31  //   a[0], p->f, *p
32  const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
33
34  if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) {
35    if (U->getOpcode() == UO_Deref)
36      return U->getSubExpr()->IgnoreParenCasts();
37  }
38  else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) {
39    return ME->getBase()->IgnoreParenCasts();
40  }
41  else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(S)) {
42    return AE->getBase();
43  }
44
45  return NULL;
46}
47
48const Stmt *bugreporter::GetDenomExpr(const ExplodedNode *N) {
49  const Stmt *S = N->getLocationAs<PreStmt>()->getStmt();
50  if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(S))
51    return BE->getRHS();
52  return NULL;
53}
54
55const Stmt *bugreporter::GetCalleeExpr(const ExplodedNode *N) {
56  // Callee is checked as a PreVisit to the CallExpr.
57  const Stmt *S = N->getLocationAs<PreStmt>()->getStmt();
58  if (const CallExpr *CE = dyn_cast<CallExpr>(S))
59    return CE->getCallee();
60  return NULL;
61}
62
63const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) {
64  const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
65  if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(S))
66    return RS->getRetValue();
67  return NULL;
68}
69
70//===----------------------------------------------------------------------===//
71// Definitions for bug reporter visitors.
72//===----------------------------------------------------------------------===//
73
74namespace {
75class FindLastStoreBRVisitor : public BugReporterVisitor {
76  const MemRegion *R;
77  SVal V;
78  bool satisfied;
79  const ExplodedNode *StoreSite;
80public:
81  FindLastStoreBRVisitor(SVal v, const MemRegion *r)
82  : R(r), V(v), satisfied(false), StoreSite(0) {}
83
84  virtual void Profile(llvm::FoldingSetNodeID &ID) const {
85    static int tag = 0;
86    ID.AddPointer(&tag);
87    ID.AddPointer(R);
88    ID.Add(V);
89  }
90
91  PathDiagnosticPiece* VisitNode(const ExplodedNode *N,
92                                 const ExplodedNode *PrevN,
93                                 BugReporterContext& BRC) {
94
95    if (satisfied)
96      return NULL;
97
98    if (!StoreSite) {
99      const ExplodedNode *Node = N, *Last = NULL;
100
101      for ( ; Node ; Last = Node, Node = Node->getFirstPred()) {
102
103        if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
104          if (const PostStmt *P = Node->getLocationAs<PostStmt>())
105            if (const DeclStmt *DS = P->getStmtAs<DeclStmt>())
106              if (DS->getSingleDecl() == VR->getDecl()) {
107                Last = Node;
108                break;
109              }
110        }
111
112        if (Node->getState()->getSVal(R) != V)
113          break;
114      }
115
116      if (!Node || !Last) {
117        satisfied = true;
118        return NULL;
119      }
120
121      StoreSite = Last;
122    }
123
124    if (StoreSite != N)
125      return NULL;
126
127    satisfied = true;
128    llvm::SmallString<256> sbuf;
129    llvm::raw_svector_ostream os(sbuf);
130
131    if (const PostStmt *PS = N->getLocationAs<PostStmt>()) {
132      if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) {
133
134        if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
135          os << "Variable '" << VR->getDecl() << "' ";
136        }
137        else
138          return NULL;
139
140        if (isa<loc::ConcreteInt>(V)) {
141          bool b = false;
142          if (R->isBoundable()) {
143            if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) {
144              if (TR->getValueType()->isObjCObjectPointerType()) {
145                os << "initialized to nil";
146                b = true;
147              }
148            }
149          }
150
151          if (!b)
152            os << "initialized to a null pointer value";
153        }
154        else if (isa<nonloc::ConcreteInt>(V)) {
155          os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue();
156        }
157        else if (V.isUndef()) {
158          if (isa<VarRegion>(R)) {
159            const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
160            if (VD->getInit())
161              os << "initialized to a garbage value";
162            else
163              os << "declared without an initial value";
164          }
165        }
166      }
167    }
168
169    if (os.str().empty()) {
170      if (isa<loc::ConcreteInt>(V)) {
171        bool b = false;
172        if (R->isBoundable()) {
173          if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) {
174            if (TR->getValueType()->isObjCObjectPointerType()) {
175              os << "nil object reference stored to ";
176              b = true;
177            }
178          }
179        }
180
181        if (!b)
182          os << "Null pointer value stored to ";
183      }
184      else if (V.isUndef()) {
185        os << "Uninitialized value stored to ";
186      }
187      else if (isa<nonloc::ConcreteInt>(V)) {
188        os << "The value " << cast<nonloc::ConcreteInt>(V).getValue()
189           << " is assigned to ";
190      }
191      else
192        return NULL;
193
194      if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
195        os << '\'' << VR->getDecl() << '\'';
196      }
197      else
198        return NULL;
199    }
200
201    // FIXME: Refactor this into BugReporterContext.
202    const Stmt *S = 0;
203    ProgramPoint P = N->getLocation();
204
205    if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
206      const CFGBlock *BSrc = BE->getSrc();
207      S = BSrc->getTerminatorCondition();
208    }
209    else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) {
210      S = PS->getStmt();
211    }
212
213    if (!S)
214      return NULL;
215
216    // Construct a new PathDiagnosticPiece.
217    PathDiagnosticLocation L(S, BRC.getSourceManager());
218    return new PathDiagnosticEventPiece(L, os.str());
219  }
220};
221
222
223static void registerFindLastStore(BugReporterContext& BRC, const MemRegion *R,
224                                  SVal V) {
225  BRC.addVisitor(new FindLastStoreBRVisitor(V, R));
226}
227
228class TrackConstraintBRVisitor : public BugReporterVisitor {
229  DefinedSVal Constraint;
230  const bool Assumption;
231  bool isSatisfied;
232public:
233  TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption)
234  : Constraint(constraint), Assumption(assumption), isSatisfied(false) {}
235
236  void Profile(llvm::FoldingSetNodeID &ID) const {
237    static int tag = 0;
238    ID.AddPointer(&tag);
239    ID.AddBoolean(Assumption);
240    ID.Add(Constraint);
241  }
242
243  PathDiagnosticPiece* VisitNode(const ExplodedNode *N,
244                                 const ExplodedNode *PrevN,
245                                 BugReporterContext& BRC) {
246    if (isSatisfied)
247      return NULL;
248
249    // Check if in the previous state it was feasible for this constraint
250    // to *not* be true.
251    if (PrevN->getState()->assume(Constraint, !Assumption)) {
252
253      isSatisfied = true;
254
255      // As a sanity check, make sure that the negation of the constraint
256      // was infeasible in the current state.  If it is feasible, we somehow
257      // missed the transition point.
258      if (N->getState()->assume(Constraint, !Assumption))
259        return NULL;
260
261      // We found the transition point for the constraint.  We now need to
262      // pretty-print the constraint. (work-in-progress)
263      std::string sbuf;
264      llvm::raw_string_ostream os(sbuf);
265
266      if (isa<Loc>(Constraint)) {
267        os << "Assuming pointer value is ";
268        os << (Assumption ? "non-null" : "null");
269      }
270
271      if (os.str().empty())
272        return NULL;
273
274      // FIXME: Refactor this into BugReporterContext.
275      const Stmt *S = 0;
276      ProgramPoint P = N->getLocation();
277
278      if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
279        const CFGBlock *BSrc = BE->getSrc();
280        S = BSrc->getTerminatorCondition();
281      }
282      else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) {
283        S = PS->getStmt();
284      }
285
286      if (!S)
287        return NULL;
288
289      // Construct a new PathDiagnosticPiece.
290      PathDiagnosticLocation L(S, BRC.getSourceManager());
291      return new PathDiagnosticEventPiece(L, os.str());
292    }
293
294    return NULL;
295  }
296};
297} // end anonymous namespace
298
299static void registerTrackConstraint(BugReporterContext& BRC,
300                                    DefinedSVal Constraint,
301                                    bool Assumption) {
302  BRC.addVisitor(new TrackConstraintBRVisitor(Constraint, Assumption));
303}
304
305void bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC,
306                                                const void *data,
307                                                const ExplodedNode* N) {
308
309  const Stmt *S = static_cast<const Stmt*>(data);
310
311  if (!S)
312    return;
313
314  GRStateManager &StateMgr = BRC.getStateManager();
315
316  // Walk through nodes until we get one that matches the statement
317  // exactly.
318  while (N) {
319    const ProgramPoint &pp = N->getLocation();
320    if (const PostStmt *ps = dyn_cast<PostStmt>(&pp)) {
321      if (ps->getStmt() == S)
322        break;
323    }
324    N = *N->pred_begin();
325  }
326
327  if (!N)
328    return;
329
330  const GRState *state = N->getState();
331
332  // Walk through lvalue-to-rvalue conversions.
333  if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S)) {
334    if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
335      const VarRegion *R =
336        StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext());
337
338      // What did we load?
339      SVal V = state->getSVal(loc::MemRegionVal(R));
340
341      if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)
342          || V.isUndef()) {
343        ::registerFindLastStore(BRC, R, V);
344      }
345    }
346  }
347
348  SVal V = state->getSValAsScalarOrLoc(S);
349
350  // Uncomment this to find cases where we aren't properly getting the
351  // base value that was dereferenced.
352  // assert(!V.isUnknownOrUndef());
353
354  // Is it a symbolic value?
355  if (loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&V)) {
356    const SubRegion *R = cast<SubRegion>(L->getRegion());
357    while (R && !isa<SymbolicRegion>(R)) {
358      R = dyn_cast<SubRegion>(R->getSuperRegion());
359    }
360
361    if (R) {
362      assert(isa<SymbolicRegion>(R));
363      registerTrackConstraint(BRC, loc::MemRegionVal(R), false);
364    }
365  }
366}
367
368void bugreporter::registerFindLastStore(BugReporterContext& BRC,
369                                        const void *data,
370                                        const ExplodedNode* N) {
371
372  const MemRegion *R = static_cast<const MemRegion*>(data);
373
374  if (!R)
375    return;
376
377  const GRState *state = N->getState();
378  SVal V = state->getSVal(R);
379
380  if (V.isUnknown())
381    return;
382
383  BRC.addVisitor(new FindLastStoreBRVisitor(V, R));
384}
385
386
387namespace {
388class NilReceiverVisitor : public BugReporterVisitor {
389public:
390  NilReceiverVisitor() {}
391
392  void Profile(llvm::FoldingSetNodeID &ID) const {
393    static int x = 0;
394    ID.AddPointer(&x);
395  }
396
397  PathDiagnosticPiece* VisitNode(const ExplodedNode *N,
398                                 const ExplodedNode *PrevN,
399                                 BugReporterContext& BRC) {
400
401    const PostStmt *P = N->getLocationAs<PostStmt>();
402    if (!P)
403      return 0;
404    const ObjCMessageExpr *ME = P->getStmtAs<ObjCMessageExpr>();
405    if (!ME)
406      return 0;
407    const Expr *Receiver = ME->getInstanceReceiver();
408    if (!Receiver)
409      return 0;
410    const GRState *state = N->getState();
411    const SVal &V = state->getSVal(Receiver);
412    const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V);
413    if (!DV)
414      return 0;
415    state = state->assume(*DV, true);
416    if (state)
417      return 0;
418
419    // The receiver was nil, and hence the method was skipped.
420    // Register a BugReporterVisitor to issue a message telling us how
421    // the receiver was null.
422    bugreporter::registerTrackNullOrUndefValue(BRC, Receiver, N);
423    // Issue a message saying that the method was skipped.
424    PathDiagnosticLocation L(Receiver, BRC.getSourceManager());
425    return new PathDiagnosticEventPiece(L, "No method actually called "
426                                           "because the receiver is nil");
427  }
428};
429} // end anonymous namespace
430
431void bugreporter::registerNilReceiverVisitor(BugReporterContext &BRC) {
432  BRC.addVisitor(new NilReceiverVisitor());
433}
434
435// Registers every VarDecl inside a Stmt with a last store vistor.
436void bugreporter::registerVarDeclsLastStore(BugReporterContext &BRC,
437                                                   const void *stmt,
438                                                   const ExplodedNode *N) {
439  const Stmt *S = static_cast<const Stmt *>(stmt);
440
441  std::deque<const Stmt *> WorkList;
442
443  WorkList.push_back(S);
444
445  while (!WorkList.empty()) {
446    const Stmt *Head = WorkList.front();
447    WorkList.pop_front();
448
449    GRStateManager &StateMgr = BRC.getStateManager();
450    const GRState *state = N->getState();
451
452    if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Head)) {
453      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
454        const VarRegion *R =
455        StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext());
456
457        // What did we load?
458        SVal V = state->getSVal(S);
459
460        if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)) {
461          ::registerFindLastStore(BRC, R, V);
462        }
463      }
464    }
465
466    for (Stmt::const_child_iterator I = Head->child_begin();
467        I != Head->child_end(); ++I)
468      WorkList.push_back(*I);
469  }
470}
471