https://github.com/gamesh411 updated https://github.com/llvm/llvm-project/pull/191061
From 22a3128a565a0a8ac98145ed0fe57e80b251e00a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 8 Apr 2026 23:39:24 +0200 Subject: [PATCH 1/2] [analyzer] Fix crash in CStringChecker on zero-size element types Move the null check of Offset before its dereference in checkInit. When the element type has zero size (e.g. empty struct), the division returns an empty optional which was dereferenced unconditionally. Fixes #190457 --- clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 6 +++--- clang/test/Analysis/bstring.c | 8 ++++++++ clang/test/Analysis/bstring.cpp | 9 +++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index fbb47348db55b..e486787bd8d7f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -507,13 +507,13 @@ ProgramStateRef CStringChecker::checkInit(CheckerContext &C, IdxTy) .getAs<NonLoc>(); + if (!Offset) + return State; + // Retrieve the index of the last element. const NonLoc One = SVB.makeIntVal(1, IdxTy).castAs<NonLoc>(); SVal LastIdx = SVB.evalBinOpNN(State, BO_Sub, *Offset, One, IdxTy); - if (!Offset) - return State; - SVal LastElementVal = State->getLValue(ElemTy, LastIdx, loc::MemRegionVal(SuperR)); if (!isa<Loc>(LastElementVal)) diff --git a/clang/test/Analysis/bstring.c b/clang/test/Analysis/bstring.c index 01f85cecfbf43..ecfceb0626d04 100644 --- a/clang/test/Analysis/bstring.c +++ b/clang/test/Analysis/bstring.c @@ -530,3 +530,11 @@ void nocrash_on_locint_offset(void *addr, void* from, struct S s) { size_t iAdd = (size_t) addr; memcpy(((void *) &(s.f)), from, iAdd); } + +// PR#190457 - Crash on memcpy with zero-size element type (empty struct). +void nocrash_on_empty_struct_memcpy(void) { + struct {} a[10]; + __builtin_memcpy(&a[2], a, 2); // should not crash + // expected-warning@-1 {{'memcpy' will always overflow; destination buffer has size 0, but size argument is 2}} + // expected-warning@-2 {{Memory copy function overflows the destination buffer}} +} diff --git a/clang/test/Analysis/bstring.cpp b/clang/test/Analysis/bstring.cpp index 9f044c6453739..c2331cbb04f54 100644 --- a/clang/test/Analysis/bstring.cpp +++ b/clang/test/Analysis/bstring.cpp @@ -248,3 +248,12 @@ void memmove_uninit_without_outofbound() { memmove(dst, src, sizeof(src)); // uninit-warning{{The first element of the 2nd argument is undefined}} // uninit-note@-1{{Other elements might also be undefined}} } + +// related to PR#190457 - In C++ empty structs have 'sizeof' of 1, so this +// should not crash and should not warn about overflow (unlike the C case +// where sizeof(struct{}) is 0). +void nocrash_on_empty_struct_memcpy_cpp() { + struct {} a[10]; + __builtin_memcpy(&a[2], a, 2); // should not crash + // no-warning +} From bf7992e881246acd24ba95de33ef5ca49fa1c018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Thu, 9 Apr 2026 00:51:20 +0200 Subject: [PATCH 2/2] [analyzer][test] Fix bstring.c test for Windows/MSVC targets Guard overflow warnings behind !_WIN32. On MSVC targets, the sizeof of an empty struct is 4 in C mode (see MicrosoftRecordLayoutBuilder::layout in RecordLayoutBuilder.cpp). --- clang/test/Analysis/bstring.c | 12 +++++++++--- clang/test/Analysis/bstring.cpp | 6 +++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/clang/test/Analysis/bstring.c b/clang/test/Analysis/bstring.c index ecfceb0626d04..f343aaec43307 100644 --- a/clang/test/Analysis/bstring.c +++ b/clang/test/Analysis/bstring.c @@ -532,9 +532,15 @@ void nocrash_on_locint_offset(void *addr, void* from, struct S s) { } // PR#190457 - Crash on memcpy with zero-size element type (empty struct). +// In the GNU C extension, empty structs have sizeof == 0, which caused a +// division by zero in checkInit. On MSVC targets, even in C mode, empty +// structs have nonzero sizeof (due to ABI requirements), so the overflow +// warnings don't fire there. void nocrash_on_empty_struct_memcpy(void) { struct {} a[10]; - __builtin_memcpy(&a[2], a, 2); // should not crash - // expected-warning@-1 {{'memcpy' will always overflow; destination buffer has size 0, but size argument is 2}} - // expected-warning@-2 {{Memory copy function overflows the destination buffer}} + __builtin_memcpy(&a[2], a, 2); // no-crash +#if !defined(_WIN32) + // expected-warning@-2 {{'memcpy' will always overflow; destination buffer has size 0, but size argument is 2}} + // expected-warning@-3 {{Memory copy function overflows the destination buffer}} +#endif } diff --git a/clang/test/Analysis/bstring.cpp b/clang/test/Analysis/bstring.cpp index c2331cbb04f54..2f1712648d8e1 100644 --- a/clang/test/Analysis/bstring.cpp +++ b/clang/test/Analysis/bstring.cpp @@ -249,9 +249,9 @@ void memmove_uninit_without_outofbound() { // uninit-note@-1{{Other elements might also be undefined}} } -// related to PR#190457 - In C++ empty structs have 'sizeof' of 1, so this -// should not crash and should not warn about overflow (unlike the C case -// where sizeof(struct{}) is 0). +// #190457 - In C++ the sizeof of an empty struct is 1, so this should not +// crash and should not warn about overflow (unlike the C case where it is 0 +// with the GNU extension). void nocrash_on_empty_struct_memcpy_cpp() { struct {} a[10]; __builtin_memcpy(&a[2], a, 2); // should not crash _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
