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