Skip to content

Commit fcefda9

Browse files
Merge branch 'danmar:main' into chr_14193
2 parents 090b7e8 + d69955c commit fcefda9

15 files changed

Lines changed: 283 additions & 18 deletions

.github/workflows/CI-windows.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,11 @@ jobs:
9191
with:
9292
persist-credentials: false
9393

94-
- name: Set up Python 3.13
94+
- name: Set up Python
9595
if: matrix.config == 'release'
9696
uses: actions/setup-python@v5
9797
with:
98-
python-version: '3.13'
98+
python-version: '3.14'
9999
check-latest: true
100100

101101
- name: Set up Visual Studio environment

.github/workflows/asan.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ jobs:
3636
with:
3737
key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }}
3838

39-
- name: Set up Python 3.13
39+
- name: Set up Python
4040
uses: actions/setup-python@v5
4141
with:
42-
python-version: '3.13'
42+
python-version: '3.14'
4343
check-latest: true
4444

4545
- name: Install missing software on ubuntu

.github/workflows/scriptcheck.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ jobs:
4848
runs-on: ubuntu-22.04
4949
strategy:
5050
matrix:
51-
python-version: [3.7, 3.8, 3.9, '3.10', '3.11', '3.12', '3.13']
51+
python-version: [3.7, 3.8, 3.9, '3.10', '3.11', '3.12', '3.13', '3.14']
5252
include:
53-
- python-version: '3.13'
53+
- python-version: '3.14'
5454
python-latest: true
5555

5656
fail-fast: false

.github/workflows/tsan.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ jobs:
3636
with:
3737
key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }}
3838

39-
- name: Set up Python 3.13
39+
- name: Set up Python
4040
uses: actions/setup-python@v5
4141
with:
42-
python-version: '3.13'
42+
python-version: '3.14'
4343
check-latest: true
4444

4545
- name: Install missing software on ubuntu

.github/workflows/ubsan.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ jobs:
3636
with:
3737
key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }}
3838

39-
- name: Set up Python 3.13
39+
- name: Set up Python
4040
uses: actions/setup-python@v5
4141
with:
42-
python-version: '3.13'
42+
python-version: '3.14'
4343
check-latest: true
4444

4545
- name: Install missing software on ubuntu

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ ifdef FILESDIR
4343
override CPPFLAGS+=-DFILESDIR=\"$(FILESDIR)\"
4444
endif
4545

46-
RDYNAMIC=-rdynamic
46+
RDYNAMIC?=-rdynamic
4747
# Set the CPPCHK_GLIBCXX_DEBUG flag. This flag is not used in release Makefiles.
4848
# The _GLIBCXX_DEBUG define doesn't work in Cygwin or other Win32 systems.
4949
ifndef COMSPEC

addons/misra.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import re
2323
import os
2424
import argparse
25-
import codecs
2625
import string
2726
import copy
2827

@@ -4525,7 +4524,7 @@ def loadRuleTexts(self, filename):
45254524
encodings = ['ascii', 'utf-8', 'windows-1250', 'windows-1252']
45264525
for e in encodings:
45274526
try:
4528-
file_stream = codecs.open(filename, 'r', encoding=e)
4527+
file_stream = open(filename, 'r', encoding=e)
45294528
file_stream.readlines()
45304529
file_stream.seek(0)
45314530
except UnicodeDecodeError:

lib/checkother.cpp

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <map>
4444
#include <set>
4545
#include <sstream>
46+
#include <unordered_map>
4647
#include <utility>
4748

4849
//---------------------------------------------------------------------------
@@ -4358,6 +4359,139 @@ void CheckOther::checkModuloOfOneError(const Token *tok)
43584359
reportError(tok, Severity::style, "moduloofone", "Modulo of one is always equal to zero");
43594360
}
43604361

4362+
static const std::string noname;
4363+
4364+
struct UnionMember {
4365+
UnionMember()
4366+
: name(noname)
4367+
, size(0) {}
4368+
4369+
UnionMember(const std::string &name, size_t size)
4370+
: name(name)
4371+
, size(size) {}
4372+
4373+
const std::string &name;
4374+
size_t size;
4375+
};
4376+
4377+
struct Union {
4378+
explicit Union(const Scope &scope)
4379+
: scope(&scope)
4380+
, name(scope.className) {}
4381+
4382+
const Scope *scope;
4383+
const std::string &name;
4384+
std::vector<UnionMember> members;
4385+
4386+
const UnionMember *largestMember() const {
4387+
const UnionMember *largest = nullptr;
4388+
for (const UnionMember &m : members) {
4389+
if (m.size == 0)
4390+
return nullptr;
4391+
if (largest == nullptr || m.size > largest->size)
4392+
largest = &m;
4393+
}
4394+
return largest;
4395+
}
4396+
4397+
bool isLargestMemberFirst() const {
4398+
const UnionMember *largest = largestMember();
4399+
return largest == nullptr || largest == &members[0];
4400+
}
4401+
};
4402+
4403+
static UnionMember parseUnionMember(const Variable &var,
4404+
const Settings &settings)
4405+
{
4406+
const Token *nameToken = var.nameToken();
4407+
if (nameToken == nullptr)
4408+
return UnionMember();
4409+
4410+
const ValueType *vt = nameToken->valueType();
4411+
size_t size = 0;
4412+
if (var.isArray()) {
4413+
size = var.dimension(0);
4414+
} else if (vt != nullptr) {
4415+
size = ValueFlow::getSizeOf(*vt, settings,
4416+
ValueFlow::Accuracy::ExactOrZero);
4417+
}
4418+
return UnionMember(nameToken->str(), size);
4419+
}
4420+
4421+
static std::vector<Union> parseUnions(const SymbolDatabase &symbolDatabase,
4422+
const Settings &settings)
4423+
{
4424+
std::vector<Union> unions;
4425+
4426+
for (const Scope &scope : symbolDatabase.scopeList) {
4427+
if (scope.type != ScopeType::eUnion)
4428+
continue;
4429+
4430+
Union u(scope);
4431+
for (const Variable &var : scope.varlist) {
4432+
u.members.push_back(parseUnionMember(var, settings));
4433+
}
4434+
unions.push_back(u);
4435+
}
4436+
4437+
return unions;
4438+
}
4439+
4440+
static bool isZeroInitializer(const Token *tok)
4441+
{
4442+
return Token::Match(tok, "= { 0| } ;");
4443+
}
4444+
4445+
4446+
void CheckOther::checkUnionZeroInit()
4447+
{
4448+
if (!mSettings->severity.isEnabled(Severity::portability))
4449+
return;
4450+
4451+
logChecker("CheckOther::checkUnionZeroInit"); // portability
4452+
4453+
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
4454+
4455+
std::unordered_map<const Scope *, Union> unionsByScopeId;
4456+
const std::vector<Union> unions = parseUnions(*symbolDatabase, *mSettings);
4457+
for (const Union &u : unions) {
4458+
unionsByScopeId.emplace(u.scope, u);
4459+
}
4460+
4461+
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
4462+
if (!tok->isName() || !isZeroInitializer(tok->next()))
4463+
continue;
4464+
4465+
const ValueType *vt = tok->valueType();
4466+
if (vt == nullptr || vt->typeScope == nullptr)
4467+
continue;
4468+
auto it = unionsByScopeId.find(vt->typeScope);
4469+
if (it == unionsByScopeId.end())
4470+
continue;
4471+
const Union &u = it->second;
4472+
if (!u.isLargestMemberFirst()) {
4473+
const UnionMember *largestMember = u.largestMember();
4474+
if (largestMember == nullptr) {
4475+
throw InternalError(tok, "Largest union member not found",
4476+
InternalError::INTERNAL);
4477+
}
4478+
assert(largestMember != nullptr);
4479+
unionZeroInitError(tok, *largestMember);
4480+
}
4481+
}
4482+
}
4483+
4484+
void CheckOther::unionZeroInitError(const Token *tok,
4485+
const UnionMember& largestMember)
4486+
{
4487+
reportError(tok, Severity::portability, "UnionZeroInit",
4488+
(tok != nullptr ? "$symbol:" + tok->str() + "\n" : "") +
4489+
"Zero initializing union '$symbol' does not guarantee " +
4490+
"its complete storage to be zero initialized as its largest member " +
4491+
"is not declared as the first member. Consider making " +
4492+
largestMember.name + " the first member or favor memset().");
4493+
}
4494+
43614495
//-----------------------------------------------------------------------------
43624496
// Overlapping write (undefined behavior)
43634497
//-----------------------------------------------------------------------------
@@ -4576,6 +4710,7 @@ void CheckOther::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger)
45764710
checkOther.checkAccessOfMovedVariable();
45774711
checkOther.checkModuloOfOne();
45784712
checkOther.checkOverlappingWrite();
4713+
checkOther.checkUnionZeroInit();
45794714
}
45804715

45814716
void CheckOther::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const
@@ -4658,4 +4793,5 @@ void CheckOther::getErrorMessages(ErrorLogger *errorLogger, const Settings *sett
46584793
const std::vector<const Token *> nullvec;
46594794
c.funcArgOrderDifferent("function", nullptr, nullptr, nullvec, nullvec);
46604795
c.checkModuloOfOneError(nullptr);
4796+
c.unionZeroInitError(nullptr, UnionMember());
46614797
}

lib/checkother.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class Function;
4040
class Variable;
4141
class ErrorLogger;
4242
class Tokenizer;
43+
struct UnionMember;
4344

4445
/// @addtogroup Checks
4546
/// @{
@@ -194,6 +195,8 @@ class CPPCHECKLIB CheckOther : public Check {
194195

195196
void checkModuloOfOne();
196197

198+
void checkUnionZeroInit();
199+
197200
void checkOverlappingWrite();
198201
void overlappingWriteUnion(const Token *tok);
199202
void overlappingWriteFunction(const Token *tok, const std::string& funcname);
@@ -257,6 +260,7 @@ class CPPCHECKLIB CheckOther : public Check {
257260
void knownPointerToBoolError(const Token* tok, const ValueFlow::Value* value);
258261
void comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2);
259262
void checkModuloOfOneError(const Token *tok);
263+
void unionZeroInitError(const Token *tok, const UnionMember& largestMember);
260264

261265
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override;
262266

@@ -291,6 +295,7 @@ class CPPCHECKLIB CheckOther : public Check {
291295
// portability
292296
"- Passing NULL pointer to function with variable number of arguments leads to UB.\n"
293297
"- Casting non-zero integer literal in decimal or octal format to pointer.\n"
298+
"- Incorrect zero initialization of unions can lead to access of uninitialized memory.\n"
294299

295300
// style
296301
"- C-style pointer cast in C++ code\n"

lib/tokenize.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9411,6 +9411,8 @@ void Tokenizer::simplifyAttribute()
94119411

94129412
else if (Token::Match(attr, "[(,] unused|__unused__|used|__used__ [,)]")) {
94139413
Token *vartok = getVariableTokenAfterAttributes(tok);
9414+
if (!vartok)
9415+
vartok = functok;
94149416
if (vartok) {
94159417
const std::string &attribute(attr->strAt(1));
94169418
if (attribute.find("unused") != std::string::npos)
@@ -9526,7 +9528,7 @@ void Tokenizer::simplifyCPPAttribute()
95269528
head = skipCPPOrAlignAttribute(head)->next();
95279529
while (Token::Match(head, "%name%|::|*|&|<|>|,")) // skip return type
95289530
head = head->next();
9529-
if (head && head->str() == "(" && TokenList::isFunctionHead(head, "{;")) {
9531+
if (head && head->str() == "(" && (TokenList::isFunctionHead(head, "{;") || Token::simpleMatch(head->link(), ") __attribute__"))) {
95309532
head->previous()->isAttributeNoreturn(true);
95319533
}
95329534
} else if (Token::findsimplematch(tok->tokAt(2), "nodiscard", tok->link())) {

0 commit comments

Comments
 (0)