[clang] 9037730 - [analyzer] Support allocClassWithName in OSObjectCStyleCast checker

Valeriy Savchenko via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 30 05:58:21 PDT 2021


Author: Valeriy Savchenko
Date: 2021-03-30T15:58:06+03:00
New Revision: 90377308de6cac8239bc1a1dcd32b57b9ec91444

URL: https://github.com/llvm/llvm-project/commit/90377308de6cac8239bc1a1dcd32b57b9ec91444
DIFF: https://github.com/llvm/llvm-project/commit/90377308de6cac8239bc1a1dcd32b57b9ec91444.diff

LOG: [analyzer] Support allocClassWithName in OSObjectCStyleCast checker

`allocClassWithName` allocates an object with the given type.
The type is actually provided as a string argument (type's name).
This creates a possibility for not particularly useful warnings
from the analyzer.

In order to combat with those, this patch checks for casts of the
`allocClassWithName` results to types mentioned directly as its
argument.  All other uses of this method should be reasoned about
as before.

rdar://72165694

Differential Revision: https://reviews.llvm.org/D99500

Added: 
    

Modified: 
    clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp
    clang/test/Analysis/os_object_base.h
    clang/test/Analysis/osobjectcstylecastchecker_test.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp b/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp
index 270b66dab020..0a8379d9ab99 100644
--- a/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp
@@ -32,7 +32,21 @@ class OSObjectCStyleCastChecker : public Checker<check::ASTCodeBody> {
   void checkASTCodeBody(const Decl *D, AnalysisManager &AM,
                         BugReporter &BR) const;
 };
+} // namespace
+
+namespace clang {
+namespace ast_matchers {
+AST_MATCHER_P(StringLiteral, mentionsBoundType, std::string, BindingID) {
+  return Builder->removeBindings([this, &Node](const BoundNodesMap &Nodes) {
+    const auto &BN = Nodes.getNode(this->BindingID);
+    if (const auto *ND = BN.get<NamedDecl>()) {
+      return ND->getName() != Node.getString();
+    }
+    return true;
+  });
 }
+} // end namespace ast_matchers
+} // end namespace clang
 
 static void emitDiagnostics(const BoundNodes &Nodes,
                             BugReporter &BR,
@@ -63,22 +77,41 @@ static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) {
   return hasType(pointerType(pointee(hasDeclaration(DeclM))));
 }
 
-void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D, AnalysisManager &AM,
+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")));
+  // 'allocClassWithName' allocates an object with the given type.
+  // The type is actually provided as a string argument (type's name).
+  // This makes the following pattern possible:
+  //
+  // Foo *object = (Foo *)allocClassWithName("Foo");
+  //
+  // While OSRequiredCast can be used here, it is still not a useful warning.
+  auto AllocClassWithNameM = callExpr(
+      callee(functionDecl(hasName("allocClassWithName"))),
+      // Here we want to make sure that the string argument matches the
+      // type in the cast expression.
+      hasArgument(0, stringLiteral(mentionsBoundType(WarnRecordDecl))));
+
+  auto OSObjTypeM =
+      hasTypePointingTo(cxxRecordDecl(isDerivedFrom("OSMetaClassBase")));
   auto OSObjSubclassM = hasTypePointingTo(
-    cxxRecordDecl(isDerivedFrom("OSObject")).bind(WarnRecordDecl));
-
-  auto CastM = cStyleCastExpr(
-          allOf(hasSourceExpression(allOf(OSObjTypeM, unless(DynamicCastM))),
-          OSObjSubclassM)).bind(WarnAtNode);
-
-  auto Matches = match(stmt(forEachDescendant(CastM)), *D->getBody(), AM.getASTContext());
+      cxxRecordDecl(isDerivedFrom("OSObject")).bind(WarnRecordDecl));
+
+  auto CastM =
+      cStyleCastExpr(
+          allOf(OSObjSubclassM,
+                hasSourceExpression(
+                    allOf(OSObjTypeM,
+                          unless(anyOf(DynamicCastM, AllocClassWithNameM))))))
+          .bind(WarnAtNode);
+
+  auto Matches =
+      match(stmt(forEachDescendant(CastM)), *D->getBody(), AM.getASTContext());
   for (BoundNodes Match : Matches)
     emitDiagnostics(Match, BR, ADC, this);
 }

diff  --git a/clang/test/Analysis/os_object_base.h b/clang/test/Analysis/os_object_base.h
index 4698185f2b3c..c3d5d6271d48 100644
--- a/clang/test/Analysis/os_object_base.h
+++ b/clang/test/Analysis/os_object_base.h
@@ -66,6 +66,7 @@ struct OSObject : public OSMetaClassBase {
 
 struct OSMetaClass : public OSMetaClassBase {
   virtual OSObject * alloc() const;
+  static OSObject * allocClassWithName(const char * name);
   virtual ~OSMetaClass(){}
 };
 

diff  --git a/clang/test/Analysis/osobjectcstylecastchecker_test.cpp b/clang/test/Analysis/osobjectcstylecastchecker_test.cpp
index a5ebb2823a92..aa43a18e6346 100644
--- a/clang/test/Analysis/osobjectcstylecastchecker_test.cpp
+++ b/clang/test/Analysis/osobjectcstylecastchecker_test.cpp
@@ -37,3 +37,12 @@ unsigned no_warn_on_other_type_cast(A *a) {
   return b->getCount();
 }
 
+unsigned no_warn_alloc_class_with_name() {
+  OSArray *a = (OSArray *)OSMetaClass::allocClassWithName("OSArray"); // no warning
+  return a->getCount();
+}
+
+unsigned warn_alloc_class_with_name() {
+  OSArray *a = (OSArray *)OSMetaClass::allocClassWithName("OSObject"); // expected-warning{{C-style cast of an OSObject is prone to type confusion attacks; use 'OSRequiredCast' if the object is definitely of type 'OSArray', or 'OSDynamicCast' followed by a null check if unsure}}
+  return a->getCount();
+}


        


More information about the cfe-commits mailing list