brintos

brintos / llvm-project-archived public Read only

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