|
| 1 | +/* |
| 2 | + * Cppcheck - A tool for static C/C++ code analysis |
| 3 | + * Copyright (C) 2007-2025 Cppcheck team. |
| 4 | + * |
| 5 | + * This program is free software: you can redistribute it and/or modify |
| 6 | + * it under the terms of the GNU General Public License as published by |
| 7 | + * the Free Software Foundation, either version 3 of the License, or |
| 8 | + * (at your option) any later version. |
| 9 | + * |
| 10 | + * This program is distributed in the hope that it will be useful, |
| 11 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | + * GNU General Public License for more details. |
| 14 | + * |
| 15 | + * You should have received a copy of the GNU General Public License |
| 16 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 17 | + */ |
| 18 | + |
| 19 | +#include "checkunionzeroinit.h" |
| 20 | + |
| 21 | +#include <sstream> |
| 22 | +#include <unordered_map> |
| 23 | +#include <vector> |
| 24 | + |
| 25 | +#include "errortypes.h" |
| 26 | +#include "settings.h" |
| 27 | +#include "symboldatabase.h" |
| 28 | +#include "token.h" |
| 29 | +#include "tokenize.h" |
| 30 | +#include "valueflow.h" |
| 31 | + |
| 32 | +static const CWE CWEXXX(0U); /* TODO */ |
| 33 | + |
| 34 | +static const std::string noname; |
| 35 | + |
| 36 | +// Register this check class (by creating a static instance of it) |
| 37 | +namespace { |
| 38 | + CheckUnionZeroInit instance; |
| 39 | +} |
| 40 | + |
| 41 | +struct UnionMember { |
| 42 | + UnionMember() |
| 43 | + : name(noname) |
| 44 | + , size(0) {} |
| 45 | + |
| 46 | + UnionMember(const std::string &name_, size_t size_) |
| 47 | + : name(name_) |
| 48 | + , size(size_) {} |
| 49 | + |
| 50 | + const std::string &name; |
| 51 | + size_t size; |
| 52 | +}; |
| 53 | + |
| 54 | +struct Union { |
| 55 | + Union(const Scope *scope_, const std::string &name_) |
| 56 | + : scope(scope_) |
| 57 | + , name(name_) {} |
| 58 | + |
| 59 | + const Scope *scope; |
| 60 | + const std::string &name; |
| 61 | + std::vector<UnionMember> members; |
| 62 | + |
| 63 | + const UnionMember *largestMember() const { |
| 64 | + const UnionMember *largest = nullptr; |
| 65 | + for (const UnionMember &m : members) { |
| 66 | + if (m.size == 0) |
| 67 | + return nullptr; |
| 68 | + if (largest == nullptr || m.size > largest->size) |
| 69 | + largest = &m; |
| 70 | + } |
| 71 | + return largest; |
| 72 | + } |
| 73 | + |
| 74 | + bool isLargestMemberFirst() const { |
| 75 | + const UnionMember *largest = largestMember(); |
| 76 | + return largest == nullptr || largest == &members[0]; |
| 77 | + } |
| 78 | +}; |
| 79 | + |
| 80 | +static UnionMember parseUnionMember(const Variable &var, |
| 81 | + const Settings &settings) |
| 82 | +{ |
| 83 | + const Token *nameToken = var.nameToken(); |
| 84 | + if (nameToken == nullptr) |
| 85 | + return UnionMember(); |
| 86 | + |
| 87 | + const ValueType *vt = nameToken->valueType(); |
| 88 | + size_t size = 0; |
| 89 | + if (var.isArray()) { |
| 90 | + size = var.dimension(0); |
| 91 | + } else if (vt != nullptr) { |
| 92 | + size = ValueFlow::getSizeOf(*vt, settings, |
| 93 | + ValueFlow::Accuracy::ExactOrZero); |
| 94 | + } |
| 95 | + return UnionMember(nameToken->str(), size); |
| 96 | +} |
| 97 | + |
| 98 | +static std::vector<Union> parseUnions(const SymbolDatabase &symbolDatabase, |
| 99 | + const Settings &settings) |
| 100 | +{ |
| 101 | + std::vector<Union> unions; |
| 102 | + |
| 103 | + for (const Scope &scope : symbolDatabase.scopeList) { |
| 104 | + if (scope.type != ScopeType::eUnion) |
| 105 | + continue; |
| 106 | + |
| 107 | + Union u(&scope, scope.className); |
| 108 | + for (const Variable &var : scope.varlist) { |
| 109 | + u.members.push_back(parseUnionMember(var, settings)); |
| 110 | + } |
| 111 | + unions.push_back(u); |
| 112 | + } |
| 113 | + |
| 114 | + return unions; |
| 115 | +} |
| 116 | + |
| 117 | +static bool isZeroInitializer(const Token *tok) |
| 118 | +{ |
| 119 | + return Token::simpleMatch(tok, "= { 0 } ;") || |
| 120 | + Token::simpleMatch(tok, "= { } ;"); |
| 121 | +} |
| 122 | + |
| 123 | +void CheckUnionZeroInit::check() |
| 124 | +{ |
| 125 | + if (!mSettings->severity.isEnabled(Severity::portability)) |
| 126 | + return; |
| 127 | + |
| 128 | + logChecker("CheckUnionZeroInit::check"); // portability |
| 129 | + |
| 130 | + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
| 131 | + |
| 132 | + std::unordered_map<const Scope *, Union> unionsByScopeId; |
| 133 | + const std::vector<Union> unions = parseUnions(*symbolDatabase, *mSettings); |
| 134 | + for (const Union &u : unions) { |
| 135 | + unionsByScopeId.insert({u.scope, u}); |
| 136 | + } |
| 137 | + |
| 138 | + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
| 139 | + if (!tok->isName() || !isZeroInitializer(tok->next())) |
| 140 | + continue; |
| 141 | + |
| 142 | + const ValueType *vt = tok->valueType(); |
| 143 | + if (vt == nullptr || vt->typeScope == nullptr) |
| 144 | + continue; |
| 145 | + auto it = unionsByScopeId.find(vt->typeScope); |
| 146 | + if (it == unionsByScopeId.end()) |
| 147 | + continue; |
| 148 | + const Union &u = it->second; |
| 149 | + if (!u.isLargestMemberFirst()) { |
| 150 | + const UnionMember *largestMember = u.largestMember(); |
| 151 | + assert(largestMember != nullptr); |
| 152 | + unionZeroInitError(tok, *largestMember); |
| 153 | + } |
| 154 | + } |
| 155 | +} |
| 156 | + |
| 157 | +void CheckUnionZeroInit::runChecks(const Tokenizer &tokenizer, |
| 158 | + ErrorLogger *errorLogger) |
| 159 | +{ |
| 160 | + CheckUnionZeroInit(&tokenizer, &tokenizer.getSettings(), errorLogger).check(); |
| 161 | +} |
| 162 | + |
| 163 | +void CheckUnionZeroInit::unionZeroInitError(const Token *tok, |
| 164 | + const UnionMember& largestMember) |
| 165 | +{ |
| 166 | + reportError(tok, Severity::portability, "UnionZeroInit", |
| 167 | + "$symbol:" + (tok != nullptr ? tok->str() : "") + "\n" |
| 168 | + "Zero initializing union '$symbol' does not guarantee " + |
| 169 | + "its complete storage to be zero initialized as its largest member " + |
| 170 | + "is not declared as the first member. Consider making " + |
| 171 | + largestMember.name + " the first member or favor memset().", |
| 172 | + CWEXXX, Certainty::normal); |
| 173 | +} |
| 174 | + |
| 175 | +void CheckUnionZeroInit::getErrorMessages(ErrorLogger *errorLogger, |
| 176 | + const Settings *settings) const |
| 177 | +{ |
| 178 | + CheckUnionZeroInit c(nullptr, settings, errorLogger); |
| 179 | + c.unionZeroInitError(nullptr, UnionMember()); |
| 180 | +} |
| 181 | + |
| 182 | +std::string CheckUnionZeroInit::generateTestMessage(const Tokenizer &tokenizer, |
| 183 | + const Settings &settings) |
| 184 | +{ |
| 185 | + std::stringstream ss; |
| 186 | + |
| 187 | + const std::vector<Union> unions = parseUnions(*tokenizer.getSymbolDatabase(), |
| 188 | + settings); |
| 189 | + for (const Union &u : unions) { |
| 190 | + ss << "Union{"; |
| 191 | + ss << "name=\"" << u.name << "\", "; |
| 192 | + ss << "scope=" << u.scope << ", "; |
| 193 | + ss << "isLargestMemberFirst=" << u.isLargestMemberFirst(); |
| 194 | + ss << "}" << std::endl; |
| 195 | + |
| 196 | + const UnionMember *largest = u.largestMember(); |
| 197 | + for (const UnionMember &m : u.members) { |
| 198 | + ss << " Member{"; |
| 199 | + ss << "name=\"" << m.name << "\", "; |
| 200 | + ss << "size=" << m.size; |
| 201 | + ss << "}"; |
| 202 | + if (&m == largest) |
| 203 | + ss << " (largest)"; |
| 204 | + ss << std::endl; |
| 205 | + } |
| 206 | + } |
| 207 | + |
| 208 | + return ss.str(); |
| 209 | +} |
0 commit comments