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