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