1351280Sdim//===- OSObjectCStyleCast.cpp ------------------------------------*- C++ -*-==// 2351280Sdim// 3351280Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4351280Sdim// See https://llvm.org/LICENSE.txt for license information. 5351280Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6351280Sdim// 7351280Sdim//===----------------------------------------------------------------------===// 8351280Sdim// 9351280Sdim// This file defines OSObjectCStyleCast checker, which checks for C-style casts 10351280Sdim// of OSObjects. Such casts almost always indicate a code smell, 11351280Sdim// as an explicit static or dynamic cast should be used instead. 12351280Sdim//===----------------------------------------------------------------------===// 13351280Sdim 14351280Sdim#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15351280Sdim#include "clang/ASTMatchers/ASTMatchFinder.h" 16351280Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 17351280Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 18351280Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 19351280Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 20351280Sdim#include "llvm/Support/Debug.h" 21351280Sdim 22351280Sdimusing namespace clang; 23351280Sdimusing namespace ento; 24351280Sdimusing namespace ast_matchers; 25351280Sdim 26351280Sdimnamespace { 27351280Sdim 28351280Sdimconst char *WarnAtNode = "OSObjCast"; 29351280Sdim 30351280Sdimclass OSObjectCStyleCastChecker : public Checker<check::ASTCodeBody> { 31351280Sdimpublic: 32351280Sdim void checkASTCodeBody(const Decl *D, 33351280Sdim AnalysisManager &AM, 34351280Sdim BugReporter &BR) const; 35351280Sdim}; 36351280Sdim 37351280Sdimstatic void emitDiagnostics(const BoundNodes &Nodes, 38351280Sdim BugReporter &BR, 39351280Sdim AnalysisDeclContext *ADC, 40351280Sdim const OSObjectCStyleCastChecker *Checker) { 41351280Sdim const auto *CE = Nodes.getNodeAs<CastExpr>(WarnAtNode); 42351280Sdim assert(CE); 43351280Sdim 44351280Sdim std::string Diagnostics; 45351280Sdim llvm::raw_string_ostream OS(Diagnostics); 46351280Sdim OS << "C-style cast of OSObject. Use OSDynamicCast instead."; 47351280Sdim 48351280Sdim BR.EmitBasicReport( 49351280Sdim ADC->getDecl(), 50351280Sdim Checker, 51351280Sdim /*Name=*/"OSObject C-Style Cast", 52351280Sdim /*BugCategory=*/"Security", 53351280Sdim OS.str(), 54351280Sdim PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), ADC), 55351280Sdim CE->getSourceRange()); 56351280Sdim} 57351280Sdim 58351280Sdimstatic auto hasTypePointingTo(DeclarationMatcher DeclM) 59351280Sdim -> decltype(hasType(pointerType())) { 60351280Sdim return hasType(pointerType(pointee(hasDeclaration(DeclM)))); 61351280Sdim} 62351280Sdim 63351280Sdimvoid OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D, AnalysisManager &AM, 64351280Sdim BugReporter &BR) const { 65351280Sdim 66351280Sdim AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D); 67351280Sdim 68351280Sdim auto DynamicCastM = callExpr(callee(functionDecl(hasName("safeMetaCast")))); 69351280Sdim 70351280Sdim auto OSObjTypeM = hasTypePointingTo(cxxRecordDecl(isDerivedFrom("OSMetaClassBase"))); 71351280Sdim auto OSObjSubclassM = hasTypePointingTo( 72351280Sdim cxxRecordDecl(isDerivedFrom("OSObject"))); 73351280Sdim 74351280Sdim auto CastM = cStyleCastExpr( 75351280Sdim allOf(hasSourceExpression(allOf(OSObjTypeM, unless(DynamicCastM))), 76351280Sdim OSObjSubclassM)).bind(WarnAtNode); 77351280Sdim 78351280Sdim auto Matches = match(stmt(forEachDescendant(CastM)), *D->getBody(), AM.getASTContext()); 79351280Sdim for (BoundNodes Match : Matches) 80351280Sdim emitDiagnostics(Match, BR, ADC, this); 81351280Sdim} 82351280Sdim} 83351280Sdim 84351280Sdimvoid ento::registerOSObjectCStyleCast(CheckerManager &Mgr) { 85351280Sdim Mgr.registerChecker<OSObjectCStyleCastChecker>(); 86351280Sdim} 87351280Sdim 88351280Sdimbool ento::shouldRegisterOSObjectCStyleCast(const LangOptions &LO) { 89351280Sdim return true; 90351280Sdim} 91