Author: Rashmi Mudduluru
Date: 2026-06-16T12:54:15-07:00
New Revision: 2dfdd094d084e258934cc1e89be8a09fb59cdfa1

URL: 
https://github.com/llvm/llvm-project/commit/2dfdd094d084e258934cc1e89be8a09fb59cdfa1
DIFF: 
https://github.com/llvm/llvm-project/commit/2dfdd094d084e258934cc1e89be8a09fb59cdfa1.diff

LOG: [clang][StaticAnalyzer] Reduce MallocSizeofChecker false positives for 
layout-compatible types (#200253)

When one operand is a record type and the other is a non-record type,
treat them as compatible if they share the same size and the record's
alignment satisfies the scalar's alignment. This suppresses warnings for
patterns like `malloc(sizeof(std::atomic<int32_t>))` assigned to an
`int32_t *` (or a wrapper struct with an identical layout), while still
flagging genuinely mismatched types such as `long` vs `double` or
unrelated struct pairs.

rdar://177553628

---------

Co-authored-by: Claude Sonnet 4.6 <[email protected]>

Added: 
    clang/test/Analysis/malloc-sizeof-fp.cpp

Modified: 
    clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
index f84d45214f6fe..5d4f15246c3fb 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
@@ -149,12 +149,39 @@ static bool typesCompatible(ASTContext &C, QualType A, 
QualType B) {
     if (A.getTypePtr() == B.getTypePtr())
       return true;
 
-    if (const PointerType *ptrA = A->getAs<PointerType>())
-      if (const PointerType *ptrB = B->getAs<PointerType>()) {
-        A = ptrA->getPointeeType();
-        B = ptrB->getPointeeType();
-        continue;
+    const PointerType *ptrA = A->getAs<PointerType>();
+    const PointerType *ptrB = B->getAs<PointerType>();
+
+    // When neither type is a pointer and exactly one is a record type, check
+    // target-specific size and alignment. This avoids false positives for
+    // types that wrap another type with the same layout (e.g.
+    // std::atomic<int32_t> vs int32_t, or struct{int32_t x;} vs int32_t),
+    // while preserving warnings for unrelated types that happen to share a
+    // size (e.g. long vs double, struct A vs struct B).
+    if (!ptrA && !ptrB && (A->isRecordType() != B->isRecordType()) &&
+        !A->isIncompleteType() && !B->isIncompleteType()) {
+      const RecordType *RecTy = A->getAs<RecordType>();
+      QualType Scalar = B;
+      if (!RecTy) {
+        RecTy = B->getAs<RecordType>();
+        Scalar = A;
+      }
+      if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(RecTy->getDecl())) 
{
+        if (RD->isStandardLayout()) {
+          const CXXRecordDecl *Base = RD->getStandardLayoutBaseWithFields();
+          auto F = Base->field_begin();
+          if (F != Base->field_end() && std::next(F) == Base->field_end() &&
+              C.getCanonicalType((*F)->getType()) == Scalar)
+            return true;
+        }
       }
+    }
+
+    if (ptrA && ptrB) {
+      A = ptrA->getPointeeType();
+      B = ptrB->getPointeeType();
+      continue;
+    }
 
     break;
   }

diff  --git a/clang/test/Analysis/malloc-sizeof-fp.cpp 
b/clang/test/Analysis/malloc-sizeof-fp.cpp
new file mode 100644
index 0000000000000..64590796de4ce
--- /dev/null
+++ b/clang/test/Analysis/malloc-sizeof-fp.cpp
@@ -0,0 +1,61 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=unix.MallocSizeof -std=c++11 
-verify %s
+
+// Verify no false positives for layout-compatible types: a record type that
+// wraps a scalar with identical size and alignment (e.g. std::atomic<int>
+// wrapping int, or struct { int c; } vs int).
+
+typedef int int32_t;
+using size_t = unsigned long long;
+void *malloc(size_t size);
+void free(void *ptr);
+
+namespace std {
+// Minimal atomic stub with the same size/alignment as T.
+template <typename T>
+struct atomic {
+  T _value;
+};
+} // namespace std
+
+typedef std::atomic<int32_t> u_atomic_int32_t;
+
+struct s_int {
+  int32_t c;
+};
+
+template <typename T>
+void work() {
+  u_atomic_int32_t *p1 = (u_atomic_int32_t *)malloc(sizeof(int32_t));
+  free(p1);
+
+  T *p2 = (T *)malloc(sizeof(int32_t));
+  free(p2);
+}
+
+int main() {
+  work<u_atomic_int32_t>();
+  work<int>();
+  work<s_int>();
+  return 0;
+}
+
+void test_no_false_negatives() {
+  // Unrelated struct with the same size
+  struct Color { float r; };
+  int *p = (int *)malloc(sizeof(Color));
+  // expected-warning@-1{{Result of 'malloc' is converted to a pointer of type 
'int', which is incompatible with sizeof operand type 'Color'}}
+
+  // Multi-field struct with the same size and alignment as the scalar
+  struct Pair { int a; int b; };  // 8 bytes, align 4
+  Pair *pr = (Pair *)malloc(sizeof(long));
+  // expected-warning@-1{{Result of 'malloc' is converted to a pointer of type 
'Pair', which is incompatible with sizeof operand type 'long'}}
+
+  struct Status { int code; };
+  float *f = (float *)malloc(sizeof(Status));
+  // expected-warning@-1{{Result of 'malloc' is converted to a pointer of type 
'float', which is incompatible with sizeof operand type 'Status'}}
+
+  // Pointer-sized struct vs. pointer-sized scalar
+  struct Handle { long opaque; };
+  double *d = (double *)malloc(sizeof(Handle));
+  // expected-warning@-1{{Result of 'malloc' is converted to a pointer of type 
'double', which is incompatible with sizeof operand type 'Handle'}}
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to