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

via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 3 01:38:47 PDT 2025


================
@@ -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.
----------------
Tedlion wrote:

@steakhal I think I've found the reason.

When the source is C++, there is **an additional copy constructor invocation**. `b1` is created at `StackLocalsSpaceRegion`, while the  function `consume_B` takes the object being copy-constructed, so I suppose `StackArgumentsSpaceRegion` is corrected.

If the copy-constructor is non-trivial and not visible in the TU, the argument function `consume_B` takes maybe well-initialized in the custom copy-constructor, then no warnings should be raised. That's a scenario never happens in C.

When the copy-constructor is trivial, the analyzer's ExprEngine copies the binding from `StackLocalsSpaceRegion` to  `StackArgumentsSpaceRegion`.  I get the frame stacks handling this  
(Note the frame `8: ExprEngine::handleConstructor` and `ExprEngine::performTrivialCopy`):
```
#0: RegionStoreManager::getBindingForField @ RegionStore.cpp:2123
#1: RegionStoreManager::tryBindSmallStruct @ RegionStore.cpp:2826
#2: RegionStoreManager::bindStruct @ RegionStore.cpp:2855
#3: RegionStoreManager::bind @ RegionStore.cpp:2550
#4: RegionStoreManager::Bind @ RegionStore.cpp:597
#5: ProgramState::bindLoc @ ProgramState.cpp:119
#6: ExprEngine::evalBind @ ExprEngine.cpp:3741
#7: ExprEngine::performTrivialCopy @ ExprEngineCXX.cpp:88
#8: ExprEngine::handleConstructor @ ExprEngineCXX.cpp:746
#9: ExprEngine::Visit @ ExprEngine.cpp:2192
#10: ExprEngine::ProcessStmt @ ExprEngine.cpp:1135
#11: CoreEngine::HandlePostStmt @ CoreEngine.cpp:531
#12: CoreEngine::dispatchWorkItem @ CoreEngine.cpp:255
#13: `CoreEngine::ExecuteWorkList'::`2'::<lambda_1>::operator() @ CoreEngine.cpp:159
#14: CoreEngine::ExecuteWorkList @ CoreEngine.cpp:163
#15: AnalysisConsumer::RunPathSensitiveChecks @ ExprEngine.h:192
#16: AnalysisConsumer::RunPathSensitiveChecks @ AnalysisConsumer.cpp:768
#17: AnalysisConsumer::HandleCode @ AnalysisConsumer.cpp:737
#18: AnalysisConsumer::HandleDeclsCallGraph @ AnalysisConsumer.cpp:508
#19: AnalysisConsumer::runAnalysisOnTranslationUnit @ AnalysisConsumer.cpp:580
#20: AnalysisConsumer::HandleTranslationUnit @ AnalysisConsumer.cpp:643
#21: ParseAST @ ParseAST.cpp:183
```
The above procedure happens when parsing the statement `consume_B(b1)` and before the `CallAndMessageChecker::PreVisitProcessArg` is invoked.

**In function `RegionStoreManager::tryBindSmallStruct`, there are statements which intentionally skip the unnamed bit-fields**:
```cpp
......
  for (const auto *FD : RD->fields()) {
    if (FD->isUnnamedBitField())
      continue;
......
``` 
That's the problem when `CallAndMessageChecker::PreVisitProcessArg` is invoking, a direct binding for named field `i` can be found , while the binding for unnamed not, then a default value is returned. After commenting the continue lines,  the `UndefinedVal` for the unnamed bit-field is returned  in  `FindUninitializedField::find()` and the warning "Passed-by-value struct argument contains uninitialized data (e.g., field: '') [core.CallAndMessage]" is raised, if my patch is not applied.

PS:  Since the intentionally skipping is in `tryBindSmallStruct`, so I try to make the struct larger:
 ```cpp
typedef struct {
    int x;
    int y;

    int   i : 2; // 1 bit
    int     :30;
} B;

extern void consume_B(B);

void bitfield_B_init(void) {
    B b1;
    b1.x = 0;
    b1.y = 0;
    b1.i = 1; // b1 is initialized
    consume_B(b1);
}
```
Now the binding for the unnamed bit-field in   `FindUninitializedField::find()` is the correct `SVal` `UndefinedVal`, and checking for c++ also raise a warning "warning: Passed-by-value struct argument contains uninitialized data (e.g., field: '') [core.CallAndMessage]".


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


More information about the cfe-commits mailing list