https://github.com/Tedlion updated https://github.com/llvm/llvm-project/pull/145066
>From 03004d9a9348e365a2d2d05e69f83fc404ddb605 Mon Sep 17 00:00:00 2001 From: tangwy <ted_l...@qq.com> Date: Sat, 21 Jun 2025 00:22:10 +0800 Subject: [PATCH 1/3] [clang][analyzer] Fix the false positive ArgInitializedness warning on unnamed bit-field For the following code: struct B{ int i :2; int :30; // unnamed bit-field }; extern void consume_B(B); void bitfield_B_init(void) { B b1; b1.i = 1; // b1 is initialized consume_B(b1); } The current clang static analyzer gives false positive warning "Passed-by-value struct argument contains uninitialized data (e.g., field: '') [core.CallAndMessage]" when taking the source as C code. However, no such warning is generated when clang takes the source as C++ code. After comparing the CallAndMessageChecker's different behaviors between C and C++, the reason is found: When FindUninitializedField::Find(const TypedValueRegion *R) is invoked, the concrete type of R is different. In C, 'b1' is considered to be a 'StackLocalsSpaceRegion', which makes 'StoreMgr.getBinding(store, loc::MemRegionVal(FR))' return an 'UndefinedVal'. While in c++, 'b1' is considered to be a 'tackArgumentsSpaceRegion', which finally makes the 'getBinding' return a SymbolVal. I am not quite sure about the region difference, maybe in C++ there is an implicit copy constructor function? Anyway, the unnamed bit-field is undefined, for it cannot be written unless using memory operation such as 'memset'. So a special check FD->isUnnamedBitField() is added in RegionStoreManager::getBindingForField in file RegionStore.cpp. To handle the false warning, a check isUnnamedBitField is also added in FindUninitializedField::Find in file CallAndMessageChecker.cpp. Testcases of unnamed bit-field are added in file call-and-message.c and call-and-message.cpp. I do not know what to do on the hash, so it may be updated? --- .../Checkers/CallAndMessageChecker.cpp | 2 +- clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 15 ++++++++++- clang/test/Analysis/call-and-message.c | 27 ++++++++++++++++++- clang/test/Analysis/call-and-message.cpp | 16 +++++++++++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 86476b32309c3..677cc6ee57ad2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -259,7 +259,7 @@ class FindUninitializedField { if (T->getAsStructureType()) { if (Find(FR)) return true; - } else { + } else if (!I->isUnnamedBitField()){ SVal V = StoreMgr.getBinding(store, loc::MemRegionVal(FR)); if (V.isUndef()) return true; diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index 388034b087789..1208036700f32 100644 --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -2122,8 +2122,21 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, if (const std::optional<SVal> &V = B.getDirectBinding(R)) return *V; - // If the containing record was initialized, try to get its constant value. + // UnnamedBitField is always Undefined unless using memory operation such + // as 'memset'. + // For example, for code + // typedef struct { + // int i :2; + // int :30; // unnamed bit-field + // } A; + // A a = {1}; + // The bits of the unnamed bit-field in local variable a can be anything. const FieldDecl *FD = R->getDecl(); + if (FD->isUnnamedBitField()) { + return UndefinedVal(); + } + + // If the containing record was initialized, try to get its constant value. QualType Ty = FD->getType(); const MemRegion* superR = R->getSuperRegion(); if (const auto *VR = dyn_cast<VarRegion>(superR)) { diff --git a/clang/test/Analysis/call-and-message.c b/clang/test/Analysis/call-and-message.c index b79ec8c344b6c..e2fba55d3343d 100644 --- a/clang/test/Analysis/call-and-message.c +++ b/clang/test/Analysis/call-and-message.c @@ -1,12 +1,19 @@ // RUN: %clang_analyze_cc1 %s -verify \ // RUN: -analyzer-checker=core \ // RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=true \ +// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=false \ // RUN: -analyzer-output=plist -o %t.plist // RUN: cat %t.plist | FileCheck %s // RUN: %clang_analyze_cc1 %s -verify=no-pointee \ // RUN: -analyzer-checker=core \ -// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false +// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false \ +// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=false + +// RUN: %clang_analyze_cc1 %s -verify=arg-init \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false \ +// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=true // no-pointee-no-diagnostics @@ -22,3 +29,21 @@ void pointee_uninit(void) { // checker, as described in the CallAndMessage comments! // CHECK: <key>issue_hash_content_of_line_in_context</key> // CHECK-SAME: <string>97a74322d64dca40aa57303842c745a1</string> + +typedef struct { + int i :2; + int :30; // unnamed bit-field +} B; + +extern void consume_B(B); + +void bitfield_B_init(void) { + B b1; + b1.i = 1; // b1 is initialized + consume_B(b1); +} + +void bitfield_B_uninit(void) { + B b2; + consume_B(b2); // arg-init-warning{{Passed-by-value struct argument contains uninitialized data (e.g., field: 'i') [core.CallAndMessage]}} +} \ No newline at end of file diff --git a/clang/test/Analysis/call-and-message.cpp b/clang/test/Analysis/call-and-message.cpp index 25ae23833478b..1e76973f49e13 100644 --- a/clang/test/Analysis/call-and-message.cpp +++ b/clang/test/Analysis/call-and-message.cpp @@ -169,4 +169,20 @@ void record_uninit() { // CHECK-SAME: <string>a46bb5c1ee44d4611ffeb13f7f499605</string> // CHECK: <key>issue_hash_content_of_line_in_context</key> // CHECK-SAME: <string>e0e0d30ea5a7b2e3a71e1931fa0768a5</string> + +struct B{ + int i :2; + int :30; // unnamed bit-field +}; + +void bitfield_B_init(void) { + B b1; + b1.i = 1; // b1 is initialized + consume(b1); +} + +void bitfield_B_uninit(void) { + B b2; + consume(b2); // arg-init-warning{{Passed-by-value struct argument contains uninitialized data (e.g., field: 'i') [core.CallAndMessage]}} +} } // namespace uninit_arg >From c54bc995d80056f950eb64200ccff81c2adcedcb Mon Sep 17 00:00:00 2001 From: tangwy <ted_l...@qq.com> Date: Fri, 27 Jun 2025 19:49:11 +0800 Subject: [PATCH 2/3] Replacing the else-if check with early-continue in CallAndMessageChecker.cpp. Fix formatting issues. --- clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp | 5 ++++- clang/lib/StaticAnalyzer/Core/RegionStore.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 677cc6ee57ad2..053a6e1312e24 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -253,13 +253,16 @@ class FindUninitializedField { const RecordDecl *RD = RT->getDecl()->getDefinition(); assert(RD && "Referred record has no definition"); for (const auto *I : RD->fields()) { + if (I->isUnnamedBitField()) { + continue; + } const FieldRegion *FR = MrMgr.getFieldRegion(I, R); FieldChain.push_back(I); T = I->getType(); if (T->getAsStructureType()) { if (Find(FR)) return true; - } else if (!I->isUnnamedBitField()){ + } else { SVal V = StoreMgr.getBinding(store, loc::MemRegionVal(FR)); if (V.isUndef()) return true; diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index 1208036700f32..2e509d09cc9ba 100644 --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -2133,7 +2133,7 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, // The bits of the unnamed bit-field in local variable a can be anything. const FieldDecl *FD = R->getDecl(); if (FD->isUnnamedBitField()) { - return UndefinedVal(); + return UndefinedVal(); } // If the containing record was initialized, try to get its constant value. >From db350f607a6792ef87c83d2b501e0cdf7cae4b83 Mon Sep 17 00:00:00 2001 From: Tedlion <813055...@qq.com> Date: Fri, 27 Jun 2025 20:58:32 +0800 Subject: [PATCH 3/3] Update clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp remove braces on simple single statement bodies Co-authored-by: Baranov Victor <bar.victor.2...@gmail.com> --- clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 053a6e1312e24..23935647a5826 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -253,9 +253,8 @@ class FindUninitializedField { const RecordDecl *RD = RT->getDecl()->getDefinition(); assert(RD && "Referred record has no definition"); for (const auto *I : RD->fields()) { - if (I->isUnnamedBitField()) { + if (I->isUnnamedBitField()) continue; - } const FieldRegion *FR = MrMgr.getFieldRegion(I, R); FieldChain.push_back(I); T = I->getType(); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits