/src/cppcheck/lib/checksizeof.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Cppcheck - A tool for static C/C++ code analysis |
3 | | * Copyright (C) 2007-2023 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 | | |
20 | | //--------------------------------------------------------------------------- |
21 | | #include "checksizeof.h" |
22 | | |
23 | | #include "errortypes.h" |
24 | | #include "library.h" |
25 | | #include "settings.h" |
26 | | #include "symboldatabase.h" |
27 | | #include "token.h" |
28 | | #include "tokenize.h" |
29 | | |
30 | | #include <algorithm> |
31 | | #include <iterator> |
32 | | #include <map> |
33 | | #include <vector> |
34 | | |
35 | | //--------------------------------------------------------------------------- |
36 | | |
37 | | // Register this check class (by creating a static instance of it) |
38 | | namespace { |
39 | | CheckSizeof instance; |
40 | | } |
41 | | |
42 | | // CWE IDs used: |
43 | | static const struct CWE CWE398(398U); // Indicator of Poor Code Quality |
44 | | static const struct CWE CWE467(467U); // Use of sizeof() on a Pointer Type |
45 | | static const struct CWE CWE682(682U); // Incorrect Calculation |
46 | | //--------------------------------------------------------------------------- |
47 | | //--------------------------------------------------------------------------- |
48 | | void CheckSizeof::checkSizeofForNumericParameter() |
49 | 1.36k | { |
50 | 1.36k | if (!mSettings->severity.isEnabled(Severity::warning)) |
51 | 0 | return; |
52 | | |
53 | 1.36k | logChecker("CheckSizeof::checkSizeofForNumericParameter"); // warning |
54 | | |
55 | 1.36k | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
56 | 1.73k | for (const Scope * scope : symbolDatabase->functionScopes) { |
57 | 35.9k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
58 | 34.1k | if (Token::Match(tok, "sizeof ( %num% )") || |
59 | 34.1k | Token::Match(tok, "sizeof %num%")) { |
60 | 0 | sizeofForNumericParameterError(tok); |
61 | 0 | } |
62 | 34.1k | } |
63 | 1.73k | } |
64 | 1.36k | } |
65 | | |
66 | | void CheckSizeof::sizeofForNumericParameterError(const Token *tok) |
67 | 0 | { |
68 | 0 | reportError(tok, Severity::warning, |
69 | 0 | "sizeofwithnumericparameter", "Suspicious usage of 'sizeof' with a numeric constant as parameter.\n" |
70 | 0 | "It is unusual to use a constant value with sizeof. For example, 'sizeof(10)'" |
71 | 0 | " returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 10. 'sizeof('A')'" |
72 | 0 | " and 'sizeof(char)' can return different results.", CWE682, Certainty::normal); |
73 | 0 | } |
74 | | |
75 | | |
76 | | //--------------------------------------------------------------------------- |
77 | | //--------------------------------------------------------------------------- |
78 | | void CheckSizeof::checkSizeofForArrayParameter() |
79 | 1.36k | { |
80 | 1.36k | if (!mSettings->severity.isEnabled(Severity::warning)) |
81 | 0 | return; |
82 | | |
83 | 1.36k | logChecker("CheckSizeof::checkSizeofForArrayParameter"); // warning |
84 | | |
85 | 1.36k | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
86 | 1.73k | for (const Scope * scope : symbolDatabase->functionScopes) { |
87 | 35.9k | for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { |
88 | 34.1k | if (Token::Match(tok, "sizeof ( %var% )") || |
89 | 34.1k | Token::Match(tok, "sizeof %var% !![")) { |
90 | 0 | const Token* varTok = tok->next(); |
91 | 0 | if (varTok->str() == "(") { |
92 | 0 | varTok = varTok->next(); |
93 | 0 | } |
94 | |
|
95 | 0 | const Variable *var = varTok->variable(); |
96 | 0 | if (var && var->isArray() && var->isArgument() && !var->isReference()) |
97 | 0 | sizeofForArrayParameterError(tok); |
98 | 0 | } |
99 | 34.1k | } |
100 | 1.73k | } |
101 | 1.36k | } |
102 | | |
103 | | void CheckSizeof::sizeofForArrayParameterError(const Token *tok) |
104 | 0 | { |
105 | 0 | reportError(tok, Severity::warning, |
106 | 0 | "sizeofwithsilentarraypointer", "Using 'sizeof' on array given as function argument " |
107 | 0 | "returns size of a pointer.\n" |
108 | 0 | "Using 'sizeof' for array given as function argument returns the size of a pointer. " |
109 | 0 | "It does not return the size of the whole array in bytes as might be " |
110 | 0 | "expected. For example, this code:\n" |
111 | 0 | " int f(char a[100]) {\n" |
112 | 0 | " return sizeof(a);\n" |
113 | 0 | " }\n" |
114 | 0 | "returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 100 (the " |
115 | 0 | "size of the array in bytes).", CWE467, Certainty::normal |
116 | 0 | ); |
117 | 0 | } |
118 | | |
119 | | void CheckSizeof::checkSizeofForPointerSize() |
120 | 1.36k | { |
121 | 1.36k | if (!mSettings->severity.isEnabled(Severity::warning)) |
122 | 0 | return; |
123 | | |
124 | 1.36k | logChecker("CheckSizeof::checkSizeofForPointerSize"); // warning |
125 | | |
126 | 1.36k | const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); |
127 | 1.73k | for (const Scope * scope : symbolDatabase->functionScopes) { |
128 | 37.6k | for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { |
129 | 35.9k | const Token* tokSize; |
130 | 35.9k | const Token* tokFunc; |
131 | 35.9k | const Token *variable = nullptr; |
132 | 35.9k | const Token *variable2 = nullptr; |
133 | | |
134 | | // Find any function that may use sizeof on a pointer |
135 | | // Once leaving those tests, it is mandatory to have: |
136 | | // - variable matching the used pointer |
137 | | // - tokVar pointing on the argument where sizeof may be used |
138 | 35.9k | if (Token::Match(tok->tokAt(2), "%name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2))) { |
139 | 0 | if (Token::Match(tok, "%var% =")) |
140 | 0 | variable = tok; |
141 | 0 | else if (tok->strAt(1) == ")" && Token::Match(tok->linkAt(1)->tokAt(-2), "%var% =")) |
142 | 0 | variable = tok->linkAt(1)->tokAt(-2); |
143 | 0 | else if (tok->link() && Token::Match(tok, "> ( %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)) && Token::Match(tok->link()->tokAt(-3), "%var% =")) |
144 | 0 | variable = tok->link()->tokAt(-3); |
145 | 0 | tokSize = tok->tokAt(4); |
146 | 0 | tokFunc = tok->tokAt(2); |
147 | 35.9k | } else if (Token::simpleMatch(tok, "memset (") && tok->strAt(-1) != ".") { |
148 | 0 | variable = tok->tokAt(2); |
149 | 0 | tokSize = variable->nextArgument(); |
150 | 0 | if (tokSize) |
151 | 0 | tokSize = tokSize->nextArgument(); |
152 | 0 | tokFunc = tok; |
153 | 35.9k | } else if (Token::Match(tok, "memcpy|memcmp|memmove|strncpy|strncmp|strncat (") && tok->strAt(-1) != ".") { |
154 | 0 | variable = tok->tokAt(2); |
155 | 0 | variable2 = variable->nextArgument(); |
156 | 0 | if (!variable2) |
157 | 0 | continue; |
158 | 0 | tokSize = variable2->nextArgument(); |
159 | 0 | tokFunc = tok; |
160 | 35.9k | } else { |
161 | 35.9k | continue; |
162 | 35.9k | } |
163 | | |
164 | 0 | if (tokSize && tokFunc->str() == "calloc") |
165 | 0 | tokSize = tokSize->nextArgument(); |
166 | |
|
167 | 0 | if (tokSize) { |
168 | 0 | const Token * const paramsListEndTok = tokFunc->linkAt(1); |
169 | 0 | for (const Token* tok2 = tokSize; tok2 != paramsListEndTok; tok2 = tok2->next()) { |
170 | 0 | if (Token::simpleMatch(tok2, "/ sizeof")) { |
171 | | // Allow division with sizeof(char) |
172 | 0 | if (Token::simpleMatch(tok2->next(), "sizeof (")) { |
173 | 0 | const Token *sztok = tok2->tokAt(2)->astOperand2(); |
174 | 0 | const ValueType *vt = ((sztok != nullptr) ? sztok->valueType() : nullptr); |
175 | 0 | if (vt && vt->type == ValueType::CHAR && vt->pointer == 0) |
176 | 0 | continue; |
177 | 0 | } |
178 | 0 | auto hasMultiplication = [](const Token* parTok) -> bool { |
179 | 0 | while (parTok) { // Allow division if followed by multiplication |
180 | 0 | if (parTok->isArithmeticalOp() && parTok->str() == "*") { |
181 | 0 | const Token* szToks[] = { parTok->astOperand1(), parTok->astOperand2() }; |
182 | 0 | if (std::any_of(std::begin(szToks), std::end(szToks), [](const Token* szTok) { |
183 | 0 | return Token::simpleMatch(szTok, "(") && Token::simpleMatch(szTok->previous(), "sizeof"); |
184 | 0 | })) |
185 | 0 | return true; |
186 | 0 | } |
187 | 0 | parTok = parTok->astParent(); |
188 | 0 | } |
189 | 0 | return false; |
190 | 0 | }; |
191 | 0 | if (hasMultiplication(tok2->astParent())) |
192 | 0 | continue; |
193 | | |
194 | 0 | divideBySizeofError(tok2, tokFunc->str()); |
195 | 0 | } |
196 | 0 | } |
197 | 0 | } |
198 | |
|
199 | 0 | if (!variable || !tokSize) |
200 | 0 | continue; |
201 | | |
202 | 0 | while (Token::Match(variable, "%var% ::|.")) |
203 | 0 | variable = variable->tokAt(2); |
204 | |
|
205 | 0 | while (Token::Match(variable2, "%var% ::|.")) |
206 | 0 | variable2 = variable2->tokAt(2); |
207 | |
|
208 | 0 | if (!variable) |
209 | 0 | continue; |
210 | | |
211 | | // Ensure the variables are in the symbol database |
212 | | // Also ensure the variables are pointers |
213 | | // Only keep variables which are pointers |
214 | 0 | const Variable *var = variable->variable(); |
215 | 0 | if (!var || !var->isPointer() || var->isArray()) { |
216 | 0 | variable = nullptr; |
217 | 0 | } |
218 | |
|
219 | 0 | if (variable2) { |
220 | 0 | var = variable2->variable(); |
221 | 0 | if (!var || !var->isPointer() || var->isArray()) { |
222 | 0 | variable2 = nullptr; |
223 | 0 | } |
224 | 0 | } |
225 | | |
226 | | // If there are no pointer variable at this point, there is |
227 | | // no need to continue |
228 | 0 | if (variable == nullptr && variable2 == nullptr) { |
229 | 0 | continue; |
230 | 0 | } |
231 | | |
232 | | // Jump to the next sizeof token in the function and in the parameter |
233 | | // This is to allow generic operations with sizeof |
234 | 0 | for (; tokSize && tokSize->str() != ")" && tokSize->str() != "," && tokSize->str() != "sizeof"; tokSize = tokSize->next()) {} |
235 | |
|
236 | 0 | if (tokSize->str() != "sizeof") |
237 | 0 | continue; |
238 | | |
239 | | // Now check for the sizeof usage: Does the level of pointer indirection match? |
240 | 0 | if (tokSize->linkAt(1)->strAt(-1) == "*") { |
241 | 0 | if (variable && variable->valueType() && variable->valueType()->pointer == 1 && variable->valueType()->type != ValueType::VOID) |
242 | 0 | sizeofForPointerError(variable, variable->str()); |
243 | 0 | else if (variable2 && variable2->valueType() && variable2->valueType()->pointer == 1 && variable2->valueType()->type != ValueType::VOID) |
244 | 0 | sizeofForPointerError(variable2, variable2->str()); |
245 | 0 | } |
246 | |
|
247 | 0 | if (Token::simpleMatch(tokSize, "sizeof ( &")) |
248 | 0 | tokSize = tokSize->tokAt(3); |
249 | 0 | else if (Token::Match(tokSize, "sizeof (|&")) |
250 | 0 | tokSize = tokSize->tokAt(2); |
251 | 0 | else |
252 | 0 | tokSize = tokSize->next(); |
253 | |
|
254 | 0 | while (Token::Match(tokSize, "%var% ::|.")) |
255 | 0 | tokSize = tokSize->tokAt(2); |
256 | |
|
257 | 0 | if (Token::Match(tokSize, "%var% [|(")) |
258 | 0 | continue; |
259 | | |
260 | | // Now check for the sizeof usage again. Once here, everything using sizeof(varid) or sizeof(&varid) |
261 | | // looks suspicious |
262 | 0 | if (variable && tokSize->varId() == variable->varId()) |
263 | 0 | sizeofForPointerError(variable, variable->str()); |
264 | 0 | if (variable2 && tokSize->varId() == variable2->varId()) |
265 | 0 | sizeofForPointerError(variable2, variable2->str()); |
266 | 0 | } |
267 | 1.73k | } |
268 | 1.36k | } |
269 | | |
270 | | void CheckSizeof::sizeofForPointerError(const Token *tok, const std::string &varname) |
271 | 0 | { |
272 | 0 | reportError(tok, Severity::warning, "pointerSize", |
273 | 0 | "Size of pointer '" + varname + "' used instead of size of its data.\n" |
274 | 0 | "Size of pointer '" + varname + "' used instead of size of its data. " |
275 | 0 | "This is likely to lead to a buffer overflow. You probably intend to " |
276 | 0 | "write 'sizeof(*" + varname + ")'.", CWE467, Certainty::normal); |
277 | 0 | } |
278 | | |
279 | | void CheckSizeof::divideBySizeofError(const Token *tok, const std::string &memfunc) |
280 | 0 | { |
281 | 0 | reportError(tok, Severity::warning, "sizeofDivisionMemfunc", |
282 | 0 | "Division by result of sizeof(). " + memfunc + "() expects a size in bytes, did you intend to multiply instead?", CWE682, Certainty::normal); |
283 | 0 | } |
284 | | |
285 | | //----------------------------------------------------------------------------- |
286 | | //----------------------------------------------------------------------------- |
287 | | void CheckSizeof::sizeofsizeof() |
288 | 1.36k | { |
289 | 1.36k | if (!mSettings->severity.isEnabled(Severity::warning)) |
290 | 0 | return; |
291 | | |
292 | 1.36k | logChecker("CheckSizeof::sizeofsizeof"); // warning |
293 | | |
294 | 93.5k | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
295 | 92.2k | if (Token::Match(tok, "sizeof (| sizeof")) { |
296 | 0 | sizeofsizeofError(tok); |
297 | 0 | tok = tok->next(); |
298 | 0 | } |
299 | 92.2k | } |
300 | 1.36k | } |
301 | | |
302 | | void CheckSizeof::sizeofsizeofError(const Token *tok) |
303 | 0 | { |
304 | 0 | reportError(tok, Severity::warning, |
305 | 0 | "sizeofsizeof", "Calling 'sizeof' on 'sizeof'.\n" |
306 | 0 | "Calling sizeof for 'sizeof looks like a suspicious code and " |
307 | 0 | "most likely there should be just one 'sizeof'. The current " |
308 | 0 | "code is equivalent to 'sizeof(size_t)'", CWE682, Certainty::normal); |
309 | 0 | } |
310 | | |
311 | | //----------------------------------------------------------------------------- |
312 | | |
313 | | void CheckSizeof::sizeofCalculation() |
314 | 1.36k | { |
315 | 1.36k | if (!mSettings->severity.isEnabled(Severity::warning)) |
316 | 0 | return; |
317 | | |
318 | 1.36k | logChecker("CheckSizeof::sizeofCalculation"); // warning |
319 | | |
320 | 1.36k | const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); |
321 | | |
322 | 93.5k | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
323 | 92.2k | if (!Token::simpleMatch(tok, "sizeof (")) |
324 | 92.2k | continue; |
325 | | |
326 | | // ignore if the `sizeof` result is cast to void inside a macro, i.e. the calculation is |
327 | | // expected to be parsed but skipped, such as in a disabled custom ASSERT() macro |
328 | 0 | if (tok->isExpandedMacro() && tok->previous()) { |
329 | 0 | const Token *cast_end = (tok->previous()->str() == "(") ? tok->previous() : tok; |
330 | 0 | if (Token::simpleMatch(cast_end->tokAt(-3), "( void )") || |
331 | 0 | Token::simpleMatch(cast_end->tokAt(-4), "static_cast < void >")) { |
332 | 0 | continue; |
333 | 0 | } |
334 | 0 | } |
335 | | |
336 | 0 | const Token *argument = tok->next()->astOperand2(); |
337 | 0 | if (!argument || !argument->isCalculation()) |
338 | 0 | continue; |
339 | | |
340 | 0 | bool inconclusive = false; |
341 | 0 | if (argument->isExpandedMacro()) |
342 | 0 | inconclusive = true; |
343 | 0 | else if (tok->next()->isExpandedMacro()) |
344 | 0 | inconclusive = true; |
345 | |
|
346 | 0 | if (!inconclusive || printInconclusive) |
347 | 0 | sizeofCalculationError(argument, inconclusive); |
348 | 0 | } |
349 | 1.36k | } |
350 | | |
351 | | void CheckSizeof::sizeofCalculationError(const Token *tok, bool inconclusive) |
352 | 0 | { |
353 | 0 | reportError(tok, Severity::warning, |
354 | 0 | "sizeofCalculation", "Found calculation inside sizeof().", CWE682, inconclusive ? Certainty::inconclusive : Certainty::normal); |
355 | 0 | } |
356 | | |
357 | | //----------------------------------------------------------------------------- |
358 | | |
359 | | void CheckSizeof::sizeofFunction() |
360 | 1.36k | { |
361 | 1.36k | if (!mSettings->severity.isEnabled(Severity::warning)) |
362 | 0 | return; |
363 | | |
364 | 1.36k | logChecker("CheckSizeof::sizeofFunction"); // warning |
365 | | |
366 | 93.5k | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
367 | 92.2k | if (Token::simpleMatch(tok, "sizeof (")) { |
368 | | |
369 | | // ignore if the `sizeof` result is cast to void inside a macro, i.e. the calculation is |
370 | | // expected to be parsed but skipped, such as in a disabled custom ASSERT() macro |
371 | 0 | if (tok->isExpandedMacro() && tok->previous()) { |
372 | 0 | const Token *cast_end = (tok->previous()->str() == "(") ? tok->previous() : tok; |
373 | 0 | if (Token::simpleMatch(cast_end->tokAt(-3), "( void )") || |
374 | 0 | Token::simpleMatch(cast_end->tokAt(-4), "static_cast < void >")) { |
375 | 0 | continue; |
376 | 0 | } |
377 | 0 | } |
378 | | |
379 | 0 | if (const Token *argument = tok->next()->astOperand2()) { |
380 | 0 | const Token *checkToken = argument->previous(); |
381 | 0 | if (checkToken->tokType() == Token::eName) |
382 | 0 | break; |
383 | 0 | const Function * fun = checkToken->function(); |
384 | | // Don't report error if the function is overloaded |
385 | 0 | if (fun && fun->nestedIn->functionMap.count(checkToken->str()) == 1) { |
386 | 0 | sizeofFunctionError(tok); |
387 | 0 | } |
388 | 0 | } |
389 | 0 | } |
390 | 92.2k | } |
391 | 1.36k | } |
392 | | |
393 | | void CheckSizeof::sizeofFunctionError(const Token *tok) |
394 | 0 | { |
395 | 0 | reportError(tok, Severity::warning, |
396 | 0 | "sizeofFunctionCall", "Found function call inside sizeof().", CWE682, Certainty::normal); |
397 | 0 | } |
398 | | |
399 | | //----------------------------------------------------------------------------- |
400 | | // Check for code like sizeof()*sizeof() or sizeof(ptr)/value |
401 | | //----------------------------------------------------------------------------- |
402 | | void CheckSizeof::suspiciousSizeofCalculation() |
403 | 1.36k | { |
404 | 1.36k | if (!mSettings->severity.isEnabled(Severity::warning) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) |
405 | 0 | return; |
406 | | |
407 | 1.36k | logChecker("CheckSizeof::suspiciousSizeofCalculation"); // warning,inconclusive |
408 | | |
409 | 93.5k | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
410 | 92.2k | if (Token::simpleMatch(tok, "sizeof (")) { |
411 | 0 | const Token* lPar = tok->astParent(); |
412 | 0 | if (lPar && lPar->str() == "(") { |
413 | 0 | const Token* const rPar = lPar->link(); |
414 | 0 | const Token* varTok = lPar->astOperand2(); |
415 | 0 | int derefCount = 0; |
416 | 0 | while (Token::Match(varTok, "[|*")) { |
417 | 0 | ++derefCount; |
418 | 0 | varTok = varTok->astOperand1(); |
419 | 0 | } |
420 | 0 | if (lPar->astParent() && lPar->astParent()->str() == "/") { |
421 | 0 | const Variable* var = varTok ? varTok->variable() : nullptr; |
422 | 0 | if (var && var->isPointer() && !var->isArray() && !(var->valueType() && var->valueType()->pointer <= derefCount)) |
423 | 0 | divideSizeofError(tok); |
424 | 0 | } |
425 | 0 | else if (Token::simpleMatch(rPar, ") * sizeof") && rPar->next()->astOperand1() == tok->next()) |
426 | 0 | multiplySizeofError(tok); |
427 | 0 | } |
428 | 0 | } |
429 | 92.2k | } |
430 | 1.36k | } |
431 | | |
432 | | void CheckSizeof::multiplySizeofError(const Token *tok) |
433 | 0 | { |
434 | 0 | reportError(tok, Severity::warning, |
435 | 0 | "multiplySizeof", "Multiplying sizeof() with sizeof() indicates a logic error.", CWE682, Certainty::inconclusive); |
436 | 0 | } |
437 | | |
438 | | void CheckSizeof::divideSizeofError(const Token *tok) |
439 | 0 | { |
440 | 0 | reportError(tok, Severity::warning, |
441 | 0 | "divideSizeof", "Division of result of sizeof() on pointer type.\n" |
442 | 0 | "Division of result of sizeof() on pointer type. sizeof() returns the size of the pointer, " |
443 | 0 | "not the size of the memory area it points to.", CWE682, Certainty::inconclusive); |
444 | 0 | } |
445 | | |
446 | | void CheckSizeof::sizeofVoid() |
447 | 1.36k | { |
448 | 1.36k | if (!mSettings->severity.isEnabled(Severity::portability)) |
449 | 0 | return; |
450 | | |
451 | 1.36k | logChecker("CheckSizeof::sizeofVoid"); // portability |
452 | | |
453 | 93.5k | for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { |
454 | 92.2k | if (Token::simpleMatch(tok, "sizeof ( void )")) { |
455 | 0 | sizeofVoidError(tok); |
456 | 92.2k | } else if (Token::simpleMatch(tok, "sizeof (") && tok->next()->astOperand2()) { |
457 | 0 | const ValueType *vt = tok->next()->astOperand2()->valueType(); |
458 | 0 | if (vt && vt->type == ValueType::Type::VOID && vt->pointer == 0U) |
459 | 0 | sizeofDereferencedVoidPointerError(tok, tok->strAt(3)); |
460 | 92.2k | } else if (tok->str() == "-") { |
461 | | // only warn for: 'void *' - 'integral' |
462 | 248 | const ValueType *vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; |
463 | 248 | const ValueType *vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; |
464 | 248 | const bool op1IsvoidPointer = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U); |
465 | 248 | const bool op2IsIntegral = (vt2 && vt2->isIntegral() && vt2->pointer == 0U); |
466 | 248 | if (op1IsvoidPointer && op2IsIntegral) |
467 | 0 | arithOperationsOnVoidPointerError(tok, tok->astOperand1()->expressionString(), vt1->str()); |
468 | 91.9k | } else if (Token::Match(tok, "+|++|--|+=|-=")) { // Arithmetic operations on variable of type "void*" |
469 | 1.72k | const ValueType *vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; |
470 | 1.72k | const ValueType *vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; |
471 | | |
472 | 1.72k | const bool voidpointer1 = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U); |
473 | 1.72k | const bool voidpointer2 = (vt2 && vt2->type == ValueType::Type::VOID && vt2->pointer == 1U); |
474 | | |
475 | 1.72k | if (voidpointer1) |
476 | 0 | arithOperationsOnVoidPointerError(tok, tok->astOperand1()->expressionString(), vt1->str()); |
477 | | |
478 | 1.72k | if (!tok->isAssignmentOp() && voidpointer2) |
479 | 0 | arithOperationsOnVoidPointerError(tok, tok->astOperand2()->expressionString(), vt2->str()); |
480 | 1.72k | } |
481 | 92.2k | } |
482 | 1.36k | } |
483 | | |
484 | | void CheckSizeof::sizeofVoidError(const Token *tok) |
485 | 0 | { |
486 | 0 | const std::string message = "Behaviour of 'sizeof(void)' is not covered by the ISO C standard."; |
487 | 0 | const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1."; |
488 | 0 | reportError(tok, Severity::portability, "sizeofVoid", message + "\n" + verbose, CWE682, Certainty::normal); |
489 | 0 | } |
490 | | |
491 | | void CheckSizeof::sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname) |
492 | 0 | { |
493 | 0 | const std::string message = "'*" + varname + "' is of type 'void', the behaviour of 'sizeof(void)' is not covered by the ISO C standard."; |
494 | 0 | const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1."; |
495 | 0 | reportError(tok, Severity::portability, "sizeofDereferencedVoidPointer", message + "\n" + verbose, CWE682, Certainty::normal); |
496 | 0 | } |
497 | | |
498 | | void CheckSizeof::arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname, const std::string &vartype) |
499 | 0 | { |
500 | 0 | const std::string message = "'$symbol' is of type '" + vartype + "'. When using void pointers in calculations, the behaviour is undefined."; |
501 | 0 | const std::string verbose = message + " Arithmetic operations on 'void *' is a GNU C extension, which defines the 'sizeof(void)' to be 1."; |
502 | 0 | reportError(tok, Severity::portability, "arithOperationsOnVoidPointer", "$symbol:" + varname + '\n' + message + '\n' + verbose, CWE467, Certainty::normal); |
503 | 0 | } |