EnumCastOutOfRangeChecker.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. //===- EnumCastOutOfRangeChecker.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. // The EnumCastOutOfRangeChecker is responsible for checking integer to
  10. // enumeration casts that could result in undefined values. This could happen
  11. // if the value that we cast from is out of the value range of the enumeration.
  12. // Reference:
  13. // [ISO/IEC 14882-2014] ISO/IEC 14882-2014.
  14. // Programming Languages — C++, Fourth Edition. 2014.
  15. // C++ Standard, [dcl.enum], in paragraph 8, which defines the range of an enum
  16. // C++ Standard, [expr.static.cast], paragraph 10, which defines the behaviour
  17. // of casting an integer value that is out of range
  18. // SEI CERT C++ Coding Standard, INT50-CPP. Do not cast to an out-of-range
  19. // enumeration value
  20. //===----------------------------------------------------------------------===//
  21. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  22. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  23. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  24. #include <optional>
  25. using namespace clang;
  26. using namespace ento;
  27. namespace {
  28. // This evaluator checks two SVals for equality. The first SVal is provided via
  29. // the constructor, the second is the parameter of the overloaded () operator.
  30. // It uses the in-built ConstraintManager to resolve the equlity to possible or
  31. // not possible ProgramStates.
  32. class ConstraintBasedEQEvaluator {
  33. const DefinedOrUnknownSVal CompareValue;
  34. const ProgramStateRef PS;
  35. SValBuilder &SVB;
  36. public:
  37. ConstraintBasedEQEvaluator(CheckerContext &C,
  38. const DefinedOrUnknownSVal CompareValue)
  39. : CompareValue(CompareValue), PS(C.getState()), SVB(C.getSValBuilder()) {}
  40. bool operator()(const llvm::APSInt &EnumDeclInitValue) {
  41. DefinedOrUnknownSVal EnumDeclValue = SVB.makeIntVal(EnumDeclInitValue);
  42. DefinedOrUnknownSVal ElemEqualsValueToCast =
  43. SVB.evalEQ(PS, EnumDeclValue, CompareValue);
  44. return static_cast<bool>(PS->assume(ElemEqualsValueToCast, true));
  45. }
  46. };
  47. // This checker checks CastExpr statements.
  48. // If the value provided to the cast is one of the values the enumeration can
  49. // represent, the said value matches the enumeration. If the checker can
  50. // establish the impossibility of matching it gives a warning.
  51. // Being conservative, it does not warn if there is slight possibility the
  52. // value can be matching.
  53. class EnumCastOutOfRangeChecker : public Checker<check::PreStmt<CastExpr>> {
  54. mutable std::unique_ptr<BuiltinBug> EnumValueCastOutOfRange;
  55. void reportWarning(CheckerContext &C) const;
  56. public:
  57. void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
  58. };
  59. using EnumValueVector = llvm::SmallVector<llvm::APSInt, 6>;
  60. // Collects all of the values an enum can represent (as SVals).
  61. EnumValueVector getDeclValuesForEnum(const EnumDecl *ED) {
  62. EnumValueVector DeclValues(
  63. std::distance(ED->enumerator_begin(), ED->enumerator_end()));
  64. llvm::transform(ED->enumerators(), DeclValues.begin(),
  65. [](const EnumConstantDecl *D) { return D->getInitVal(); });
  66. return DeclValues;
  67. }
  68. } // namespace
  69. void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const {
  70. if (const ExplodedNode *N = C.generateNonFatalErrorNode()) {
  71. if (!EnumValueCastOutOfRange)
  72. EnumValueCastOutOfRange.reset(
  73. new BuiltinBug(this, "Enum cast out of range",
  74. "The value provided to the cast expression is not in "
  75. "the valid range of values for the enum"));
  76. C.emitReport(std::make_unique<PathSensitiveBugReport>(
  77. *EnumValueCastOutOfRange, EnumValueCastOutOfRange->getDescription(),
  78. N));
  79. }
  80. }
  81. void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
  82. CheckerContext &C) const {
  83. // Only perform enum range check on casts where such checks are valid. For
  84. // all other cast kinds (where enum range checks are unnecessary or invalid),
  85. // just return immediately. TODO: The set of casts allowed for enum range
  86. // checking may be incomplete. Better to add a missing cast kind to enable a
  87. // missing check than to generate false negatives and have to remove those
  88. // later.
  89. switch (CE->getCastKind()) {
  90. case CK_IntegralCast:
  91. break;
  92. default:
  93. return;
  94. break;
  95. }
  96. // Get the value of the expression to cast.
  97. const std::optional<DefinedOrUnknownSVal> ValueToCast =
  98. C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>();
  99. // If the value cannot be reasoned about (not even a DefinedOrUnknownSVal),
  100. // don't analyze further.
  101. if (!ValueToCast)
  102. return;
  103. const QualType T = CE->getType();
  104. // Check whether the cast type is an enum.
  105. if (!T->isEnumeralType())
  106. return;
  107. // If the cast is an enum, get its declaration.
  108. // If the isEnumeralType() returned true, then the declaration must exist
  109. // even if it is a stub declaration. It is up to the getDeclValuesForEnum()
  110. // function to handle this.
  111. const EnumDecl *ED = T->castAs<EnumType>()->getDecl();
  112. EnumValueVector DeclValues = getDeclValuesForEnum(ED);
  113. // Check if any of the enum values possibly match.
  114. bool PossibleValueMatch = llvm::any_of(
  115. DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast));
  116. // If there is no value that can possibly match any of the enum values, then
  117. // warn.
  118. if (!PossibleValueMatch)
  119. reportWarning(C);
  120. }
  121. void ento::registerEnumCastOutOfRangeChecker(CheckerManager &mgr) {
  122. mgr.registerChecker<EnumCastOutOfRangeChecker>();
  123. }
  124. bool ento::shouldRegisterEnumCastOutOfRangeChecker(const CheckerManager &mgr) {
  125. return true;
  126. }