Skip to content

Commit 03004d9

Browse files
committed
[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?
1 parent cbd4965 commit 03004d9

File tree

4 files changed

+57
-3
lines changed

4 files changed

+57
-3
lines changed

clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ class FindUninitializedField {
259259
if (T->getAsStructureType()) {
260260
if (Find(FR))
261261
return true;
262-
} else {
262+
} else if (!I->isUnnamedBitField()){
263263
SVal V = StoreMgr.getBinding(store, loc::MemRegionVal(FR));
264264
if (V.isUndef())
265265
return true;

clang/lib/StaticAnalyzer/Core/RegionStore.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2122,8 +2122,21 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
21222122
if (const std::optional<SVal> &V = B.getDirectBinding(R))
21232123
return *V;
21242124

2125-
// If the containing record was initialized, try to get its constant value.
2125+
// UnnamedBitField is always Undefined unless using memory operation such
2126+
// as 'memset'.
2127+
// For example, for code
2128+
// typedef struct {
2129+
// int i :2;
2130+
// int :30; // unnamed bit-field
2131+
// } A;
2132+
// A a = {1};
2133+
// The bits of the unnamed bit-field in local variable a can be anything.
21262134
const FieldDecl *FD = R->getDecl();
2135+
if (FD->isUnnamedBitField()) {
2136+
return UndefinedVal();
2137+
}
2138+
2139+
// If the containing record was initialized, try to get its constant value.
21272140
QualType Ty = FD->getType();
21282141
const MemRegion* superR = R->getSuperRegion();
21292142
if (const auto *VR = dyn_cast<VarRegion>(superR)) {

clang/test/Analysis/call-and-message.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
// RUN: %clang_analyze_cc1 %s -verify \
22
// RUN: -analyzer-checker=core \
33
// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=true \
4+
// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=false \
45
// RUN: -analyzer-output=plist -o %t.plist
56
// RUN: cat %t.plist | FileCheck %s
67

78
// RUN: %clang_analyze_cc1 %s -verify=no-pointee \
89
// RUN: -analyzer-checker=core \
9-
// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false
10+
// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false \
11+
// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=false
12+
13+
// RUN: %clang_analyze_cc1 %s -verify=arg-init \
14+
// RUN: -analyzer-checker=core \
15+
// RUN: -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=false \
16+
// RUN: -analyzer-config core.CallAndMessage:ArgInitializedness=true
1017

1118
// no-pointee-no-diagnostics
1219

@@ -22,3 +29,21 @@ void pointee_uninit(void) {
2229
// checker, as described in the CallAndMessage comments!
2330
// CHECK: <key>issue_hash_content_of_line_in_context</key>
2431
// CHECK-SAME: <string>97a74322d64dca40aa57303842c745a1</string>
32+
33+
typedef struct {
34+
int i :2;
35+
int :30; // unnamed bit-field
36+
} B;
37+
38+
extern void consume_B(B);
39+
40+
void bitfield_B_init(void) {
41+
B b1;
42+
b1.i = 1; // b1 is initialized
43+
consume_B(b1);
44+
}
45+
46+
void bitfield_B_uninit(void) {
47+
B b2;
48+
consume_B(b2); // arg-init-warning{{Passed-by-value struct argument contains uninitialized data (e.g., field: 'i') [core.CallAndMessage]}}
49+
}

clang/test/Analysis/call-and-message.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,4 +169,20 @@ void record_uninit() {
169169
// CHECK-SAME: <string>a46bb5c1ee44d4611ffeb13f7f499605</string>
170170
// CHECK: <key>issue_hash_content_of_line_in_context</key>
171171
// CHECK-SAME: <string>e0e0d30ea5a7b2e3a71e1931fa0768a5</string>
172+
173+
struct B{
174+
int i :2;
175+
int :30; // unnamed bit-field
176+
};
177+
178+
void bitfield_B_init(void) {
179+
B b1;
180+
b1.i = 1; // b1 is initialized
181+
consume(b1);
182+
}
183+
184+
void bitfield_B_uninit(void) {
185+
B b2;
186+
consume(b2); // arg-init-warning{{Passed-by-value struct argument contains uninitialized data (e.g., field: 'i') [core.CallAndMessage]}}
187+
}
172188
} // namespace uninit_arg

0 commit comments

Comments
 (0)