xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp (revision fe6060f10f634930ff71b7c50291ddc610da2475)
10b57cec5SDimitry Andric //===- OSObjectCStyleCast.cpp ------------------------------------*- C++ -*-==//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines OSObjectCStyleCast checker, which checks for C-style casts
100b57cec5SDimitry Andric // of OSObjects. Such casts almost always indicate a code smell,
110b57cec5SDimitry Andric // as an explicit static or dynamic cast should be used instead.
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
150b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
200b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
210b57cec5SDimitry Andric 
220b57cec5SDimitry Andric using namespace clang;
230b57cec5SDimitry Andric using namespace ento;
240b57cec5SDimitry Andric using namespace ast_matchers;
250b57cec5SDimitry Andric 
260b57cec5SDimitry Andric namespace {
27e8d8bef9SDimitry Andric static constexpr const char *const WarnAtNode = "WarnAtNode";
28e8d8bef9SDimitry Andric static constexpr const char *const WarnRecordDecl = "WarnRecordDecl";
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric class OSObjectCStyleCastChecker : public Checker<check::ASTCodeBody> {
310b57cec5SDimitry Andric public:
32e8d8bef9SDimitry Andric   void checkASTCodeBody(const Decl *D, AnalysisManager &AM,
330b57cec5SDimitry Andric                         BugReporter &BR) const;
340b57cec5SDimitry Andric };
35*fe6060f1SDimitry Andric } // namespace
36*fe6060f1SDimitry Andric 
37*fe6060f1SDimitry Andric namespace clang {
38*fe6060f1SDimitry Andric namespace ast_matchers {
AST_MATCHER_P(StringLiteral,mentionsBoundType,std::string,BindingID)39*fe6060f1SDimitry Andric AST_MATCHER_P(StringLiteral, mentionsBoundType, std::string, BindingID) {
40*fe6060f1SDimitry Andric   return Builder->removeBindings([this, &Node](const BoundNodesMap &Nodes) {
41*fe6060f1SDimitry Andric     const auto &BN = Nodes.getNode(this->BindingID);
42*fe6060f1SDimitry Andric     if (const auto *ND = BN.get<NamedDecl>()) {
43*fe6060f1SDimitry Andric       return ND->getName() != Node.getString();
44e8d8bef9SDimitry Andric     }
45*fe6060f1SDimitry Andric     return true;
46*fe6060f1SDimitry Andric   });
47*fe6060f1SDimitry Andric }
48*fe6060f1SDimitry Andric } // end namespace ast_matchers
49*fe6060f1SDimitry Andric } // end namespace clang
500b57cec5SDimitry Andric 
emitDiagnostics(const BoundNodes & Nodes,BugReporter & BR,AnalysisDeclContext * ADC,const OSObjectCStyleCastChecker * Checker)510b57cec5SDimitry Andric static void emitDiagnostics(const BoundNodes &Nodes,
520b57cec5SDimitry Andric                             BugReporter &BR,
530b57cec5SDimitry Andric                             AnalysisDeclContext *ADC,
540b57cec5SDimitry Andric                             const OSObjectCStyleCastChecker *Checker) {
550b57cec5SDimitry Andric   const auto *CE = Nodes.getNodeAs<CastExpr>(WarnAtNode);
56e8d8bef9SDimitry Andric   const CXXRecordDecl *RD = Nodes.getNodeAs<CXXRecordDecl>(WarnRecordDecl);
57e8d8bef9SDimitry Andric   assert(CE && RD);
580b57cec5SDimitry Andric 
590b57cec5SDimitry Andric   std::string Diagnostics;
600b57cec5SDimitry Andric   llvm::raw_string_ostream OS(Diagnostics);
61e8d8bef9SDimitry Andric   OS << "C-style cast of an OSObject is prone to type confusion attacks; "
62e8d8bef9SDimitry Andric      << "use 'OSRequiredCast' if the object is definitely of type '"
63e8d8bef9SDimitry Andric      << RD->getNameAsString() << "', or 'OSDynamicCast' followed by "
64e8d8bef9SDimitry Andric      << "a null check if unsure",
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric   BR.EmitBasicReport(
670b57cec5SDimitry Andric     ADC->getDecl(),
680b57cec5SDimitry Andric     Checker,
690b57cec5SDimitry Andric     /*Name=*/"OSObject C-Style Cast",
70e8d8bef9SDimitry Andric     categories::SecurityError,
710b57cec5SDimitry Andric     OS.str(),
720b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), ADC),
730b57cec5SDimitry Andric     CE->getSourceRange());
740b57cec5SDimitry Andric }
750b57cec5SDimitry Andric 
hasTypePointingTo(DeclarationMatcher DeclM)765ffd83dbSDimitry Andric static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) {
770b57cec5SDimitry Andric   return hasType(pointerType(pointee(hasDeclaration(DeclM))));
780b57cec5SDimitry Andric }
790b57cec5SDimitry Andric 
checkASTCodeBody(const Decl * D,AnalysisManager & AM,BugReporter & BR) const80*fe6060f1SDimitry Andric void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D,
81*fe6060f1SDimitry Andric                                                  AnalysisManager &AM,
820b57cec5SDimitry Andric                                                  BugReporter &BR) const {
830b57cec5SDimitry Andric 
840b57cec5SDimitry Andric   AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric   auto DynamicCastM = callExpr(callee(functionDecl(hasName("safeMetaCast"))));
87*fe6060f1SDimitry Andric   // 'allocClassWithName' allocates an object with the given type.
88*fe6060f1SDimitry Andric   // The type is actually provided as a string argument (type's name).
89*fe6060f1SDimitry Andric   // This makes the following pattern possible:
90*fe6060f1SDimitry Andric   //
91*fe6060f1SDimitry Andric   // Foo *object = (Foo *)allocClassWithName("Foo");
92*fe6060f1SDimitry Andric   //
93*fe6060f1SDimitry Andric   // While OSRequiredCast can be used here, it is still not a useful warning.
94*fe6060f1SDimitry Andric   auto AllocClassWithNameM = callExpr(
95*fe6060f1SDimitry Andric       callee(functionDecl(hasName("allocClassWithName"))),
96*fe6060f1SDimitry Andric       // Here we want to make sure that the string argument matches the
97*fe6060f1SDimitry Andric       // type in the cast expression.
98*fe6060f1SDimitry Andric       hasArgument(0, stringLiteral(mentionsBoundType(WarnRecordDecl))));
990b57cec5SDimitry Andric 
100*fe6060f1SDimitry Andric   auto OSObjTypeM =
101*fe6060f1SDimitry Andric       hasTypePointingTo(cxxRecordDecl(isDerivedFrom("OSMetaClassBase")));
1020b57cec5SDimitry Andric   auto OSObjSubclassM = hasTypePointingTo(
103e8d8bef9SDimitry Andric       cxxRecordDecl(isDerivedFrom("OSObject")).bind(WarnRecordDecl));
1040b57cec5SDimitry Andric 
105*fe6060f1SDimitry Andric   auto CastM =
106*fe6060f1SDimitry Andric       cStyleCastExpr(
107*fe6060f1SDimitry Andric           allOf(OSObjSubclassM,
108*fe6060f1SDimitry Andric                 hasSourceExpression(
109*fe6060f1SDimitry Andric                     allOf(OSObjTypeM,
110*fe6060f1SDimitry Andric                           unless(anyOf(DynamicCastM, AllocClassWithNameM))))))
111*fe6060f1SDimitry Andric           .bind(WarnAtNode);
1120b57cec5SDimitry Andric 
113*fe6060f1SDimitry Andric   auto Matches =
114*fe6060f1SDimitry Andric       match(stmt(forEachDescendant(CastM)), *D->getBody(), AM.getASTContext());
1150b57cec5SDimitry Andric   for (BoundNodes Match : Matches)
1160b57cec5SDimitry Andric     emitDiagnostics(Match, BR, ADC, this);
1170b57cec5SDimitry Andric }
1180b57cec5SDimitry Andric 
registerOSObjectCStyleCast(CheckerManager & Mgr)1190b57cec5SDimitry Andric void ento::registerOSObjectCStyleCast(CheckerManager &Mgr) {
1200b57cec5SDimitry Andric   Mgr.registerChecker<OSObjectCStyleCastChecker>();
1210b57cec5SDimitry Andric }
1220b57cec5SDimitry Andric 
shouldRegisterOSObjectCStyleCast(const CheckerManager & mgr)1235ffd83dbSDimitry Andric bool ento::shouldRegisterOSObjectCStyleCast(const CheckerManager &mgr) {
1240b57cec5SDimitry Andric   return true;
1250b57cec5SDimitry Andric }
126