[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