DereferenceChecker.cpp 11 KB

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