Skip to content

Commit

Permalink
[analyzer] Opt-in C Style Cast Checker for OSObject pointers
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D57261

llvm-svn: 353566
  • Loading branch information
George Karpenkov committed Feb 8, 2019
1 parent fcb63c4 commit 2add627
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 0 deletions.
8 changes: 8 additions & 0 deletions clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,14 @@ def GCDAntipattern : Checker<"GCDAntipattern">,
Documentation<NotDocumented>;
} // end "optin.performance"

let ParentPackage = OSXOptIn in {

def OSObjectCStyleCast : Checker<"OSObjectCStyleCast">,
HelpText<"Checker for C-style casts of OSObjects">,
Documentation<NotDocumented>;

} // end "optin.osx"

let ParentPackage = CocoaAlpha in {

def IvarInvalidationModeling : Checker<"IvarInvalidationModeling">,
Expand Down
1 change: 1 addition & 0 deletions clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ add_clang_library(clangStaticAnalyzerCheckers
ObjCSelfInitChecker.cpp
ObjCSuperDeallocChecker.cpp
ObjCUnusedIVarsChecker.cpp
OSObjectCStyleCast.cpp
PaddingChecker.cpp
PointerArithChecker.cpp
PointerSubChecker.cpp
Expand Down
90 changes: 90 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//===- OSObjectCStyleCast.cpp ------------------------------------*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines OSObjectCStyleCast checker, which checks for C-style casts
// of OSObjects. Such casts almost always indicate a code smell,
// as an explicit static or dynamic cast should be used instead.
//===----------------------------------------------------------------------===//

#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "llvm/Support/Debug.h"

using namespace clang;
using namespace ento;
using namespace ast_matchers;

namespace {

const char *WarnAtNode = "OSObjCast";

class OSObjectCStyleCastChecker : public Checker<check::ASTCodeBody> {
public:
void checkASTCodeBody(const Decl *D,
AnalysisManager &AM,
BugReporter &BR) const;
};

static void emitDiagnostics(const BoundNodes &Nodes,
BugReporter &BR,
AnalysisDeclContext *ADC,
const OSObjectCStyleCastChecker *Checker) {
const auto *CE = Nodes.getNodeAs<CastExpr>(WarnAtNode);
assert(CE);

std::string Diagnostics;
llvm::raw_string_ostream OS(Diagnostics);
OS << "C-style cast of OSObject. Use OSDynamicCast instead.";

BR.EmitBasicReport(
ADC->getDecl(),
Checker,
/*Name=*/"OSObject C-Style Cast",
/*Category=*/"Security",
OS.str(),
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), ADC),
CE->getSourceRange());
}

static auto hasTypePointingTo(DeclarationMatcher DeclM)
-> decltype(hasType(pointerType())) {
return hasType(pointerType(pointee(hasDeclaration(DeclM))));
}

void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D, AnalysisManager &AM,
BugReporter &BR) const {

AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);

auto DynamicCastM = callExpr(callee(functionDecl(hasName("safeMetaCast"))));

auto OSObjTypeM = hasTypePointingTo(cxxRecordDecl(isDerivedFrom("OSMetaClassBase")));
auto OSObjSubclassM = hasTypePointingTo(
cxxRecordDecl(isDerivedFrom("OSObject")));

auto CastM = cStyleCastExpr(
allOf(hasSourceExpression(allOf(OSObjTypeM, unless(DynamicCastM))),
OSObjSubclassM)).bind(WarnAtNode);

auto Matches = match(stmt(forEachDescendant(CastM)), *D->getBody(), AM.getASTContext());
for (BoundNodes Match : Matches)
emitDiagnostics(Match, BR, ADC, this);
}
}

void ento::registerOSObjectCStyleCast(CheckerManager &Mgr) {
Mgr.registerChecker<OSObjectCStyleCastChecker>();
}

bool ento::shouldRegisterOSObjectCStyleCast(const LangOptions &LO) {
return true;
}
39 changes: 39 additions & 0 deletions clang/test/Analysis/osobjectcstylecastchecker_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=optin.osx.OSObjectCStyleCast %s -verify
#include "os_object_base.h"

struct OSArray : public OSObject {
unsigned getCount();
};

struct A {
int x;
};
struct B : public A {
unsigned getCount();
};

unsigned warn_on_explicit_downcast(OSObject * obj) {
OSArray *a = (OSArray *) obj; // expected-warning{{C-style cast of OSObject. Use OSDynamicCast instead}}
return a->getCount();
}

void no_warn_on_upcast(OSArray *arr) {
OSObject *obj = (OSObject *) arr;
obj->retain();
obj->release();
}

unsigned no_warn_on_dynamic_cast(OSObject *obj) {
OSArray *a = OSDynamicCast(OSArray, obj);
return a->getCount();
}

unsigned long no_warn_on_primitive_conversion(OSArray *arr) {
return (unsigned long) arr;
}

unsigned no_warn_on_other_type_cast(A *a) {
B *b = (B *) a;
return b->getCount();
}

0 comments on commit 2add627

Please sign in to comment.