xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp (revision e8d8bef961a50d4dc22501cde4fb9fb0be1b2532)
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 {
27*e8d8bef9SDimitry Andric static constexpr const char *const WarnAtNode = "WarnAtNode";
28*e8d8bef9SDimitry Andric static constexpr const char *const WarnRecordDecl = "WarnRecordDecl";
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric class OSObjectCStyleCastChecker : public Checker<check::ASTCodeBody> {
310b57cec5SDimitry Andric public:
32*e8d8bef9SDimitry Andric   void checkASTCodeBody(const Decl *D, AnalysisManager &AM,
330b57cec5SDimitry Andric                         BugReporter &BR) const;
340b57cec5SDimitry Andric };
35*e8d8bef9SDimitry Andric }
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric static void emitDiagnostics(const BoundNodes &Nodes,
380b57cec5SDimitry Andric                             BugReporter &BR,
390b57cec5SDimitry Andric                             AnalysisDeclContext *ADC,
400b57cec5SDimitry Andric                             const OSObjectCStyleCastChecker *Checker) {
410b57cec5SDimitry Andric   const auto *CE = Nodes.getNodeAs<CastExpr>(WarnAtNode);
42*e8d8bef9SDimitry Andric   const CXXRecordDecl *RD = Nodes.getNodeAs<CXXRecordDecl>(WarnRecordDecl);
43*e8d8bef9SDimitry Andric   assert(CE && RD);
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric   std::string Diagnostics;
460b57cec5SDimitry Andric   llvm::raw_string_ostream OS(Diagnostics);
47*e8d8bef9SDimitry Andric   OS << "C-style cast of an OSObject is prone to type confusion attacks; "
48*e8d8bef9SDimitry Andric      << "use 'OSRequiredCast' if the object is definitely of type '"
49*e8d8bef9SDimitry Andric      << RD->getNameAsString() << "', or 'OSDynamicCast' followed by "
50*e8d8bef9SDimitry Andric      << "a null check if unsure",
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric   BR.EmitBasicReport(
530b57cec5SDimitry Andric     ADC->getDecl(),
540b57cec5SDimitry Andric     Checker,
550b57cec5SDimitry Andric     /*Name=*/"OSObject C-Style Cast",
56*e8d8bef9SDimitry Andric     categories::SecurityError,
570b57cec5SDimitry Andric     OS.str(),
580b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), ADC),
590b57cec5SDimitry Andric     CE->getSourceRange());
600b57cec5SDimitry Andric }
610b57cec5SDimitry Andric 
625ffd83dbSDimitry Andric static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) {
630b57cec5SDimitry Andric   return hasType(pointerType(pointee(hasDeclaration(DeclM))));
640b57cec5SDimitry Andric }
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D, AnalysisManager &AM,
670b57cec5SDimitry Andric                                                  BugReporter &BR) const {
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric   AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
700b57cec5SDimitry Andric 
710b57cec5SDimitry Andric   auto DynamicCastM = callExpr(callee(functionDecl(hasName("safeMetaCast"))));
720b57cec5SDimitry Andric 
730b57cec5SDimitry Andric   auto OSObjTypeM = hasTypePointingTo(cxxRecordDecl(isDerivedFrom("OSMetaClassBase")));
740b57cec5SDimitry Andric   auto OSObjSubclassM = hasTypePointingTo(
75*e8d8bef9SDimitry Andric     cxxRecordDecl(isDerivedFrom("OSObject")).bind(WarnRecordDecl));
760b57cec5SDimitry Andric 
770b57cec5SDimitry Andric   auto CastM = cStyleCastExpr(
780b57cec5SDimitry Andric           allOf(hasSourceExpression(allOf(OSObjTypeM, unless(DynamicCastM))),
790b57cec5SDimitry Andric           OSObjSubclassM)).bind(WarnAtNode);
800b57cec5SDimitry Andric 
810b57cec5SDimitry Andric   auto Matches = match(stmt(forEachDescendant(CastM)), *D->getBody(), AM.getASTContext());
820b57cec5SDimitry Andric   for (BoundNodes Match : Matches)
830b57cec5SDimitry Andric     emitDiagnostics(Match, BR, ADC, this);
840b57cec5SDimitry Andric }
850b57cec5SDimitry Andric 
860b57cec5SDimitry Andric void ento::registerOSObjectCStyleCast(CheckerManager &Mgr) {
870b57cec5SDimitry Andric   Mgr.registerChecker<OSObjectCStyleCastChecker>();
880b57cec5SDimitry Andric }
890b57cec5SDimitry Andric 
905ffd83dbSDimitry Andric bool ento::shouldRegisterOSObjectCStyleCast(const CheckerManager &mgr) {
910b57cec5SDimitry Andric   return true;
920b57cec5SDimitry Andric }
93