[clang] [clang][analyzer] Fix the false positive ArgInitializedness warning on unnamed bit-field (PR #145066)

via cfe-commits cfe-commits at lists.llvm.org
Fri Jun 20 09:26:43 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-static-analyzer-1

Author: None (Tedlion)

<details>
<summary>Changes</summary>

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?**

---
Full diff: https://github.com/llvm/llvm-project/pull/145066.diff


4 Files Affected:

- (modified) clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp (+1-1) 
- (modified) clang/lib/StaticAnalyzer/Core/RegionStore.cpp (+14-1) 
- (modified) clang/test/Analysis/call-and-message.c (+26-1) 
- (modified) clang/test/Analysis/call-and-message.cpp (+16) 


``````````diff
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

``````````

</details>


https://github.com/llvm/llvm-project/pull/145066


More information about the cfe-commits mailing list