clang API Documentation

DereferenceChecker.cpp
Go to the documentation of this file.
00001 //== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // This defines NullDerefChecker, a builtin check in ExprEngine that performs
00011 // checks for null pointers at loads and stores.
00012 //
00013 //===----------------------------------------------------------------------===//
00014 
00015 #include "ClangSACheckers.h"
00016 #include "clang/AST/ExprObjC.h"
00017 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
00018 #include "clang/StaticAnalyzer/Core/Checker.h"
00019 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
00020 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
00021 #include "llvm/ADT/SmallString.h"
00022 #include "llvm/Support/raw_ostream.h"
00023 
00024 using namespace clang;
00025 using namespace ento;
00026 
00027 namespace {
00028 class DereferenceChecker
00029     : public Checker< check::Location,
00030                       check::Bind,
00031                       EventDispatcher<ImplicitNullDerefEvent> > {
00032   mutable std::unique_ptr<BuiltinBug> BT_null;
00033   mutable std::unique_ptr<BuiltinBug> BT_undef;
00034 
00035   void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C,
00036                  bool IsBind = false) const;
00037 
00038 public:
00039   void checkLocation(SVal location, bool isLoad, const Stmt* S,
00040                      CheckerContext &C) const;
00041   void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
00042 
00043   static void AddDerefSource(raw_ostream &os,
00044                              SmallVectorImpl<SourceRange> &Ranges,
00045                              const Expr *Ex, const ProgramState *state,
00046                              const LocationContext *LCtx,
00047                              bool loadedFrom = false);
00048 };
00049 } // end anonymous namespace
00050 
00051 void
00052 DereferenceChecker::AddDerefSource(raw_ostream &os,
00053                                    SmallVectorImpl<SourceRange> &Ranges,
00054                                    const Expr *Ex,
00055                                    const ProgramState *state,
00056                                    const LocationContext *LCtx,
00057                                    bool loadedFrom) {
00058   Ex = Ex->IgnoreParenLValueCasts();
00059   switch (Ex->getStmtClass()) {
00060     default:
00061       break;
00062     case Stmt::DeclRefExprClass: {
00063       const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
00064       if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
00065         os << " (" << (loadedFrom ? "loaded from" : "from")
00066            << " variable '" <<  VD->getName() << "')";
00067         Ranges.push_back(DR->getSourceRange());
00068       }
00069       break;
00070     }
00071     case Stmt::MemberExprClass: {
00072       const MemberExpr *ME = cast<MemberExpr>(Ex);
00073       os << " (" << (loadedFrom ? "loaded from" : "via")
00074          << " field '" << ME->getMemberNameInfo() << "')";
00075       SourceLocation L = ME->getMemberLoc();
00076       Ranges.push_back(SourceRange(L, L));
00077       break;
00078     }
00079     case Stmt::ObjCIvarRefExprClass: {
00080       const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
00081       os << " (" << (loadedFrom ? "loaded from" : "via")
00082          << " ivar '" << IV->getDecl()->getName() << "')";
00083       SourceLocation L = IV->getLocation();
00084       Ranges.push_back(SourceRange(L, L));
00085       break;
00086     }    
00087   }
00088 }
00089 
00090 void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
00091                                    CheckerContext &C, bool IsBind) const {
00092   // Generate an error node.
00093   ExplodedNode *N = C.generateSink(State);
00094   if (!N)
00095     return;
00096 
00097   // We know that 'location' cannot be non-null.  This is what
00098   // we call an "explicit" null dereference.
00099   if (!BT_null)
00100     BT_null.reset(new BuiltinBug(this, "Dereference of null pointer"));
00101 
00102   SmallString<100> buf;
00103   llvm::raw_svector_ostream os(buf);
00104 
00105   SmallVector<SourceRange, 2> Ranges;
00106 
00107   // Walk through lvalue casts to get the original expression
00108   // that syntactically caused the load.
00109   if (const Expr *expr = dyn_cast<Expr>(S))
00110     S = expr->IgnoreParenLValueCasts();
00111 
00112   if (IsBind) {
00113     if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) {
00114       if (BO->isAssignmentOp())
00115         S = BO->getRHS();
00116     } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
00117       assert(DS->isSingleDecl() && "We process decls one by one");
00118       if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
00119         if (const Expr *Init = VD->getAnyInitializer())
00120           S = Init;
00121     }
00122   }
00123 
00124   switch (S->getStmtClass()) {
00125   case Stmt::ArraySubscriptExprClass: {
00126     os << "Array access";
00127     const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
00128     AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
00129                    State.get(), N->getLocationContext());
00130     os << " results in a null pointer dereference";
00131     break;
00132   }
00133   case Stmt::UnaryOperatorClass: {
00134     os << "Dereference of null pointer";
00135     const UnaryOperator *U = cast<UnaryOperator>(S);
00136     AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
00137                    State.get(), N->getLocationContext(), true);
00138     break;
00139   }
00140   case Stmt::MemberExprClass: {
00141     const MemberExpr *M = cast<MemberExpr>(S);
00142     if (M->isArrow() || bugreporter::isDeclRefExprToReference(M->getBase())) {
00143       os << "Access to field '" << M->getMemberNameInfo()
00144          << "' results in a dereference of a null pointer";
00145       AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
00146                      State.get(), N->getLocationContext(), true);
00147     }
00148     break;
00149   }
00150   case Stmt::ObjCIvarRefExprClass: {
00151     const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
00152     os << "Access to instance variable '" << *IV->getDecl()
00153        << "' results in a dereference of a null pointer";
00154     AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
00155                    State.get(), N->getLocationContext(), true);
00156     break;
00157   }
00158   default:
00159     break;
00160   }
00161 
00162   os.flush();
00163   BugReport *report =
00164     new BugReport(*BT_null,
00165                   buf.empty() ? BT_null->getDescription() : buf.str(),
00166                   N);
00167 
00168   bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S), *report);
00169 
00170   for (SmallVectorImpl<SourceRange>::iterator
00171        I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
00172     report->addRange(*I);
00173 
00174   C.emitReport(report);
00175 }
00176 
00177 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
00178                                        CheckerContext &C) const {
00179   // Check for dereference of an undefined value.
00180   if (l.isUndef()) {
00181     if (ExplodedNode *N = C.generateSink()) {
00182       if (!BT_undef)
00183         BT_undef.reset(
00184             new BuiltinBug(this, "Dereference of undefined pointer value"));
00185 
00186       BugReport *report =
00187         new BugReport(*BT_undef, BT_undef->getDescription(), N);
00188       bugreporter::trackNullOrUndefValue(N, bugreporter::getDerefExpr(S),
00189                                          *report);
00190       C.emitReport(report);
00191     }
00192     return;
00193   }
00194 
00195   DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
00196 
00197   // Check for null dereferences.
00198   if (!location.getAs<Loc>())
00199     return;
00200 
00201   ProgramStateRef state = C.getState();
00202 
00203   ProgramStateRef notNullState, nullState;
00204   std::tie(notNullState, nullState) = state->assume(location);
00205 
00206   // The explicit NULL case.
00207   if (nullState) {
00208     if (!notNullState) {
00209       reportBug(nullState, S, C);
00210       return;
00211     }
00212 
00213     // Otherwise, we have the case where the location could either be
00214     // null or not-null.  Record the error node as an "implicit" null
00215     // dereference.
00216     if (ExplodedNode *N = C.generateSink(nullState)) {
00217       ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
00218       dispatchEvent(event);
00219     }
00220   }
00221 
00222   // From this point forward, we know that the location is not null.
00223   C.addTransition(notNullState);
00224 }
00225 
00226 void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
00227                                    CheckerContext &C) const {
00228   // If we're binding to a reference, check if the value is known to be null.
00229   if (V.isUndef())
00230     return;
00231 
00232   const MemRegion *MR = L.getAsRegion();
00233   const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
00234   if (!TVR)
00235     return;
00236 
00237   if (!TVR->getValueType()->isReferenceType())
00238     return;
00239 
00240   ProgramStateRef State = C.getState();
00241 
00242   ProgramStateRef StNonNull, StNull;
00243   std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
00244 
00245   if (StNull) {
00246     if (!StNonNull) {
00247       reportBug(StNull, S, C, /*isBind=*/true);
00248       return;
00249     }
00250 
00251     // At this point the value could be either null or non-null.
00252     // Record this as an "implicit" null dereference.
00253     if (ExplodedNode *N = C.generateSink(StNull)) {
00254       ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N,
00255                                        &C.getBugReporter() };
00256       dispatchEvent(event);
00257     }
00258   }
00259 
00260   // Unlike a regular null dereference, initializing a reference with a
00261   // dereferenced null pointer does not actually cause a runtime exception in
00262   // Clang's implementation of references.
00263   //
00264   //   int &r = *p; // safe??
00265   //   if (p != NULL) return; // uh-oh
00266   //   r = 5; // trap here
00267   //
00268   // The standard says this is invalid as soon as we try to create a "null
00269   // reference" (there is no such thing), but turning this into an assumption
00270   // that 'p' is never null will not match our actual runtime behavior.
00271   // So we do not record this assumption, allowing us to warn on the last line
00272   // of this example.
00273   //
00274   // We do need to add a transition because we may have generated a sink for
00275   // the "implicit" null dereference.
00276   C.addTransition(State, this);
00277 }
00278 
00279 void ento::registerDereferenceChecker(CheckerManager &mgr) {
00280   mgr.registerChecker<DereferenceChecker>();
00281 }