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/BugReporter/BugType.h"
     18 #include "clang/StaticAnalyzer/Core/Checker.h"
     19 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
     20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
     21 #include "llvm/ADT/SmallString.h"
     22 #include "llvm/Support/raw_ostream.h"
     23 
     24 using namespace clang;
     25 using namespace ento;
     26 
     27 namespace {
     28 class DereferenceChecker
     29     : public Checker< check::Location,
     30                       check::Bind,
     31                       EventDispatcher<ImplicitNullDerefEvent> > {
     32   mutable OwningPtr<BuiltinBug> BT_null;
     33   mutable OwningPtr<BuiltinBug> BT_undef;
     34 
     35   void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C,
     36                  bool IsBind = false) const;
     37 
     38 public:
     39   void checkLocation(SVal location, bool isLoad, const Stmt* S,
     40                      CheckerContext &C) const;
     41   void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
     42 
     43   static void AddDerefSource(raw_ostream &os,
     44                              SmallVectorImpl<SourceRange> &Ranges,
     45                              const Expr *Ex, const ProgramState *state,
     46                              const LocationContext *LCtx,
     47                              bool loadedFrom = false);
     48 };
     49 } // end anonymous namespace
     50 
     51 void
     52 DereferenceChecker::AddDerefSource(raw_ostream &os,
     53                                    SmallVectorImpl<SourceRange> &Ranges,
     54                                    const Expr *Ex,
     55                                    const ProgramState *state,
     56                                    const LocationContext *LCtx,
     57                                    bool loadedFrom) {
     58   Ex = Ex->IgnoreParenLValueCasts();
     59   switch (Ex->getStmtClass()) {
     60     default:
     61       break;
     62     case Stmt::DeclRefExprClass: {
     63       const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
     64       if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
     65         os << " (" << (loadedFrom ? "loaded from" : "from")
     66            << " variable '" <<  VD->getName() << "')";
     67         Ranges.push_back(DR->getSourceRange());
     68       }
     69       break;
     70     }
     71     case Stmt::MemberExprClass: {
     72       const MemberExpr *ME = cast<MemberExpr>(Ex);
     73       os << " (" << (loadedFrom ? "loaded from" : "via")
     74          << " field '" << ME->getMemberNameInfo() << "')";
     75       SourceLocation L = ME->getMemberLoc();
     76       Ranges.push_back(SourceRange(L, L));
     77       break;
     78     }
     79     case Stmt::ObjCIvarRefExprClass: {
     80       const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
     81       os << " (" << (loadedFrom ? "loaded from" : "via")
     82          << " ivar '" << IV->getDecl()->getName() << "')";
     83       SourceLocation L = IV->getLocation();
     84       Ranges.push_back(SourceRange(L, L));
     85       break;
     86     }
     87   }
     88 }
     89 
     90 void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
     91                                    CheckerContext &C, bool IsBind) const {
     92   // Generate an error node.
     93   ExplodedNode *N = C.generateSink(State);
     94   if (!N)
     95     return;
     96 
     97   // We know that 'location' cannot be non-null.  This is what
     98   // we call an "explicit" null dereference.
     99   if (!BT_null)
    100     BT_null.reset(new BuiltinBug("Dereference of null pointer"));
    101 
    102   SmallString<100> buf;
    103   llvm::raw_svector_ostream os(buf);
    104 
    105   SmallVector<SourceRange, 2> Ranges;
    106 
    107   // Walk through lvalue casts to get the original expression
    108   // that syntactically caused the load.
    109   if (const Expr *expr = dyn_cast<Expr>(S))
    110     S = expr->IgnoreParenLValueCasts();
    111 
    112   if (IsBind) {
    113     if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) {
    114       if (BO->isAssignmentOp())
    115         S = BO->getRHS();
    116     } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
    117       assert(DS->isSingleDecl() && "We process decls one by one");
    118       if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
    119         if (const Expr *Init = VD->getAnyInitializer())
    120           S = Init;
    121     }
    122   }
    123 
    124   switch (S->getStmtClass()) {
    125   case Stmt::ArraySubscriptExprClass: {
    126     os << "Array access";
    127     const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
    128     AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
    129                    State.getPtr(), N->getLocationContext());
    130     os << " results in a null pointer dereference";
    131     break;
    132   }
    133   case Stmt::UnaryOperatorClass: {
    134     os << "Dereference of null pointer";
    135     const UnaryOperator *U = cast<UnaryOperator>(S);
    136     AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
    137                    State.getPtr(), N->getLocationContext(), true);
    138     break;
    139   }
    140   case Stmt::MemberExprClass: {
    141     const MemberExpr *M = cast<MemberExpr>(S);
    142     if (M->isArrow() || bugreporter::isDeclRefExprToReference(M->getBase())) {
    143       os << "Access to field '" << M->getMemberNameInfo()
    144          << "' results in a dereference of a null pointer";
    145       AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
    146                      State.getPtr(), N->getLocationContext(), true);
    147     }
    148     break;
    149   }
    150   case Stmt::ObjCIvarRefExprClass: {
    151     const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
    152     os << "Access to instance variable '" << *IV->getDecl()
    153        << "' results in a dereference of a null pointer";
    154     AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
    155                    State.getPtr(), N->getLocationContext(), true);
    156     break;
    157   }
    158   default:
    159     break;
    160   }
    161 
    162   os.flush();
    163   BugReport *report =
    164     new BugReport(*BT_null,
    165                   buf.empty() ? BT_null->getDescription() : buf.str(),
    166                   N);
    167 
    168   bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), *report);
    169 
    170   for (SmallVectorImpl<SourceRange>::iterator
    171        I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
    172     report->addRange(*I);
    173 
    174   C.emitReport(report);
    175 }
    176 
    177 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
    178                                        CheckerContext &C) const {
    179   // Check for dereference of an undefined value.
    180   if (l.isUndef()) {
    181     if (ExplodedNode *N = C.generateSink()) {
    182       if (!BT_undef)
    183         BT_undef.reset(new BuiltinBug("Dereference of undefined pointer value"));
    184 
    185       BugReport *report =
    186         new BugReport(*BT_undef, BT_undef->getDescription(), N);
    187       bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S),
    188                                          *report);
    189       C.emitReport(report);
    190     }
    191     return;
    192   }
    193 
    194   DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
    195 
    196   // Check for null dereferences.
    197   if (!location.getAs<Loc>())
    198     return;
    199 
    200   ProgramStateRef state = C.getState();
    201 
    202   ProgramStateRef notNullState, nullState;
    203   llvm::tie(notNullState, nullState) = state->assume(location);
    204 
    205   // The explicit NULL case.
    206   if (nullState) {
    207     if (!notNullState) {
    208       reportBug(nullState, S, C);
    209       return;
    210     }
    211 
    212     // Otherwise, we have the case where the location could either be
    213     // null or not-null.  Record the error node as an "implicit" null
    214     // dereference.
    215     if (ExplodedNode *N = C.generateSink(nullState)) {
    216       ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
    217       dispatchEvent(event);
    218     }
    219   }
    220 
    221   // From this point forward, we know that the location is not null.
    222   C.addTransition(notNullState);
    223 }
    224 
    225 void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
    226                                    CheckerContext &C) const {
    227   // If we're binding to a reference, check if the value is known to be null.
    228   if (V.isUndef())
    229     return;
    230 
    231   const MemRegion *MR = L.getAsRegion();
    232   const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
    233   if (!TVR)
    234     return;
    235 
    236   if (!TVR->getValueType()->isReferenceType())
    237     return;
    238 
    239   ProgramStateRef State = C.getState();
    240 
    241   ProgramStateRef StNonNull, StNull;
    242   llvm::tie(StNonNull, StNull) =
    243       State->assume(V.castAs<DefinedOrUnknownSVal>());
    244 
    245   if (StNull) {
    246     if (!StNonNull) {
    247       reportBug(StNull, S, C, /*isBind=*/true);
    248       return;
    249     }
    250 
    251     // At this point the value could be either null or non-null.
    252     // Record this as an "implicit" null dereference.
    253     if (ExplodedNode *N = C.generateSink(StNull)) {
    254       ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N,
    255                                        &C.getBugReporter() };
    256       dispatchEvent(event);
    257     }
    258   }
    259 
    260   // Unlike a regular null dereference, initializing a reference with a
    261   // dereferenced null pointer does not actually cause a runtime exception in
    262   // Clang's implementation of references.
    263   //
    264   //   int &r = *p; // safe??
    265   //   if (p != NULL) return; // uh-oh
    266   //   r = 5; // trap here
    267   //
    268   // The standard says this is invalid as soon as we try to create a "null
    269   // reference" (there is no such thing), but turning this into an assumption
    270   // that 'p' is never null will not match our actual runtime behavior.
    271   // So we do not record this assumption, allowing us to warn on the last line
    272   // of this example.
    273   //
    274   // We do need to add a transition because we may have generated a sink for
    275   // the "implicit" null dereference.
    276   C.addTransition(State, this);
    277 }
    278 
    279 void ento::registerDereferenceChecker(CheckerManager &mgr) {
    280   mgr.registerChecker<DereferenceChecker>();
    281 }
    282