| //== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==// | 
 | // | 
 | //                     The LLVM Compiler Infrastructure | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | // | 
 | // This defines NullDerefChecker, a builtin check in GRExprEngine that performs | 
 | // checks for null pointers at loads and stores. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "GRExprEngineInternalChecks.h" | 
 | #include "clang/Checker/BugReporter/BugType.h" | 
 | #include "clang/Checker/Checkers/DereferenceChecker.h" | 
 | #include "clang/Checker/PathSensitive/Checker.h" | 
 | #include "clang/Checker/PathSensitive/GRExprEngine.h" | 
 |  | 
 | using namespace clang; | 
 |  | 
 | namespace { | 
 | class DereferenceChecker : public Checker { | 
 |   BuiltinBug *BT_null; | 
 |   BuiltinBug *BT_undef; | 
 |   llvm::SmallVector<ExplodedNode*, 2> ImplicitNullDerefNodes; | 
 | public: | 
 |   DereferenceChecker() : BT_null(0), BT_undef(0) {} | 
 |   static void *getTag() { static int tag = 0; return &tag; } | 
 |   void VisitLocation(CheckerContext &C, const Stmt *S, SVal location); | 
 |  | 
 |   std::pair<ExplodedNode * const*, ExplodedNode * const*> | 
 |   getImplicitNodes() const { | 
 |     return std::make_pair(ImplicitNullDerefNodes.data(), | 
 |                           ImplicitNullDerefNodes.data() + | 
 |                           ImplicitNullDerefNodes.size()); | 
 |   } | 
 | }; | 
 | } // end anonymous namespace | 
 |  | 
 | void clang::RegisterDereferenceChecker(GRExprEngine &Eng) { | 
 |   Eng.registerCheck(new DereferenceChecker()); | 
 | } | 
 |  | 
 | std::pair<ExplodedNode * const *, ExplodedNode * const *> | 
 | clang::GetImplicitNullDereferences(GRExprEngine &Eng) { | 
 |   DereferenceChecker *checker = Eng.getChecker<DereferenceChecker>(); | 
 |   if (!checker) | 
 |     return std::make_pair((ExplodedNode * const *) 0, | 
 |                           (ExplodedNode * const *) 0); | 
 |   return checker->getImplicitNodes(); | 
 | } | 
 |  | 
 | void DereferenceChecker::VisitLocation(CheckerContext &C, const Stmt *S, | 
 |                                        SVal l) { | 
 |   // Check for dereference of an undefined value. | 
 |   if (l.isUndef()) { | 
 |     if (ExplodedNode *N = C.GenerateSink()) { | 
 |       if (!BT_undef) | 
 |         BT_undef = new BuiltinBug("Dereference of undefined pointer value"); | 
 |  | 
 |       EnhancedBugReport *report = | 
 |         new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); | 
 |       report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, | 
 |                                 bugreporter::GetDerefExpr(N)); | 
 |       C.EmitReport(report); | 
 |     } | 
 |     return; | 
 |   } | 
 |  | 
 |   DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l); | 
 |  | 
 |   // Check for null dereferences. | 
 |   if (!isa<Loc>(location)) | 
 |     return; | 
 |  | 
 |   const GRState *state = C.getState(); | 
 |   const GRState *notNullState, *nullState; | 
 |   llvm::tie(notNullState, nullState) = state->Assume(location); | 
 |  | 
 |   // The explicit NULL case. | 
 |   if (nullState) { | 
 |     if (!notNullState) { | 
 |       // Generate an error node. | 
 |       ExplodedNode *N = C.GenerateSink(nullState); | 
 |       if (!N) | 
 |         return; | 
 |  | 
 |       // We know that 'location' cannot be non-null.  This is what | 
 |       // we call an "explicit" null dereference. | 
 |       if (!BT_null) | 
 |         BT_null = new BuiltinBug("Dereference of null pointer"); | 
 |  | 
 |       llvm::SmallString<100> buf; | 
 |       llvm::SmallVector<SourceRange, 2> Ranges; | 
 |  | 
 |       switch (S->getStmtClass()) { | 
 |         case Stmt::UnaryOperatorClass: { | 
 |           const UnaryOperator *U = cast<UnaryOperator>(S); | 
 |           const Expr *SU = U->getSubExpr()->IgnoreParens(); | 
 |           if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(SU)) { | 
 |             if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { | 
 |               llvm::raw_svector_ostream os(buf); | 
 |               os << "Dereference of null pointer (loaded from variable '" | 
 |                  << VD->getName() << "')"; | 
 |               Ranges.push_back(DR->getSourceRange()); | 
 |             } | 
 |           } | 
 |           break; | 
 |         } | 
 |         case Stmt::MemberExprClass: { | 
 |           const MemberExpr *M = cast<MemberExpr>(S); | 
 |           if (M->isArrow()) | 
 |             if (DeclRefExpr *DR = | 
 |                 dyn_cast<DeclRefExpr>(M->getBase()->IgnoreParenCasts())) { | 
 |               if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { | 
 |                 llvm::raw_svector_ostream os(buf); | 
 |                 os << "Field access results in a dereference of a null pointer " | 
 |                       "(loaded from variable '" << VD->getName() << "')"; | 
 |                 Ranges.push_back(M->getBase()->getSourceRange()); | 
 |               } | 
 |             } | 
 |           break; | 
 |         } | 
 |         default: | 
 |           break; | 
 |       } | 
 |  | 
 |       EnhancedBugReport *report = | 
 |         new EnhancedBugReport(*BT_null, | 
 |                               buf.empty() ? BT_null->getDescription():buf.str(), | 
 |                               N); | 
 |  | 
 |       report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, | 
 |                                 bugreporter::GetDerefExpr(N)); | 
 |  | 
 |       for (llvm::SmallVectorImpl<SourceRange>::iterator | 
 |             I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) | 
 |         report->addRange(*I); | 
 |  | 
 |       C.EmitReport(report); | 
 |       return; | 
 |     } | 
 |     else { | 
 |       // Otherwise, we have the case where the location could either be | 
 |       // null or not-null.  Record the error node as an "implicit" null | 
 |       // dereference. | 
 |       if (ExplodedNode *N = C.GenerateSink(nullState)) | 
 |         ImplicitNullDerefNodes.push_back(N); | 
 |     } | 
 |   } | 
 |  | 
 |   // From this point forward, we know that the location is not null. | 
 |   C.addTransition(notNullState); | 
 | } |