UndefResultChecker.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. //=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===//
  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 UndefResultChecker, a builtin check in ExprEngine that
  10. // performs checks for undefined results of non-assignment binary operators.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  14. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  15. #include "clang/StaticAnalyzer/Core/Checker.h"
  16. #include "clang/StaticAnalyzer/Core/CheckerManager.h"
  17. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  18. #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
  19. #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
  20. #include "llvm/ADT/SmallString.h"
  21. #include "llvm/Support/raw_ostream.h"
  22. using namespace clang;
  23. using namespace ento;
  24. namespace {
  25. class UndefResultChecker
  26. : public Checker< check::PostStmt<BinaryOperator> > {
  27. mutable std::unique_ptr<BugType> BT;
  28. public:
  29. void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const;
  30. };
  31. } // end anonymous namespace
  32. static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex) {
  33. ProgramStateRef state = C.getState();
  34. if (!isa<ArraySubscriptExpr>(Ex))
  35. return false;
  36. SVal Loc = C.getSVal(Ex);
  37. if (!Loc.isValid())
  38. return false;
  39. const MemRegion *MR = Loc.castAs<loc::MemRegionVal>().getRegion();
  40. const ElementRegion *ER = dyn_cast<ElementRegion>(MR);
  41. if (!ER)
  42. return false;
  43. DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
  44. DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
  45. state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType());
  46. ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true);
  47. ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false);
  48. return StOutBound && !StInBound;
  49. }
  50. static bool isShiftOverflow(const BinaryOperator *B, CheckerContext &C) {
  51. return C.isGreaterOrEqual(
  52. B->getRHS(), C.getASTContext().getIntWidth(B->getLHS()->getType()));
  53. }
  54. static bool isLeftShiftResultUnrepresentable(const BinaryOperator *B,
  55. CheckerContext &C) {
  56. SValBuilder &SB = C.getSValBuilder();
  57. ProgramStateRef State = C.getState();
  58. const llvm::APSInt *LHS = SB.getKnownValue(State, C.getSVal(B->getLHS()));
  59. const llvm::APSInt *RHS = SB.getKnownValue(State, C.getSVal(B->getRHS()));
  60. assert(LHS && RHS && "Values unknown, inconsistent state");
  61. return (unsigned)RHS->getZExtValue() > LHS->countLeadingZeros();
  62. }
  63. void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
  64. CheckerContext &C) const {
  65. if (C.getSVal(B).isUndef()) {
  66. // Do not report assignments of uninitialized values inside swap functions.
  67. // This should allow to swap partially uninitialized structs
  68. // (radar://14129997)
  69. if (const FunctionDecl *EnclosingFunctionDecl =
  70. dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
  71. if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
  72. return;
  73. // Generate an error node.
  74. ExplodedNode *N = C.generateErrorNode();
  75. if (!N)
  76. return;
  77. if (!BT)
  78. BT.reset(
  79. new BuiltinBug(this, "Result of operation is garbage or undefined"));
  80. SmallString<256> sbuf;
  81. llvm::raw_svector_ostream OS(sbuf);
  82. const Expr *Ex = nullptr;
  83. bool isLeft = true;
  84. if (C.getSVal(B->getLHS()).isUndef()) {
  85. Ex = B->getLHS()->IgnoreParenCasts();
  86. isLeft = true;
  87. }
  88. else if (C.getSVal(B->getRHS()).isUndef()) {
  89. Ex = B->getRHS()->IgnoreParenCasts();
  90. isLeft = false;
  91. }
  92. if (Ex) {
  93. OS << "The " << (isLeft ? "left" : "right") << " operand of '"
  94. << BinaryOperator::getOpcodeStr(B->getOpcode())
  95. << "' is a garbage value";
  96. if (isArrayIndexOutOfBounds(C, Ex))
  97. OS << " due to array index out of bounds";
  98. } else {
  99. // Neither operand was undefined, but the result is undefined.
  100. if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
  101. B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
  102. C.isNegative(B->getRHS())) {
  103. OS << "The result of the "
  104. << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
  105. : "right")
  106. << " shift is undefined because the right operand is negative";
  107. Ex = B->getRHS();
  108. } else if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
  109. B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
  110. isShiftOverflow(B, C)) {
  111. OS << "The result of the "
  112. << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
  113. : "right")
  114. << " shift is undefined due to shifting by ";
  115. Ex = B->getRHS();
  116. SValBuilder &SB = C.getSValBuilder();
  117. const llvm::APSInt *I =
  118. SB.getKnownValue(C.getState(), C.getSVal(B->getRHS()));
  119. if (!I)
  120. OS << "a value that is";
  121. else if (I->isUnsigned())
  122. OS << '\'' << I->getZExtValue() << "\', which is";
  123. else
  124. OS << '\'' << I->getSExtValue() << "\', which is";
  125. OS << " greater or equal to the width of type '"
  126. << B->getLHS()->getType().getAsString() << "'.";
  127. } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
  128. C.isNegative(B->getLHS())) {
  129. OS << "The result of the left shift is undefined because the left "
  130. "operand is negative";
  131. Ex = B->getLHS();
  132. } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
  133. isLeftShiftResultUnrepresentable(B, C)) {
  134. ProgramStateRef State = C.getState();
  135. SValBuilder &SB = C.getSValBuilder();
  136. const llvm::APSInt *LHS =
  137. SB.getKnownValue(State, C.getSVal(B->getLHS()));
  138. const llvm::APSInt *RHS =
  139. SB.getKnownValue(State, C.getSVal(B->getRHS()));
  140. OS << "The result of the left shift is undefined due to shifting \'"
  141. << LHS->getSExtValue() << "\' by \'" << RHS->getZExtValue()
  142. << "\', which is unrepresentable in the unsigned version of "
  143. << "the return type \'" << B->getLHS()->getType().getAsString()
  144. << "\'";
  145. Ex = B->getLHS();
  146. } else {
  147. OS << "The result of the '"
  148. << BinaryOperator::getOpcodeStr(B->getOpcode())
  149. << "' expression is undefined";
  150. }
  151. }
  152. auto report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
  153. if (Ex) {
  154. report->addRange(Ex->getSourceRange());
  155. bugreporter::trackExpressionValue(N, Ex, *report);
  156. }
  157. else
  158. bugreporter::trackExpressionValue(N, B, *report);
  159. C.emitReport(std::move(report));
  160. }
  161. }
  162. void ento::registerUndefResultChecker(CheckerManager &mgr) {
  163. mgr.registerChecker<UndefResultChecker>();
  164. }
  165. bool ento::shouldRegisterUndefResultChecker(const CheckerManager &mgr) {
  166. return true;
  167. }