xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //===- OSObjectCStyleCast.cpp ------------------------------------*- C++ -*-==//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric // This file defines OSObjectCStyleCast checker, which checks for C-style casts
10*0b57cec5SDimitry Andric // of OSObjects. Such casts almost always indicate a code smell,
11*0b57cec5SDimitry Andric // as an explicit static or dynamic cast should be used instead.
12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
13*0b57cec5SDimitry Andric 
14*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15*0b57cec5SDimitry Andric #include "clang/ASTMatchers/ASTMatchFinder.h"
16*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
17*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
19*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
20*0b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
21*0b57cec5SDimitry Andric 
22*0b57cec5SDimitry Andric using namespace clang;
23*0b57cec5SDimitry Andric using namespace ento;
24*0b57cec5SDimitry Andric using namespace ast_matchers;
25*0b57cec5SDimitry Andric 
26*0b57cec5SDimitry Andric namespace {
27*0b57cec5SDimitry Andric 
28*0b57cec5SDimitry Andric const char *WarnAtNode = "OSObjCast";
29*0b57cec5SDimitry Andric 
30*0b57cec5SDimitry Andric class OSObjectCStyleCastChecker : public Checker<check::ASTCodeBody> {
31*0b57cec5SDimitry Andric public:
32*0b57cec5SDimitry Andric   void checkASTCodeBody(const Decl *D,
33*0b57cec5SDimitry Andric                         AnalysisManager &AM,
34*0b57cec5SDimitry Andric                         BugReporter &BR) const;
35*0b57cec5SDimitry Andric };
36*0b57cec5SDimitry Andric 
37*0b57cec5SDimitry Andric static void emitDiagnostics(const BoundNodes &Nodes,
38*0b57cec5SDimitry Andric                             BugReporter &BR,
39*0b57cec5SDimitry Andric                             AnalysisDeclContext *ADC,
40*0b57cec5SDimitry Andric                             const OSObjectCStyleCastChecker *Checker) {
41*0b57cec5SDimitry Andric   const auto *CE = Nodes.getNodeAs<CastExpr>(WarnAtNode);
42*0b57cec5SDimitry Andric   assert(CE);
43*0b57cec5SDimitry Andric 
44*0b57cec5SDimitry Andric   std::string Diagnostics;
45*0b57cec5SDimitry Andric   llvm::raw_string_ostream OS(Diagnostics);
46*0b57cec5SDimitry Andric   OS << "C-style cast of OSObject. Use OSDynamicCast instead.";
47*0b57cec5SDimitry Andric 
48*0b57cec5SDimitry Andric   BR.EmitBasicReport(
49*0b57cec5SDimitry Andric     ADC->getDecl(),
50*0b57cec5SDimitry Andric     Checker,
51*0b57cec5SDimitry Andric     /*Name=*/"OSObject C-Style Cast",
52*0b57cec5SDimitry Andric     /*BugCategory=*/"Security",
53*0b57cec5SDimitry Andric     OS.str(),
54*0b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), ADC),
55*0b57cec5SDimitry Andric     CE->getSourceRange());
56*0b57cec5SDimitry Andric }
57*0b57cec5SDimitry Andric 
58*0b57cec5SDimitry Andric static auto hasTypePointingTo(DeclarationMatcher DeclM)
59*0b57cec5SDimitry Andric     -> decltype(hasType(pointerType())) {
60*0b57cec5SDimitry Andric   return hasType(pointerType(pointee(hasDeclaration(DeclM))));
61*0b57cec5SDimitry Andric }
62*0b57cec5SDimitry Andric 
63*0b57cec5SDimitry Andric void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D, AnalysisManager &AM,
64*0b57cec5SDimitry Andric                                                  BugReporter &BR) const {
65*0b57cec5SDimitry Andric 
66*0b57cec5SDimitry Andric   AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
67*0b57cec5SDimitry Andric 
68*0b57cec5SDimitry Andric   auto DynamicCastM = callExpr(callee(functionDecl(hasName("safeMetaCast"))));
69*0b57cec5SDimitry Andric 
70*0b57cec5SDimitry Andric   auto OSObjTypeM = hasTypePointingTo(cxxRecordDecl(isDerivedFrom("OSMetaClassBase")));
71*0b57cec5SDimitry Andric   auto OSObjSubclassM = hasTypePointingTo(
72*0b57cec5SDimitry Andric     cxxRecordDecl(isDerivedFrom("OSObject")));
73*0b57cec5SDimitry Andric 
74*0b57cec5SDimitry Andric   auto CastM = cStyleCastExpr(
75*0b57cec5SDimitry Andric           allOf(hasSourceExpression(allOf(OSObjTypeM, unless(DynamicCastM))),
76*0b57cec5SDimitry Andric           OSObjSubclassM)).bind(WarnAtNode);
77*0b57cec5SDimitry Andric 
78*0b57cec5SDimitry Andric   auto Matches = match(stmt(forEachDescendant(CastM)), *D->getBody(), AM.getASTContext());
79*0b57cec5SDimitry Andric   for (BoundNodes Match : Matches)
80*0b57cec5SDimitry Andric     emitDiagnostics(Match, BR, ADC, this);
81*0b57cec5SDimitry Andric }
82*0b57cec5SDimitry Andric }
83*0b57cec5SDimitry Andric 
84*0b57cec5SDimitry Andric void ento::registerOSObjectCStyleCast(CheckerManager &Mgr) {
85*0b57cec5SDimitry Andric   Mgr.registerChecker<OSObjectCStyleCastChecker>();
86*0b57cec5SDimitry Andric }
87*0b57cec5SDimitry Andric 
88*0b57cec5SDimitry Andric bool ento::shouldRegisterOSObjectCStyleCast(const LangOptions &LO) {
89*0b57cec5SDimitry Andric   return true;
90*0b57cec5SDimitry Andric }
91