clang 20.0.0git
OSObjectCStyleCast.cpp
Go to the documentation of this file.
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
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} // namespace
36
37namespace clang {
38namespace ast_matchers {
39AST_MATCHER_P(StringLiteral, mentionsBoundType, std::string, BindingID) {
40 return Builder->removeBindings([this, &Node](const BoundNodesMap &Nodes) {
41 const auto &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_matchers
49} // end namespace clang
50
51static void emitDiagnostics(const BoundNodes &Nodes,
52 BugReporter &BR,
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
67 ADC->getDecl(),
68 Checker,
69 /*Name=*/"OSObject C-Style Cast",
71 OS.str(),
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,
82 BugReporter &BR) const {
83
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 the
97 // 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 =
107 allOf(OSObjSubclassM,
108 hasSourceExpression(
109 allOf(OSObjTypeM,
110 unless(anyOf(DynamicCastM, AllocClassWithNameM))))))
111 .bind(WarnAtNode);
112
113 auto Matches =
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}
BoundNodesTreeBuilder Nodes
DynTypedNode Node
#define AST_MATCHER_P(Type, DefineMatcher, ParamType, Param)
AST_MATCHER_P(Type, DefineMatcher, ParamType, Param) { ... } defines a single-parameter function name...
const Decl * D
static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM)
static void emitDiagnostics(const BoundNodes &Nodes, BugReporter &BR, AnalysisDeclContext *ADC, const OSObjectCStyleCastChecker *Checker)
AnalysisDeclContext contains the context data for the function, method or block under analysis.
const Decl * getDecl() const
Represents a C++ struct/union/class.
Definition: DeclCXX.h:258
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition: Expr.h:3498
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
Definition: DeclBase.h:1077
This represents a decl that may have a name.
Definition: Decl.h:249
StringLiteral - This represents a string literal expression, e.g.
Definition: Expr.h:1778
Maps string IDs to AST nodes matched by parts of a matcher.
Definition: ASTMatchers.h:109
ASTContext & getASTContext() override
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:585
const SourceManager & getSourceManager()
Definition: BugReporter.h:623
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges=std::nullopt, ArrayRef< FixItHint > Fixits=std::nullopt)
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
const internal::VariadicDynCastAllOfMatcher< Stmt, CStyleCastExpr > cStyleCastExpr
Matches a C-style cast expression.
const internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
internal::Matcher< Decl > DeclarationMatcher
Types of matchers for the top-level classes in the AST class hierarchy.
Definition: ASTMatchers.h:143
const internal::VariadicDynCastAllOfMatcher< Stmt, StringLiteral > stringLiteral
Matches string literals (also matches wide string literals).
const AstTypeMatcher< PointerType > pointerType
Matches pointer types, but does not match Objective-C object pointer types.
internal::Matcher< NamedDecl > hasName(StringRef Name)
Matches NamedDecl nodes that have the specified name.
Definition: ASTMatchers.h:3079
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
const internal::ArgumentAdaptingMatcherFunc< internal::ForEachDescendantMatcher > forEachDescendant
Matches AST nodes that have descendant AST nodes that match the provided matcher.
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> allOf
Matches if all given matchers match.
const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl
Matches function declarations.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXRecordDecl > cxxRecordDecl
Matches C++ class declarations.
internal::PolymorphicMatcher< internal::HasDeclarationMatcher, void(internal::HasDeclarationSupportedTypes), internal::Matcher< Decl > > hasDeclaration(const internal::Matcher< Decl > &InnerMatcher)
Matches a node if the declaration associated with that node matches the given matcher.
Definition: ASTMatchers.h:3653
const internal::VariadicAllOfMatcher< Stmt > stmt
Matches statements.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf
Matches if any of the given matchers matches.
const char *const SecurityError
The JSON file list parser is used to communicate input to InstallAPI.