Skip to content

Commit 90d7f72

Browse files
authored
Fix 12060: calculate sizeof(struct) (cppcheck-opensource#5919)
1 parent 381360b commit 90d7f72

2 files changed

Lines changed: 121 additions & 1 deletion

File tree

lib/valueflow.cpp

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,53 @@ static void setTokenValueCast(Token *parent, const ValueType &valueType, const V
11051105
}
11061106
}
11071107

1108+
template<class F>
1109+
static size_t accumulateStructMembers(const Scope* scope, F f)
1110+
{
1111+
size_t total = 0;
1112+
for (const Variable& var : scope->varlist) {
1113+
if (var.isStatic())
1114+
continue;
1115+
if (const ValueType* vt = var.valueType()) {
1116+
if (vt->type == ValueType::Type::RECORD && vt->typeScope == scope)
1117+
return 0;
1118+
total = f(total, *vt);
1119+
}
1120+
if (total == 0)
1121+
return 0;
1122+
}
1123+
return total;
1124+
}
1125+
1126+
static size_t bitCeil(size_t x)
1127+
{
1128+
if (x <= 1)
1129+
return 1;
1130+
--x;
1131+
x |= x >> 1;
1132+
x |= x >> 2;
1133+
x |= x >> 4;
1134+
x |= x >> 8;
1135+
x |= x >> 16;
1136+
x |= x >> 32;
1137+
return x + 1;
1138+
}
1139+
1140+
static size_t getAlignOf(const ValueType& vt, const Settings& settings)
1141+
{
1142+
if (vt.pointer || vt.isPrimitive()) {
1143+
auto align = ValueFlow::getSizeOf(vt, settings);
1144+
return align == 0 ? 0 : bitCeil(align);
1145+
}
1146+
if (vt.type == ValueType::Type::RECORD && vt.typeScope) {
1147+
return accumulateStructMembers(vt.typeScope, [&](size_t max, const ValueType& vt2) {
1148+
size_t a = getAlignOf(vt2, settings);
1149+
return std::max(max, a);
1150+
});
1151+
}
1152+
return 0;
1153+
}
1154+
11081155
static nonneg int getSizeOfType(const Token *typeTok, const Settings &settings)
11091156
{
11101157
const ValueType &valueType = ValueType::parseDecl(typeTok, settings);
@@ -1134,7 +1181,23 @@ size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings)
11341181
return settings.platform.sizeof_double;
11351182
if (vt.type == ValueType::Type::LONGDOUBLE)
11361183
return settings.platform.sizeof_long_double;
1137-
1184+
if (vt.type == ValueType::Type::RECORD && vt.typeScope) {
1185+
size_t total = accumulateStructMembers(vt.typeScope, [&](size_t total, const ValueType& vt2) -> size_t {
1186+
size_t n = ValueFlow::getSizeOf(vt2, settings);
1187+
size_t a = getAlignOf(vt2, settings);
1188+
if (n == 0 || a == 0)
1189+
return 0;
1190+
size_t padding = (a - (total % a)) % a;
1191+
return total + padding + n;
1192+
});
1193+
if (total == 0)
1194+
return 0;
1195+
size_t align = getAlignOf(vt, settings);
1196+
if (align == 0)
1197+
return 0;
1198+
total += (align - (total % align)) % align;
1199+
return total;
1200+
}
11381201
return 0;
11391202
}
11401203

test/testvalueflow.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,63 @@ class TestValueFlow : public TestFixture {
13941394
values = tokenValues(code, "=");
13951395
ASSERT_EQUALS(1U, values.size());
13961396
ASSERT_EQUALS(sizeof(std::int32_t) * 10 * 20, values.back().intvalue);
1397+
1398+
code = "struct X { float a; float b; };\n"
1399+
"void f() {\n"
1400+
" x = sizeof(X);\n"
1401+
"}";
1402+
values = tokenValues(code, "( X )");
1403+
ASSERT_EQUALS(1U, values.size());
1404+
ASSERT_EQUALS(8, values.back().intvalue);
1405+
1406+
code = "struct X { char a; char b; };\n"
1407+
"void f() {\n"
1408+
" x = sizeof(X);\n"
1409+
"}";
1410+
values = tokenValues(code, "( X )");
1411+
ASSERT_EQUALS(1U, values.size());
1412+
ASSERT_EQUALS(2, values.back().intvalue);
1413+
1414+
code = "struct X { char a; float b; };\n"
1415+
"void f() {\n"
1416+
" x = sizeof(X);\n"
1417+
"}";
1418+
values = tokenValues(code, "( X )");
1419+
ASSERT_EQUALS(1U, values.size());
1420+
ASSERT_EQUALS(8, values.back().intvalue);
1421+
1422+
code = "struct X { char a; char b; float c; };\n"
1423+
"void f() {\n"
1424+
" x = sizeof(X);\n"
1425+
"}";
1426+
values = tokenValues(code, "( X )");
1427+
ASSERT_EQUALS(1U, values.size());
1428+
ASSERT_EQUALS(8, values.back().intvalue);
1429+
1430+
code = "struct X { float a; char b; };\n"
1431+
"void f() {\n"
1432+
" x = sizeof(X);\n"
1433+
"}";
1434+
values = tokenValues(code, "( X )");
1435+
ASSERT_EQUALS(1U, values.size());
1436+
ASSERT_EQUALS(8, values.back().intvalue);
1437+
1438+
code = "struct X { float a; char b; char c; };\n"
1439+
"void f() {\n"
1440+
" x = sizeof(X);\n"
1441+
"}";
1442+
values = tokenValues(code, "( X )");
1443+
ASSERT_EQUALS(1U, values.size());
1444+
ASSERT_EQUALS(8, values.back().intvalue);
1445+
1446+
code = "struct A { float a; char b; };\n"
1447+
"struct X { A a; char b; };\n"
1448+
"void f() {\n"
1449+
" x = sizeof(X);\n"
1450+
"}";
1451+
values = tokenValues(code, "( X )");
1452+
ASSERT_EQUALS(1U, values.size());
1453+
ASSERT_EQUALS(12, values.back().intvalue);
13971454
}
13981455

13991456
void valueFlowComma()

0 commit comments

Comments
 (0)