NonNullParamChecker.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. //===--- NonNullParamChecker.cpp - Undefined arguments 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 NonNullParamChecker, which checks for arguments expected not to
  11. // be null due to:
  12. // - the corresponding parameters being declared to have nonnull attribute
  13. // - the corresponding parameters being references; since the call would form
  14. // a reference to a null pointer
  15. //
  16. //===----------------------------------------------------------------------===//
  17. #include "ClangSACheckers.h"
  18. #include "clang/AST/Attr.h"
  19. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  20. #include "clang/StaticAnalyzer/Core/Checker.h"
  21. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  22. #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
  23. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  24. using namespace clang;
  25. using namespace ento;
  26. namespace {
  27. class NonNullParamChecker
  28. : public Checker< check::PreCall > {
  29. mutable std::unique_ptr<BugType> BTAttrNonNull;
  30. mutable std::unique_ptr<BugType> BTNullRefArg;
  31. public:
  32. void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
  33. BugReport *genReportNullAttrNonNull(const ExplodedNode *ErrorN,
  34. const Expr *ArgE) const;
  35. BugReport *genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
  36. const Expr *ArgE) const;
  37. };
  38. } // end anonymous namespace
  39. void NonNullParamChecker::checkPreCall(const CallEvent &Call,
  40. CheckerContext &C) const {
  41. const Decl *FD = Call.getDecl();
  42. if (!FD)
  43. return;
  44. const NonNullAttr *Att = FD->getAttr<NonNullAttr>();
  45. ProgramStateRef state = C.getState();
  46. CallEvent::param_type_iterator TyI = Call.param_type_begin(),
  47. TyE = Call.param_type_end();
  48. for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx){
  49. // Check if the parameter is a reference. We want to report when reference
  50. // to a null pointer is passed as a paramter.
  51. bool haveRefTypeParam = false;
  52. if (TyI != TyE) {
  53. haveRefTypeParam = (*TyI)->isReferenceType();
  54. TyI++;
  55. }
  56. bool haveAttrNonNull = Att && Att->isNonNull(idx);
  57. if (!haveAttrNonNull) {
  58. // Check if the parameter is also marked 'nonnull'.
  59. ArrayRef<ParmVarDecl*> parms = Call.parameters();
  60. if (idx < parms.size())
  61. haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>();
  62. }
  63. if (!haveRefTypeParam && !haveAttrNonNull)
  64. continue;
  65. // If the value is unknown or undefined, we can't perform this check.
  66. const Expr *ArgE = Call.getArgExpr(idx);
  67. SVal V = Call.getArgSVal(idx);
  68. Optional<DefinedSVal> DV = V.getAs<DefinedSVal>();
  69. if (!DV)
  70. continue;
  71. // Process the case when the argument is not a location.
  72. assert(!haveRefTypeParam || DV->getAs<Loc>());
  73. if (haveAttrNonNull && !DV->getAs<Loc>()) {
  74. // If the argument is a union type, we want to handle a potential
  75. // transparent_union GCC extension.
  76. if (!ArgE)
  77. continue;
  78. QualType T = ArgE->getType();
  79. const RecordType *UT = T->getAsUnionType();
  80. if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
  81. continue;
  82. if (Optional<nonloc::CompoundVal> CSV =
  83. DV->getAs<nonloc::CompoundVal>()) {
  84. nonloc::CompoundVal::iterator CSV_I = CSV->begin();
  85. assert(CSV_I != CSV->end());
  86. V = *CSV_I;
  87. DV = V.getAs<DefinedSVal>();
  88. assert(++CSV_I == CSV->end());
  89. // FIXME: Handle (some_union){ some_other_union_val }, which turns into
  90. // a LazyCompoundVal inside a CompoundVal.
  91. if (!V.getAs<Loc>())
  92. continue;
  93. // Retrieve the corresponding expression.
  94. if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
  95. if (const InitListExpr *IE =
  96. dyn_cast<InitListExpr>(CE->getInitializer()))
  97. ArgE = dyn_cast<Expr>(*(IE->begin()));
  98. } else {
  99. // FIXME: Handle LazyCompoundVals?
  100. continue;
  101. }
  102. }
  103. ConstraintManager &CM = C.getConstraintManager();
  104. ProgramStateRef stateNotNull, stateNull;
  105. std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
  106. if (stateNull && !stateNotNull) {
  107. // Generate an error node. Check for a null node in case
  108. // we cache out.
  109. if (ExplodedNode *errorNode = C.generateSink(stateNull)) {
  110. BugReport *R = nullptr;
  111. if (haveAttrNonNull)
  112. R = genReportNullAttrNonNull(errorNode, ArgE);
  113. else if (haveRefTypeParam)
  114. R = genReportReferenceToNullPointer(errorNode, ArgE);
  115. // Highlight the range of the argument that was null.
  116. R->addRange(Call.getArgSourceRange(idx));
  117. // Emit the bug report.
  118. C.emitReport(R);
  119. }
  120. // Always return. Either we cached out or we just emitted an error.
  121. return;
  122. }
  123. // If a pointer value passed the check we should assume that it is
  124. // indeed not null from this point forward.
  125. assert(stateNotNull);
  126. state = stateNotNull;
  127. }
  128. // If we reach here all of the arguments passed the nonnull check.
  129. // If 'state' has been updated generated a new node.
  130. C.addTransition(state);
  131. }
  132. BugReport *NonNullParamChecker::genReportNullAttrNonNull(
  133. const ExplodedNode *ErrorNode, const Expr *ArgE) const {
  134. // Lazily allocate the BugType object if it hasn't already been
  135. // created. Ownership is transferred to the BugReporter object once
  136. // the BugReport is passed to 'EmitWarning'.
  137. if (!BTAttrNonNull)
  138. BTAttrNonNull.reset(new BugType(
  139. this, "Argument with 'nonnull' attribute passed null", "API"));
  140. BugReport *R = new BugReport(*BTAttrNonNull,
  141. "Null pointer passed as an argument to a 'nonnull' parameter",
  142. ErrorNode);
  143. if (ArgE)
  144. bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R);
  145. return R;
  146. }
  147. BugReport *NonNullParamChecker::genReportReferenceToNullPointer(
  148. const ExplodedNode *ErrorNode, const Expr *ArgE) const {
  149. if (!BTNullRefArg)
  150. BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
  151. BugReport *R = new BugReport(*BTNullRefArg,
  152. "Forming reference to null pointer",
  153. ErrorNode);
  154. if (ArgE) {
  155. const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
  156. if (!ArgEDeref)
  157. ArgEDeref = ArgE;
  158. bugreporter::trackNullOrUndefValue(ErrorNode,
  159. ArgEDeref,
  160. *R);
  161. }
  162. return R;
  163. }
  164. void ento::registerNonNullParamChecker(CheckerManager &mgr) {
  165. mgr.registerChecker<NonNullParamChecker>();
  166. }