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
