Home | History | Annotate | Download | only in Checkers
      1 //== NullDerefChecker.cpp - Null dereference checker ------------*- 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 defines NullDerefChecker, a builtin check in ExprEngine that performs
     11 // checks for null pointers at loads and stores.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "ClangSACheckers.h"
     16 #include "clang/AST/ExprObjC.h"
     17 #include "clang/StaticAnalyzer/Core/Checker.h"
     18 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
     19 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
     20 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
     21 #include "llvm/ADT/SmallString.h"
     22 
     23 using namespace clang;
     24 using namespace ento;
     25 
     26 namespace {
     27 class DereferenceChecker
     28     : public Checker< check::Location,
     29                         EventDispatcher<ImplicitNullDerefEvent> > {
     30   mutable OwningPtr<BuiltinBug> BT_null;
     31   mutable OwningPtr<BuiltinBug> BT_undef;
     32 
     33 public:
     34   void checkLocation(SVal location, bool isLoad, const Stmt* S,
     35                      CheckerContext &C) const;
     36 
     37   static const MemRegion *AddDerefSource(raw_ostream &os,
     38                              SmallVectorImpl<SourceRange> &Ranges,
     39                              const Expr *Ex, const ProgramState *state,
     40                              const LocationContext *LCtx,
     41                              bool loadedFrom = false);
     42 };
     43 } // end anonymous namespace
     44 
     45 const MemRegion *
     46 DereferenceChecker::AddDerefSource(raw_ostream &os,
     47                                    SmallVectorImpl<SourceRange> &Ranges,
     48                                    const Expr *Ex,
     49                                    const ProgramState *state,
     50                                    const LocationContext *LCtx,
     51                                    bool loadedFrom) {
     52   Ex = Ex->IgnoreParenLValueCasts();
     53   const MemRegion *sourceR = 0;
     54   switch (Ex->getStmtClass()) {
     55     default:
     56       break;
     57     case Stmt::DeclRefExprClass: {
     58       const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
     59       if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
     60         os << " (" << (loadedFrom ? "loaded from" : "from")
     61            << " variable '" <<  VD->getName() << "')";
     62         Ranges.push_back(DR->getSourceRange());
     63         sourceR = state->getLValue(VD, LCtx).getAsRegion();
     64       }
     65       break;
     66     }
     67     case Stmt::MemberExprClass: {
     68       const MemberExpr *ME = cast<MemberExpr>(Ex);
     69       os << " (" << (loadedFrom ? "loaded from" : "via")
     70          << " field '" << ME->getMemberNameInfo() << "')";
     71       SourceLocation L = ME->getMemberLoc();
     72       Ranges.push_back(SourceRange(L, L));
     73       break;
     74     }
     75   }
     76   return sourceR;
     77 }
     78 
     79 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
     80                                        CheckerContext &C) const {
     81   // Check for dereference of an undefined value.
     82   if (l.isUndef()) {
     83     if (ExplodedNode *N = C.generateSink()) {
     84       if (!BT_undef)
     85         BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value"));
     86 
     87       BugReport *report =
     88         new BugReport(*BT_undef, BT_undef->getDescription(), N);
     89       report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
     90                                         bugreporter::GetDerefExpr(N), report));
     91       C.EmitReport(report);
     92     }
     93     return;
     94   }
     95 
     96   DefinedOrUnknownSVal location = cast<DefinedOrUnknownSVal>(l);
     97 
     98   // Check for null dereferences.
     99   if (!isa<Loc>(location))
    100     return;
    101 
    102   ProgramStateRef state = C.getState();
    103   const LocationContext *LCtx = C.getLocationContext();
    104   ProgramStateRef notNullState, nullState;
    105   llvm::tie(notNullState, nullState) = state->assume(location);
    106 
    107   // The explicit NULL case.
    108   if (nullState) {
    109     if (!notNullState) {
    110       // Generate an error node.
    111       ExplodedNode *N = C.generateSink(nullState);
    112       if (!N)
    113         return;
    114 
    115       // We know that 'location' cannot be non-null.  This is what
    116       // we call an "explicit" null dereference.
    117       if (!BT_null)
    118         BT_null.reset(new BuiltinBug("Dereference of null pointer"));
    119 
    120       SmallString<100> buf;
    121       SmallVector<SourceRange, 2> Ranges;
    122 
    123       // Walk through lvalue casts to get the original expression
    124       // that syntactically caused the load.
    125       if (const Expr *expr = dyn_cast<Expr>(S))
    126         S = expr->IgnoreParenLValueCasts();
    127 
    128       const MemRegion *sourceR = 0;
    129 
    130       switch (S->getStmtClass()) {
    131         case Stmt::ArraySubscriptExprClass: {
    132           llvm::raw_svector_ostream os(buf);
    133           os << "Array access";
    134           const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
    135           sourceR =
    136             AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
    137                            state.getPtr(), LCtx);
    138           os << " results in a null pointer dereference";
    139           break;
    140         }
    141         case Stmt::UnaryOperatorClass: {
    142           llvm::raw_svector_ostream os(buf);
    143           os << "Dereference of null pointer";
    144           const UnaryOperator *U = cast<UnaryOperator>(S);
    145           sourceR =
    146             AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
    147                            state.getPtr(), LCtx, true);
    148           break;
    149         }
    150         case Stmt::MemberExprClass: {
    151           const MemberExpr *M = cast<MemberExpr>(S);
    152           if (M->isArrow()) {
    153             llvm::raw_svector_ostream os(buf);
    154             os << "Access to field '" << M->getMemberNameInfo()
    155                << "' results in a dereference of a null pointer";
    156             sourceR =
    157               AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
    158                              state.getPtr(), LCtx, true);
    159           }
    160           break;
    161         }
    162         case Stmt::ObjCIvarRefExprClass: {
    163           const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
    164           if (const DeclRefExpr *DR =
    165               dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) {
    166             if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
    167               llvm::raw_svector_ostream os(buf);
    168               os << "Instance variable access (via '" << VD->getName()
    169                  << "') results in a null pointer dereference";
    170             }
    171           }
    172           Ranges.push_back(IV->getSourceRange());
    173           break;
    174         }
    175         default:
    176           break;
    177       }
    178 
    179       BugReport *report =
    180         new BugReport(*BT_null,
    181                               buf.empty() ? BT_null->getDescription():buf.str(),
    182                               N);
    183 
    184       report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
    185                                         bugreporter::GetDerefExpr(N), report));
    186 
    187       for (SmallVectorImpl<SourceRange>::iterator
    188             I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
    189         report->addRange(*I);
    190 
    191       if (sourceR) {
    192         report->markInteresting(sourceR);
    193         report->markInteresting(state->getRawSVal(loc::MemRegionVal(sourceR)));
    194       }
    195 
    196       C.EmitReport(report);
    197       return;
    198     }
    199     else {
    200       // Otherwise, we have the case where the location could either be
    201       // null or not-null.  Record the error node as an "implicit" null
    202       // dereference.
    203       if (ExplodedNode *N = C.generateSink(nullState)) {
    204         ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
    205         dispatchEvent(event);
    206       }
    207     }
    208   }
    209 
    210   // From this point forward, we know that the location is not null.
    211   C.addTransition(notNullState);
    212 }
    213 
    214 void ento::registerDereferenceChecker(CheckerManager &mgr) {
    215   mgr.registerChecker<DereferenceChecker>();
    216 }
    217