Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[RawPtrRefMemberChecker] Add the support for union and pointers to unsafe pointers. #138042

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

rniwa
Copy link
Contributor

@rniwa rniwa commented Apr 30, 2025

This PR adds support for detecting unsafe union members and pointers to unsafe pointers (e.g. T** where T* is an unsafe pointer type).

…safe pointers.

This PR adds support for detecting unsafe union members and pointers to unsafe pointers
(e.g. T** where T* is an unsafe pointer type).
@rniwa rniwa requested a review from t-rasmud April 30, 2025 22:14
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer labels Apr 30, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 30, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Ryosuke Niwa (rniwa)

Changes

This PR adds support for detecting unsafe union members and pointers to unsafe pointers (e.g. T** where T* is an unsafe pointer type).


Full diff: https://github.com/llvm/llvm-project/pull/138042.diff

5 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp (+28-14)
  • (modified) clang/test/Analysis/Checkers/WebKit/unchecked-members.cpp (+24-2)
  • (modified) clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp (+26-4)
  • (modified) clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm (+27)
  • (modified) clang/test/Analysis/Checkers/WebKit/unretained-members.mm (+31-3)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
index e8992f58273bb..d1a4f203d7a49 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
@@ -85,21 +85,31 @@ class RawPtrRefMemberChecker
     if (shouldSkipDecl(RD))
       return;
 
-    for (auto *Member : RD->fields()) {
-      auto QT = Member->getType();
-      const Type *MemberType = QT.getTypePtrOrNull();
-      if (!MemberType)
-        continue;
+    for (auto *Member : RD->fields())
+      visitMember(Member, RD);
+  }
 
-      auto IsUnsafePtr = isUnsafePtr(QT);
-      if (!IsUnsafePtr || !*IsUnsafePtr)
-        continue;
+  void visitMember(const FieldDecl *Member, const RecordDecl *RD) const {
+    auto QT = Member->getType();
+    const Type *MemberType = QT.getTypePtrOrNull();
 
-      if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl())
-        reportBug(Member, MemberType, MemberCXXRD, RD);
-      else if (auto *ObjCDecl = getObjCDecl(MemberType))
-        reportBug(Member, MemberType, ObjCDecl, RD);
+    while (MemberType) {
+      auto IsUnsafePtr = isUnsafePtr(QT);
+      if (IsUnsafePtr && *IsUnsafePtr)
+        break;
+      if (!MemberType->isPointerType())
+        return;
+      QT = MemberType->getPointeeType();
+      MemberType = QT.getTypePtrOrNull();
     }
+
+    if (!MemberType)
+      return;
+
+    if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl())
+      reportBug(Member, MemberType, MemberCXXRD, RD);
+    else if (auto *ObjCDecl = getObjCDecl(MemberType))
+      reportBug(Member, MemberType, ObjCDecl, RD);
   }
 
   ObjCInterfaceDecl *getObjCDecl(const Type *TypePtr) const {
@@ -192,7 +202,8 @@ class RawPtrRefMemberChecker
 
     const auto Kind = RD->getTagKind();
     // FIMXE: Should we check union members too?
-    if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
+    if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class &&
+        Kind != TagTypeKind::Union)
       return true;
 
     // Ignore CXXRecords that come from system headers.
@@ -229,7 +240,10 @@ class RawPtrRefMemberChecker
     printQuotedName(Os, Member);
     Os << " in ";
     printQuotedQualifiedName(Os, ClassCXXRD);
-    Os << " is a ";
+    if (Member->getType().getTypePtrOrNull() == MemberType)
+      Os << " is a ";
+    else
+      Os << " contains a ";
     if (printPointer(Os, MemberType) == PrintDeclKind::Pointer) {
       auto Typedef = MemberType->getAs<TypedefType>();
       assert(Typedef);
diff --git a/clang/test/Analysis/Checkers/WebKit/unchecked-members.cpp b/clang/test/Analysis/Checkers/WebKit/unchecked-members.cpp
index 048ffbffcdefb..3fe15d88ff312 100644
--- a/clang/test/Analysis/Checkers/WebKit/unchecked-members.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/unchecked-members.cpp
@@ -34,10 +34,11 @@ namespace members {
 
 } // namespace members
 
-namespace ignore_unions {
+namespace unions {
 
   union Foo {
     CheckedObj* a;
+    // expected-warning@-1{{Member variable 'a' in 'unions::Foo' is a raw pointer to CheckedPtr capable type 'CheckedObj'}}
     CheckedPtr<CheckedObj> c;
     CheckedRef<CheckedObj> d;
   };
@@ -45,11 +46,12 @@ namespace ignore_unions {
   template<class T>
   union FooTmpl {
     T* a;
+    // expected-warning@-1{{Member variable 'a' in 'unions::FooTmpl<CheckedObj>' is a raw pointer to CheckedPtr capable type 'CheckedObj'}}
   };
 
   void forceTmplToInstantiate(FooTmpl<CheckedObj>) { }
 
-} // namespace ignore_unions
+} // namespace unions
 
 namespace checked_ptr_ref_ptr_capable {
 
@@ -59,3 +61,23 @@ namespace checked_ptr_ref_ptr_capable {
   }
 
 } // checked_ptr_ref_ptr_capable
+
+namespace ptr_to_ptr_to_checked_ptr_capable {
+
+  struct List {
+    CheckedObj** elements;
+    // expected-warning@-1{{Member variable 'elements' in 'ptr_to_ptr_to_checked_ptr_capable::List' contains a raw pointer to CheckedPtr capable type 'CheckedObj'}}
+  };
+
+  template <typename T>
+  struct TemplateList {
+    T** elements;
+    // expected-warning@-1{{Member variable 'elements' in 'ptr_to_ptr_to_checked_ptr_capable::TemplateList<CheckedObj>' contains a raw pointer to CheckedPtr capable type 'CheckedObj'}}
+  };
+  TemplateList<CheckedObj> list;
+
+  struct SafeList {
+    CheckedPtr<CheckedObj>* elements;
+  };
+
+} // namespace ptr_to_ptr_to_checked_ptr_capable
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp
index 130777a9a5fee..b8c443cda4f8e 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-members.cpp
@@ -36,20 +36,22 @@ namespace members {
   };
 } // members
 
-namespace ignore_unions {
+namespace unions {
   union Foo {
     RefCountable* a;
+    // expected-warning@-1{{Member variable 'a' in 'unions::Foo' is a raw pointer to ref-countable type 'RefCountable'}}
     RefPtr<RefCountable> b;
     Ref<RefCountable> c;
   };
 
   template<class T>
-  union RefPtr {
+  union FooTmpl {
     T* a;
+    // expected-warning@-1{{Member variable 'a' in 'unions::FooTmpl<RefCountable>' is a raw pointer to ref-countable type 'RefCountable'}}
   };
 
-  void forceTmplToInstantiate(RefPtr<RefCountable>) {}
-} // ignore_unions
+  void forceTmplToInstantiate(FooTmpl<RefCountable>) {}
+} // unions
 
 namespace ignore_system_header {
 
@@ -77,3 +79,23 @@ namespace checked_ptr_ref_ptr_capable {
   }
 
 } // checked_ptr_ref_ptr_capable
+
+namespace ptr_to_ptr_to_ref_counted {
+
+  struct List {
+    RefCountable** elements;
+    // expected-warning@-1{{Member variable 'elements' in 'ptr_to_ptr_to_ref_counted::List' contains a raw pointer to ref-countable type 'RefCountable'}}
+  };
+
+  template <typename T>
+  struct TemplateList {
+    T** elements;
+    // expected-warning@-1{{Member variable 'elements' in 'ptr_to_ptr_to_ref_counted::TemplateList<RefCountable>' contains a raw pointer to ref-countable type 'RefCountable'}}
+  };
+  TemplateList<RefCountable> list;
+
+  struct SafeList {
+    RefPtr<RefCountable>* elements;
+  };
+
+} // namespace ptr_to_ptr_to_ref_counted
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm b/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
index 9820c875b87c0..3491bc93ed98a 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm
@@ -21,6 +21,12 @@
     CFMutableArrayRef e = nullptr;
 // expected-warning@-1{{Member variable 'e' in 'members::Foo' is a retainable type 'CFMutableArrayRef'}}
   };
+  
+  union FooUnion {
+    SomeObj* a;
+    CFMutableArrayRef b;
+    // expected-warning@-1{{Member variable 'b' in 'members::FooUnion' is a retainable type 'CFMutableArrayRef'}}
+  };
 
   template<class T, class S>
   struct FooTmpl {
@@ -37,3 +43,24 @@ void forceTmplToInstantiate(FooTmpl<SomeObj, CFMutableArrayRef>) {}
   };
 
 }
+
+namespace ptr_to_ptr_to_retained {
+
+  struct List {
+    CFMutableArrayRef* elements2;
+    // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::List' contains a retainable type 'CFMutableArrayRef'}}
+  };
+
+  template <typename T, typename S>
+  struct TemplateList {
+    S* elements2;
+    // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::TemplateList<SomeObj, __CFArray *>' contains a raw pointer to retainable type '__CFArray'}}
+  };
+  TemplateList<SomeObj, CFMutableArrayRef> list;
+
+  struct SafeList {
+    RetainPtr<SomeObj>* elements1;
+    RetainPtr<CFMutableArrayRef>* elements2;
+  };
+
+} // namespace ptr_to_ptr_to_retained
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members.mm b/clang/test/Analysis/Checkers/WebKit/unretained-members.mm
index fff1f8ede091b..0cb4c4ac0f6a0 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-members.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-members.mm
@@ -47,21 +47,49 @@ void forceTmplToInstantiate(FooTmpl<SomeObj, CFMutableArrayRef>) {}
 
 }
 
-namespace ignore_unions {
+namespace unions {
   union Foo {
     SomeObj* a;
+    // expected-warning@-1{{Member variable 'a' in 'unions::Foo' is a raw pointer to retainable type 'SomeObj'}}
     RetainPtr<SomeObj> b;
     CFMutableArrayRef c;
+    // expected-warning@-1{{Member variable 'c' in 'unions::Foo' is a retainable type 'CFMutableArrayRef'}}
   };
 
   template<class T>
-  union RefPtr {
+  union FooTempl {
     T* a;
+    // expected-warning@-1{{Member variable 'a' in 'unions::FooTempl<SomeObj>' is a raw pointer to retainable type 'SomeObj'}}
   };
 
-  void forceTmplToInstantiate(RefPtr<SomeObj>) {}
+  void forceTmplToInstantiate(FooTempl<SomeObj>) {}
 }
 
+namespace ptr_to_ptr_to_retained {
+
+  struct List {
+    SomeObj** elements1;
+    // expected-warning@-1{{Member variable 'elements1' in 'ptr_to_ptr_to_retained::List' contains a raw pointer to retainable type 'SomeObj'}}
+    CFMutableArrayRef* elements2;
+    // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::List' contains a retainable type 'CFMutableArrayRef'}}
+  };
+
+  template <typename T, typename S>
+  struct TemplateList {
+    T** elements1;
+    // expected-warning@-1{{Member variable 'elements1' in 'ptr_to_ptr_to_retained::TemplateList<SomeObj, __CFArray *>' contains a raw pointer to retainable type 'SomeObj'}}
+    S* elements2;
+    // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::TemplateList<SomeObj, __CFArray *>' contains a raw pointer to retainable type '__CFArray'}}
+  };
+  TemplateList<SomeObj, CFMutableArrayRef> list;
+
+  struct SafeList {
+    RetainPtr<SomeObj>* elements1;
+    RetainPtr<CFMutableArrayRef>* elements2;
+  };
+
+} // namespace ptr_to_ptr_to_retained
+
 @interface AnotherObject : NSObject {
   NSString *ns_string;
   // expected-warning@-1{{Instance variable 'ns_string' in 'AnotherObject' is a raw pointer to retainable type 'NSString'; member variables must be a RetainPtr}}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants