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