Skip to content

Commit 7504064

Browse files
authored
fix #13653 & #13850: Wrong struct size computed for bitfields (danmar#7597)
1 parent b8245c6 commit 7504064

File tree

3 files changed

+87
-5
lines changed

3 files changed

+87
-5
lines changed

lib/valueflow.cpp

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ static Result accumulateStructMembers(const Scope* scope, F f, ValueFlow::Accura
440440
for (const Variable& var : scope->varlist) {
441441
if (var.isStatic())
442442
continue;
443+
const size_t bits = var.nameToken() ? var.nameToken()->bits() : 0;
443444
if (const ValueType* vt = var.valueType()) {
444445
if (vt->type == ValueType::Type::RECORD && vt->typeScope == scope)
445446
return {0, false};
@@ -449,12 +450,12 @@ static Result accumulateStructMembers(const Scope* scope, F f, ValueFlow::Accura
449450
if (var.nameToken()->scope() != scope && var.nameToken()->scope()->definedType) { // anonymous union
450451
const auto ret = anonScopes.insert(var.nameToken()->scope());
451452
if (ret.second)
452-
total = f(total, *vt, dim);
453+
total = f(total, *vt, dim, bits);
453454
}
454455
else
455-
total = f(total, *vt, dim);
456+
total = f(total, *vt, dim, bits);
456457
}
457-
if (accuracy == ValueFlow::Accuracy::ExactOrZero && total == 0)
458+
if (accuracy == ValueFlow::Accuracy::ExactOrZero && total == 0 && bits == 0)
458459
return {0, false};
459460
}
460461
return {total, true};
@@ -485,7 +486,7 @@ static size_t getAlignOf(const ValueType& vt, const Settings& settings, ValueFlo
485486
return align == 0 ? 0 : bitCeil(align);
486487
}
487488
if (vt.type == ValueType::Type::RECORD && vt.typeScope) {
488-
auto accHelper = [&](size_t max, const ValueType& vt2, size_t /*dim*/) {
489+
auto accHelper = [&](size_t max, const ValueType& vt2, size_t /*dim*/, size_t /*bits*/) {
489490
size_t a = getAlignOf(vt2, settings, accuracy, ++maxRecursion);
490491
return std::max(max, a);
491492
};
@@ -534,17 +535,46 @@ size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings, Accur
534535
if (vt.type == ValueType::Type::CONTAINER)
535536
return 3 * settings.platform.sizeof_pointer; // Just guess
536537
if (vt.type == ValueType::Type::RECORD && vt.typeScope) {
537-
auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim) -> size_t {
538+
size_t currentBitCount = 0;
539+
size_t currentBitfieldAlloc = 0;
540+
auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim, size_t bits) -> size_t {
541+
const size_t charBit = settings.platform.char_bit;
538542
size_t n = ValueFlow::getSizeOf(vt2, settings,accuracy, ++maxRecursion);
539543
size_t a = getAlignOf(vt2, settings, accuracy);
544+
if (bits > 0) {
545+
size_t ret = total;
546+
if (currentBitfieldAlloc == 0) {
547+
currentBitfieldAlloc = n;
548+
currentBitCount = 0;
549+
} else if (currentBitCount + bits > charBit * currentBitfieldAlloc) {
550+
ret += currentBitfieldAlloc;
551+
currentBitfieldAlloc = n;
552+
currentBitCount = 0;
553+
}
554+
currentBitCount += bits;
555+
return ret;
556+
}
540557
if (n == 0 || a == 0)
541558
return accuracy == Accuracy::ExactOrZero ? 0 : total;
542559
n *= dim;
543560
size_t padding = (a - (total % a)) % a;
561+
if (currentBitCount > 0) {
562+
bool fitsInBitfield = currentBitCount + n * charBit <= currentBitfieldAlloc * charBit;
563+
bool isAligned = currentBitCount % (charBit * a) == 0;
564+
if (vt2.isIntegral() && fitsInBitfield && isAligned) {
565+
currentBitCount += charBit * n;
566+
return total;
567+
}
568+
n += currentBitfieldAlloc;
569+
currentBitfieldAlloc = 0;
570+
currentBitCount = 0;
571+
}
544572
return vt.typeScope->type == ScopeType::eUnion ? std::max(total, n) : total + padding + n;
545573
};
546574
Result result = accumulateStructMembers(vt.typeScope, accHelper, accuracy);
547575
size_t total = result.total;
576+
if (currentBitCount > 0)
577+
total += currentBitfieldAlloc;
548578
if (const Type* dt = vt.typeScope->definedType) {
549579
total = std::accumulate(dt->derivedFrom.begin(), dt->derivedFrom.end(), total, [&](size_t v, const Type::BaseInfo& bi) {
550580
if (bi.type && bi.type->classScope)

test/testother.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11240,6 +11240,15 @@ class TestOther : public TestFixture {
1124011240
"};\n"
1124111241
"void f(S<char, 3> s) {}\n");
1124211242
ASSERT_EQUALS("", errout_str());
11243+
11244+
Settings settingsUnix32 = settingsBuilder().platform(Platform::Type::Unix32).build();
11245+
check("struct S {\n" // #13850
11246+
" int i0 : 32;\n"
11247+
" int i1 : 16;\n"
11248+
" unsigned short u16;\n"
11249+
"};\n"
11250+
"void f(S s) {}\n", true, true, true, false, &settingsUnix32);
11251+
ASSERT_EQUALS("", errout_str());
1124311252
}
1124411253

1124511254
void checkComparisonFunctionIsAlwaysTrueOrFalse() {

test/testvalueflow.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ class TestValueFlow : public TestFixture {
175175
mNewTemplate = true;
176176

177177
TEST_CASE(performanceIfCount);
178+
TEST_CASE(bitfields);
178179
}
179180

180181
static bool isNotTokValue(const ValueFlow::Value &val) {
@@ -9063,6 +9064,48 @@ class TestValueFlow : public TestFixture {
90639064
"}\n";
90649065
ASSERT_EQUALS(1U, tokenValues(code, "v .", &s).size());
90659066
}
9067+
9068+
#define testBitfields(structBody, expectedSize) testBitfields_(__FILE__, __LINE__, structBody, expectedSize)
9069+
void testBitfields_(const char *file, int line, const std::string &structBody, std::size_t expectedSize) {
9070+
const std::string code = "struct S { " + structBody + " }; const std::size_t size = sizeof(S);";
9071+
const auto values = tokenValues(code.c_str(), "( S");
9072+
ASSERT_LOC(!values.empty(), file, line);
9073+
ASSERT_EQUALS_LOC(expectedSize, values.back().intvalue, file, line);
9074+
}
9075+
9076+
void bitfields() {
9077+
9078+
// #13653
9079+
testBitfields("unsigned int data_rw: 1;\n"
9080+
"unsigned int page_address: 4;\n"
9081+
"unsigned int register_address: 3;\n",
9082+
4);
9083+
9084+
testBitfields("unsigned char data_rw: 1;\n"
9085+
"unsigned char page_address: 4;\n"
9086+
"unsigned char register_address: 3;\n",
9087+
1);
9088+
9089+
testBitfields("unsigned int a : 1;\n"
9090+
"unsigned int b;\n"
9091+
"unsigned int c : 1;\n",
9092+
12);
9093+
9094+
testBitfields("unsigned int a : 1;\n"
9095+
"unsigned char b;\n"
9096+
"unsigned int c : 1;\n",
9097+
12);
9098+
9099+
testBitfields("unsigned int a : 31;\n"
9100+
"unsigned int b : 2;\n",
9101+
8);
9102+
9103+
// #13850
9104+
testBitfields("int a : 32;\n"
9105+
"int b : 16;\n"
9106+
"unsigned short c;\n",
9107+
8);
9108+
}
90669109
};
90679110

90689111
REGISTER_TEST(TestValueFlow)

0 commit comments

Comments
 (0)