https://github.com/ojhunt created https://github.com/llvm/llvm-project/pull/170577
These warnings are triggered for zeroing initializers of non-trivially initializable and non-trivially copyable types. This results in significant numbers of warnings in idiomatic object initialization code, where memset and similar are used to ensure no stale data is present in fields or padding bytes. Addresses #156996 >From 4707b4ced1b96a19da8aac9a1d14a9438eed9599 Mon Sep 17 00:00:00 2001 From: Oliver Hunt <[email protected]> Date: Wed, 3 Dec 2025 15:59:48 -0800 Subject: [PATCH] [clang] warn_cstruct_memaccess and warn_cxxstruct_memaccess are too agressive about initializers These warnings are triggered for zeroing initializers of non-trivially initializable and non-trivially copyable types. This results in significant numbers of warnings in idiomatic object initialization code, where memset and similar are used to ensure no stale data is present in fields or padding bytes. Addresses #156996 --- clang/lib/Sema/SemaChecking.cpp | 14 ++++-- ...warn-nontrivial-struct-memaccess-ptrauth.c | 45 +++++++++++------ clang/test/SemaCXX/warn-memaccess.cpp | 49 +++++++++++++++++-- .../warn-nontrivial-struct-memaccess.m | 39 ++++++++++++--- 4 files changed, 115 insertions(+), 32 deletions(-) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 58de9fe48162b..51c9010fcc244 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -10356,15 +10356,21 @@ void Sema::CheckMemaccessArguments(const CallExpr *Call, bool NonTriviallyCopyableCXXRecord = getLangOpts().CPlusPlus && RD->isCompleteDefinition() && !PointeeTy.isTriviallyCopyableType(Context); + // We don't warn about bzero or zero memsetting as these are an idiomatic + // mechanism for ensuring objects do not have stale data. + bool IsNonZeroInitMemcall = false; + if (BId == Builtin::BImemset) { + const Expr *Initializer = Call->getArg(1)->IgnoreImpCasts(); + auto IntegerConstant = dyn_cast<IntegerLiteral>(Initializer); + IsNonZeroInitMemcall = !IntegerConstant || IntegerConstant->getValue() != 0; + } - if ((BId == Builtin::BImemset || BId == Builtin::BIbzero) && - RD->isNonTrivialToPrimitiveDefaultInitialize()) { + if (IsNonZeroInitMemcall && RD->isNonTrivialToPrimitiveDefaultInitialize()) { DiagRuntimeBehavior(Dest->getExprLoc(), Dest, PDiag(diag::warn_cstruct_memaccess) << ArgIdx << FnName << PointeeTy << 0); SearchNonTrivialToInitializeField::diag(PointeeTy, Dest, *this); - } else if ((BId == Builtin::BImemset || BId == Builtin::BIbzero) && - NonTriviallyCopyableCXXRecord && ArgIdx == 0) { + } else if (IsNonZeroInitMemcall && NonTriviallyCopyableCXXRecord && ArgIdx == 0) { // FIXME: Limiting this warning to dest argument until we decide // whether it's valid for source argument too. DiagRuntimeBehavior(Dest->getExprLoc(), Dest, diff --git a/clang/test/Sema/warn-nontrivial-struct-memaccess-ptrauth.c b/clang/test/Sema/warn-nontrivial-struct-memaccess-ptrauth.c index 9cdb98e55458b..5ed4ca0bddde1 100644 --- a/clang/test/Sema/warn-nontrivial-struct-memaccess-ptrauth.c +++ b/clang/test/Sema/warn-nontrivial-struct-memaccess-ptrauth.c @@ -26,35 +26,50 @@ struct PtrAuthTrivial { struct PtrAuthNonTrivial0 { int f0; - int * AQ f1; // c-note 2 {{non-trivial to copy}} + int * AQ f1; // #PtrAuthNonTrivial0_f1 int f2; }; struct PtrAuthNonTrivial1 { - int * AQ f0; // c-note 2 {{non-trivial to copy}} + int * AQ f0; // #PtrAuthNonTrivial1_f0 int f1; struct PtrAuthNonTrivial0 f2; }; -void testPtrAuthTrivial(struct PtrAuthTrivial *d, struct PtrAuthTrivial *s) { +void testPtrAuthTrivial(struct PtrAuthTrivial *d, struct PtrAuthTrivial *s, int i) { memset(d, 0, sizeof(struct PtrAuthTrivial)); memset(d, 1, sizeof(struct PtrAuthTrivial)); + memset(d, i, sizeof(struct PtrAuthTrivial)); bzero(d, sizeof(struct PtrAuthTrivial)); memcpy(d, s, sizeof(struct PtrAuthTrivial)); memmove(d, s, sizeof(struct PtrAuthTrivial)); } void testPtrAuthNonTrivial1(struct PtrAuthNonTrivial1 *d, - struct PtrAuthNonTrivial1 *s) { - memset(d, 0, sizeof(struct PtrAuthNonTrivial1)); // cxx-warning {{is a pointer to non-trivially copyable type 'struct PtrAuthNonTrivial1'}} // cxx-note {{explicitly cast the pointer to silence}} - memset(d, 1, sizeof(struct PtrAuthNonTrivial1)); // cxx-warning {{is a pointer to non-trivially copyable type 'struct PtrAuthNonTrivial1'}} // cxx-note {{explicitly cast the pointer to silence}} - bzero(d, sizeof(struct PtrAuthNonTrivial1)); // cxx-warning {{is a pointer to non-trivially copyable type 'struct PtrAuthNonTrivial1'}} // cxx-note {{explicitly cast the pointer to silence}} - memcpy(d, s, sizeof(struct PtrAuthNonTrivial1)); - // c-warning@-1 {{that is not trivial to primitive-copy}} - // cxx-warning@-2 {{is a pointer to non-trivially copyable type 'struct PtrAuthNonTrivial1'}} - // expected-note@-3 {{explicitly cast the pointer to silence}} - memmove(d, s, sizeof(struct PtrAuthNonTrivial1)); - // c-warning@-1 {{that is not trivial to primitive-copy}} - // cxx-warning@-2 {{is a pointer to non-trivially copyable type 'struct PtrAuthNonTrivial1'}} - // expected-note@-3 {{explicitly cast the pointer to silence}} + struct PtrAuthNonTrivial1 *s, + int i) { + memset(d, 0, sizeof(struct PtrAuthNonTrivial1)); + memset(d, 1, sizeof(struct PtrAuthNonTrivial1)); // #memset_d_1 + // cxx-warning@#memset_d_1 {{is a pointer to non-trivially copyable type 'struct PtrAuthNonTrivial1'}} + // cxx-note@#memset_d_1 {{explicitly cast the pointer to silence}} + + memset(d, i, sizeof(struct PtrAuthNonTrivial1)); /// #memset_d_i + // cxx-warning@#memset_d_i {{is a pointer to non-trivially copyable type 'struct PtrAuthNonTrivial1'}} + // cxx-note@#memset_d_i {{explicitly cast the pointer to silence}} + + bzero(d, sizeof(struct PtrAuthNonTrivial1)); + + memcpy(d, s, sizeof(struct PtrAuthNonTrivial1)); // #memcpy_d + // c-warning@#memcpy_d {{that is not trivial to primitive-copy}} + // c-note@#PtrAuthNonTrivial0_f1 {{non-trivial to copy}} + // c-note@#PtrAuthNonTrivial1_f0 {{non-trivial to copy}} + // cxx-warning@#memcpy_d {{is a pointer to non-trivially copyable type 'struct PtrAuthNonTrivial1'}} + // expected-note@#memcpy_d {{explicitly cast the pointer to silence}} + + memmove(d, s, sizeof(struct PtrAuthNonTrivial1)); // #memmove_d + // c-warning@#memmove_d {{that is not trivial to primitive-copy}} + // c-note@#PtrAuthNonTrivial0_f1 {{non-trivial to copy}} + // c-note@#PtrAuthNonTrivial1_f0 {{non-trivial to copy}} + // cxx-warning@#memmove_d {{is a pointer to non-trivially copyable type 'struct PtrAuthNonTrivial1'}} + // expected-note@#memmove_d {{explicitly cast the pointer to silence}} } diff --git a/clang/test/SemaCXX/warn-memaccess.cpp b/clang/test/SemaCXX/warn-memaccess.cpp index 2e60539b3e48e..2a27d57d3eec6 100644 --- a/clang/test/SemaCXX/warn-memaccess.cpp +++ b/clang/test/SemaCXX/warn-memaccess.cpp @@ -9,8 +9,14 @@ class TriviallyCopyable {}; class NonTriviallyCopyable { NonTriviallyCopyable(const NonTriviallyCopyable&);}; struct Incomplete; +struct Polymorphic { + virtual ~Polymorphic(); +}; + + void test_bzero(TriviallyCopyable* tc, NonTriviallyCopyable *ntc, + Polymorphic *p, Incomplete* i) { // OK bzero(tc, sizeof(*tc)); @@ -18,29 +24,62 @@ void test_bzero(TriviallyCopyable* tc, // OK bzero(i, 10); - // expected-warning@+2{{first argument in call to 'bzero' is a pointer to non-trivially copyable type 'NonTriviallyCopyable'}} - // expected-note@+1{{explicitly cast the pointer to silence this warning}} + // OK bzero(ntc, sizeof(*ntc)); // OK bzero((void*)ntc, sizeof(*ntc)); + + bzero(p, sizeof(*p)); // #bzerodynamic + // expected-warning@#bzerodynamic {{destination for this 'bzero' call is a pointer to dynamic class 'Polymorphic'; vtable pointer will be overwritten}} + // expected-note@#bzerodynamic {{explicitly cast the pointer to silence this warning}} } void test_memset(TriviallyCopyable* tc, NonTriviallyCopyable *ntc, - Incomplete* i) { + Polymorphic *p, + Incomplete* i, int NonconstantInit) { // OK memset(tc, 0, sizeof(*tc)); // OK memset(i, 0, 10); - // expected-warning@+2{{first argument in call to 'memset' is a pointer to non-trivially copyable type 'NonTriviallyCopyable'}} - // expected-note@+1{{explicitly cast the pointer to silence this warning}} memset(ntc, 0, sizeof(*ntc)); // OK memset((void*)ntc, 0, sizeof(*ntc)); + + // OK + memset(p, 0, sizeof(*p)); // #memset0dynamic + // expected-warning@#memset0dynamic {{destination for this 'memset' call is a pointer to dynamic class 'Polymorphic'; vtable pointer will be overwritten}} + // expected-note@#memset0dynamic {{explicitly cast the pointer to silence this warning}} + + // OK + memset(tc, 1, sizeof(*tc)); + + // OK + memset(i, 1, 10); + + memset(ntc, 1, sizeof(*ntc)); // #memset1ntc + // expected-warning@#memset1ntc {{first argument in call to 'memset' is a pointer to non-trivially copyable type 'NonTriviallyCopyable'}} + // expected-note@#memset1ntc {{explicitly cast the pointer to silence this warning}} + + // OK + memset((void*)ntc, 1, sizeof(*ntc)); + + // OK + memset(tc, NonconstantInit, sizeof(*tc)); + + // OK + memset(i, NonconstantInit, 10); + + memset(ntc, NonconstantInit, sizeof(*ntc)); // #memsetnonconstntc + // expected-warning@#memsetnonconstntc {{first argument in call to 'memset' is a pointer to non-trivially copyable type 'NonTriviallyCopyable'}} + // expected-note@#memsetnonconstntc {{explicitly cast the pointer to silence this warning}} + + // OK + memset((void*)ntc, NonconstantInit, sizeof(*ntc)); } diff --git a/clang/test/SemaObjC/warn-nontrivial-struct-memaccess.m b/clang/test/SemaObjC/warn-nontrivial-struct-memaccess.m index a6eb485ceabe5..4ed12583afbaf 100644 --- a/clang/test/SemaObjC/warn-nontrivial-struct-memaccess.m +++ b/clang/test/SemaObjC/warn-nontrivial-struct-memaccess.m @@ -12,28 +12,51 @@ struct NonTrivial0 { int f0; - __weak id f1; // expected-note 2 {{non-trivial to default-initialize}} expected-note 2 {{non-trivial to copy}} + __weak id f1;// #NonTrivial0_f1 volatile int f2; id f3[10]; // expected-note 2 {{non-trivial to default-initialize}} expected-note 2 {{non-trivial to copy}} }; struct NonTrivial1 { - id f0; // expected-note 2 {{non-trivial to default-initialize}} expected-note 2 {{non-trivial to copy}} + id f0; // #NonTrivial1_f0 int f1; struct NonTrivial0 f2; }; -void testTrivial(struct Trivial *d, struct Trivial *s) { +void testNonTrivial0(struct NonTrivial1 *d, struct NonTrivial1 *s, int i) { + memset(d, 0, sizeof(struct NonTrivial1)); +} +void testTrivial(struct Trivial *d, struct Trivial *s, int i) { memset(d, 0, sizeof(struct Trivial)); + memset(d, 1, sizeof(struct Trivial)); + memset(d, i, sizeof(struct Trivial)); bzero(d, sizeof(struct Trivial)); memcpy(d, s, sizeof(struct Trivial)); memmove(d, s, sizeof(struct Trivial)); } -void testNonTrivial1(struct NonTrivial1 *d, struct NonTrivial1 *s) { - memset(d, 0, sizeof(struct NonTrivial1)); // expected-warning {{that is not trivial to primitive-default-initialize}} expected-note {{explicitly cast the pointer to silence}} +void testNonTrivial1(struct NonTrivial1 *d, struct NonTrivial1 *s, int i) { + memset(d, 0, sizeof(struct NonTrivial1)); + memset(d, 1, sizeof(struct NonTrivial1)); // #memset_d_1 + // expected-warning@#memset_d_1 {{that is not trivial to primitive-default-initialize}} + // expected-note@#NonTrivial1_f0 {{non-trivial to default-initialize}} + // expected-note@#NonTrivial0_f1 {{non-trivial to default-initialize}} + // expected-note@#memset_d_1 {{explicitly cast the pointer to silence}} + memset(d, i, sizeof(struct NonTrivial1)); // #memset_d_i + // expected-warning@#memset_d_i {{that is not trivial to primitive-default-initialize}} + // expected-note@#NonTrivial1_f0 {{non-trivial to default-initialize}} + // expected-note@#NonTrivial0_f1 {{non-trivial to default-initialize}} + // expected-note@#memset_d_i {{explicitly cast the pointer to silence}} memset((void *)d, 0, sizeof(struct NonTrivial1)); - bzero(d, sizeof(struct NonTrivial1)); // expected-warning {{that is not trivial to primitive-default-initialize}} expected-note {{explicitly cast the pointer to silence}} - memcpy(d, s, sizeof(struct NonTrivial1)); // expected-warning {{that is not trivial to primitive-copy}} expected-note {{explicitly cast the pointer to silence}} - memmove(d, s, sizeof(struct NonTrivial1)); // expected-warning {{that is not trivial to primitive-copy}} expected-note {{explicitly cast the pointer to silence}} + bzero(d, sizeof(struct NonTrivial1)); + memcpy(d, s, sizeof(struct NonTrivial1)); // #memcpy_d + // expected-warning@#memcpy_d {{that is not trivial to primitive-copy}} + // expected-note@#NonTrivial1_f0 {{non-trivial to copy}} + // expected-note@#NonTrivial0_f1 {{non-trivial to copy}} + // expected-note@#memcpy_d {{explicitly cast the pointer to silence}} + memmove(d, s, sizeof(struct NonTrivial1)); // #memmove_d + // expected-warning@#memmove_d {{that is not trivial to primitive-copy}} + // expected-note@#NonTrivial1_f0 {{non-trivial to copy}} + // expected-note@#NonTrivial0_f1 {{non-trivial to copy}} + // expected-note@#memmove_d {{explicitly cast the pointer to silence}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
