OSObjectCStyleCast.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. //===- OSObjectCStyleCast.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 file defines OSObjectCStyleCast checker, which checks for C-style casts
  10. // of OSObjects. Such casts almost always indicate a code smell,
  11. // as an explicit static or dynamic cast should be used instead.
  12. //===----------------------------------------------------------------------===//
  13. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  14. #include "clang/ASTMatchers/ASTMatchFinder.h"
  15. #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
  16. #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
  17. #include "clang/StaticAnalyzer/Core/Checker.h"
  18. #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
  19. #include "llvm/Support/Debug.h"
  20. using namespace clang;
  21. using namespace ento;
  22. using namespace ast_matchers;
  23. namespace {
  24. static constexpr const char *const WarnAtNode = "WarnAtNode";
  25. static constexpr const char *const WarnRecordDecl = "WarnRecordDecl";
  26. class OSObjectCStyleCastChecker : public Checker<check::ASTCodeBody> {
  27. public:
  28. void checkASTCodeBody(const Decl *D, AnalysisManager &AM,
  29. BugReporter &BR) const;
  30. };
  31. } // namespace
  32. namespace clang {
  33. namespace ast_matchers {
  34. AST_MATCHER_P(StringLiteral, mentionsBoundType, std::string, BindingID) {
  35. return Builder->removeBindings([this, &Node](const BoundNodesMap &Nodes) {
  36. const auto &BN = Nodes.getNode(this->BindingID);
  37. if (const auto *ND = BN.get<NamedDecl>()) {
  38. return ND->getName() != Node.getString();
  39. }
  40. return true;
  41. });
  42. }
  43. } // end namespace ast_matchers
  44. } // end namespace clang
  45. static void emitDiagnostics(const BoundNodes &Nodes,
  46. BugReporter &BR,
  47. AnalysisDeclContext *ADC,
  48. const OSObjectCStyleCastChecker *Checker) {
  49. const auto *CE = Nodes.getNodeAs<CastExpr>(WarnAtNode);
  50. const CXXRecordDecl *RD = Nodes.getNodeAs<CXXRecordDecl>(WarnRecordDecl);
  51. assert(CE && RD);
  52. std::string Diagnostics;
  53. llvm::raw_string_ostream OS(Diagnostics);
  54. OS << "C-style cast of an OSObject is prone to type confusion attacks; "
  55. << "use 'OSRequiredCast' if the object is definitely of type '"
  56. << RD->getNameAsString() << "', or 'OSDynamicCast' followed by "
  57. << "a null check if unsure",
  58. BR.EmitBasicReport(
  59. ADC->getDecl(),
  60. Checker,
  61. /*Name=*/"OSObject C-Style Cast",
  62. categories::SecurityError,
  63. OS.str(),
  64. PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), ADC),
  65. CE->getSourceRange());
  66. }
  67. static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) {
  68. return hasType(pointerType(pointee(hasDeclaration(DeclM))));
  69. }
  70. void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D,
  71. AnalysisManager &AM,
  72. BugReporter &BR) const {
  73. AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
  74. auto DynamicCastM = callExpr(callee(functionDecl(hasName("safeMetaCast"))));
  75. // 'allocClassWithName' allocates an object with the given type.
  76. // The type is actually provided as a string argument (type's name).
  77. // This makes the following pattern possible:
  78. //
  79. // Foo *object = (Foo *)allocClassWithName("Foo");
  80. //
  81. // While OSRequiredCast can be used here, it is still not a useful warning.
  82. auto AllocClassWithNameM = callExpr(
  83. callee(functionDecl(hasName("allocClassWithName"))),
  84. // Here we want to make sure that the string argument matches the
  85. // type in the cast expression.
  86. hasArgument(0, stringLiteral(mentionsBoundType(WarnRecordDecl))));
  87. auto OSObjTypeM =
  88. hasTypePointingTo(cxxRecordDecl(isDerivedFrom("OSMetaClassBase")));
  89. auto OSObjSubclassM = hasTypePointingTo(
  90. cxxRecordDecl(isDerivedFrom("OSObject")).bind(WarnRecordDecl));
  91. auto CastM =
  92. cStyleCastExpr(
  93. allOf(OSObjSubclassM,
  94. hasSourceExpression(
  95. allOf(OSObjTypeM,
  96. unless(anyOf(DynamicCastM, AllocClassWithNameM))))))
  97. .bind(WarnAtNode);
  98. auto Matches =
  99. match(stmt(forEachDescendant(CastM)), *D->getBody(), AM.getASTContext());
  100. for (BoundNodes Match : Matches)
  101. emitDiagnostics(Match, BR, ADC, this);
  102. }
  103. void ento::registerOSObjectCStyleCast(CheckerManager &Mgr) {
  104. Mgr.registerChecker<OSObjectCStyleCastChecker>();
  105. }
  106. bool ento::shouldRegisterOSObjectCStyleCast(const CheckerManager &mgr) {
  107. return true;
  108. }