/src/cppcheck/lib/ctu.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 "ctu.h" |
22 | | |
23 | | #include "astutils.h" |
24 | | #include "errortypes.h" |
25 | | #include "settings.h" |
26 | | #include "symboldatabase.h" |
27 | | #include "token.h" |
28 | | #include "tokenize.h" |
29 | | #include "tokenlist.h" |
30 | | #include "utils.h" |
31 | | |
32 | | #include <algorithm> |
33 | | #include <cstdint> |
34 | | #include <cstring> |
35 | | #include <iterator> // back_inserter |
36 | | #include <sstream> |
37 | | #include <utility> |
38 | | |
39 | | #include <tinyxml2.h> |
40 | | //--------------------------------------------------------------------------- |
41 | | |
42 | | static const char ATTR_CALL_ID[] = "call-id"; |
43 | | static const char ATTR_CALL_FUNCNAME[] = "call-funcname"; |
44 | | static const char ATTR_CALL_ARGNR[] = "call-argnr"; |
45 | | static const char ATTR_CALL_ARGEXPR[] = "call-argexpr"; |
46 | | static const char ATTR_CALL_ARGVALUETYPE[] = "call-argvaluetype"; |
47 | | static const char ATTR_CALL_ARGVALUE[] = "call-argvalue"; |
48 | | static const char ATTR_WARNING[] = "warning"; |
49 | | static const char ATTR_LOC_FILENAME[] = "file"; |
50 | | static const char ATTR_LOC_LINENR[] = "line"; |
51 | | static const char ATTR_LOC_COLUMN[] = "col"; |
52 | | static const char ATTR_INFO[] = "info"; |
53 | | static const char ATTR_MY_ID[] = "my-id"; |
54 | | static const char ATTR_MY_ARGNR[] = "my-argnr"; |
55 | | static const char ATTR_MY_ARGNAME[] = "my-argname"; |
56 | | static const char ATTR_VALUE[] = "value"; |
57 | | |
58 | | int CTU::maxCtuDepth = 2; |
59 | | |
60 | | std::string CTU::getFunctionId(const Tokenizer *tokenizer, const Function *function) |
61 | 0 | { |
62 | 0 | return tokenizer->list.file(function->tokenDef) + ':' + std::to_string(function->tokenDef->linenr()) + ':' + std::to_string(function->tokenDef->column()); |
63 | 0 | } |
64 | | |
65 | | CTU::FileInfo::Location::Location(const Tokenizer *tokenizer, const Token *tok) |
66 | | : fileName(tokenizer->list.file(tok)) |
67 | | , lineNumber(tok->linenr()) |
68 | | , column(tok->column()) |
69 | 0 | {} |
70 | | |
71 | | std::string CTU::FileInfo::toString() const |
72 | 0 | { |
73 | 0 | std::ostringstream out; |
74 | | |
75 | | // Function calls.. |
76 | 0 | for (const CTU::FileInfo::FunctionCall &functionCall : functionCalls) { |
77 | 0 | out << functionCall.toXmlString(); |
78 | 0 | } |
79 | | |
80 | | // Nested calls.. |
81 | 0 | for (const CTU::FileInfo::NestedCall &nestedCall : nestedCalls) { |
82 | 0 | out << nestedCall.toXmlString() << "\n"; |
83 | 0 | } |
84 | |
|
85 | 0 | return out.str(); |
86 | 0 | } |
87 | | |
88 | | std::string CTU::FileInfo::CallBase::toBaseXmlString() const |
89 | 0 | { |
90 | 0 | std::ostringstream out; |
91 | 0 | out << " " << ATTR_CALL_ID << "=\"" << callId << "\"" |
92 | 0 | << " " << ATTR_CALL_FUNCNAME << "=\"" << ErrorLogger::toxml(callFunctionName) << "\"" |
93 | 0 | << " " << ATTR_CALL_ARGNR << "=\"" << callArgNr << "\"" |
94 | 0 | << " " << ATTR_LOC_FILENAME << "=\"" << ErrorLogger::toxml(location.fileName) << "\"" |
95 | 0 | << " " << ATTR_LOC_LINENR << "=\"" << location.lineNumber << "\"" |
96 | 0 | << " " << ATTR_LOC_COLUMN << "=\"" << location.column << "\""; |
97 | 0 | return out.str(); |
98 | 0 | } |
99 | | |
100 | | std::string CTU::FileInfo::FunctionCall::toXmlString() const |
101 | 0 | { |
102 | 0 | std::ostringstream out; |
103 | 0 | out << "<function-call" |
104 | 0 | << toBaseXmlString() |
105 | 0 | << " " << ATTR_CALL_ARGEXPR << "=\"" << ErrorLogger::toxml(callArgumentExpression) << "\"" |
106 | 0 | << " " << ATTR_CALL_ARGVALUETYPE << "=\"" << static_cast<int>(callValueType) << "\"" |
107 | 0 | << " " << ATTR_CALL_ARGVALUE << "=\"" << callArgValue << "\""; |
108 | 0 | if (warning) |
109 | 0 | out << " " << ATTR_WARNING << "=\"true\""; |
110 | 0 | if (callValuePath.empty()) |
111 | 0 | out << "/>"; |
112 | 0 | else { |
113 | 0 | out << ">\n"; |
114 | 0 | for (const ErrorMessage::FileLocation &loc : callValuePath) |
115 | 0 | out << " <path" |
116 | 0 | << " " << ATTR_LOC_FILENAME << "=\"" << ErrorLogger::toxml(loc.getfile()) << "\"" |
117 | 0 | << " " << ATTR_LOC_LINENR << "=\"" << loc.line << "\"" |
118 | 0 | << " " << ATTR_LOC_COLUMN << "=\"" << loc.column << "\"" |
119 | 0 | << " " << ATTR_INFO << "=\"" << ErrorLogger::toxml(loc.getinfo()) << "\"/>\n"; |
120 | 0 | out << "</function-call>"; |
121 | 0 | } |
122 | 0 | return out.str(); |
123 | 0 | } |
124 | | |
125 | | std::string CTU::FileInfo::NestedCall::toXmlString() const |
126 | 0 | { |
127 | 0 | std::ostringstream out; |
128 | 0 | out << "<function-call" |
129 | 0 | << toBaseXmlString() |
130 | 0 | << " " << ATTR_MY_ID << "=\"" << myId << "\"" |
131 | 0 | << " " << ATTR_MY_ARGNR << "=\"" << myArgNr << "\"" |
132 | 0 | << "/>"; |
133 | 0 | return out.str(); |
134 | 0 | } |
135 | | |
136 | | std::string CTU::FileInfo::UnsafeUsage::toString() const |
137 | 0 | { |
138 | 0 | std::ostringstream out; |
139 | 0 | out << " <unsafe-usage" |
140 | 0 | << " " << ATTR_MY_ID << "=\"" << myId << '\"' |
141 | 0 | << " " << ATTR_MY_ARGNR << "=\"" << myArgNr << '\"' |
142 | 0 | << " " << ATTR_MY_ARGNAME << "=\"" << myArgumentName << '\"' |
143 | 0 | << " " << ATTR_LOC_FILENAME << "=\"" << ErrorLogger::toxml(location.fileName) << '\"' |
144 | 0 | << " " << ATTR_LOC_LINENR << "=\"" << location.lineNumber << '\"' |
145 | 0 | << " " << ATTR_LOC_COLUMN << "=\"" << location.column << '\"' |
146 | 0 | << " " << ATTR_VALUE << "=\"" << value << "\"" |
147 | 0 | << "/>\n"; |
148 | 0 | return out.str(); |
149 | 0 | } |
150 | | |
151 | | std::string CTU::toString(const std::list<CTU::FileInfo::UnsafeUsage> &unsafeUsage) |
152 | 0 | { |
153 | 0 | std::ostringstream ret; |
154 | 0 | for (const CTU::FileInfo::UnsafeUsage &u : unsafeUsage) |
155 | 0 | ret << u.toString(); |
156 | 0 | return ret.str(); |
157 | 0 | } |
158 | | |
159 | | CTU::FileInfo::CallBase::CallBase(const Tokenizer *tokenizer, const Token *callToken) |
160 | | : callId(getFunctionId(tokenizer, callToken->function())) |
161 | | , callFunctionName(callToken->next()->astOperand1()->expressionString()) |
162 | | , location(CTU::FileInfo::Location(tokenizer, callToken)) |
163 | 0 | {} |
164 | | |
165 | | CTU::FileInfo::NestedCall::NestedCall(const Tokenizer *tokenizer, const Function *myFunction, const Token *callToken) |
166 | | : CallBase(tokenizer, callToken) |
167 | | , myId(getFunctionId(tokenizer, myFunction)) |
168 | 0 | {} |
169 | | |
170 | | static std::string readAttrString(const tinyxml2::XMLElement *e, const char *attr, bool *error) |
171 | 0 | { |
172 | 0 | const char *value = e->Attribute(attr); |
173 | 0 | if (!value && error) |
174 | 0 | *error = true; |
175 | 0 | return value ? value : ""; |
176 | 0 | } |
177 | | |
178 | | static long long readAttrInt(const tinyxml2::XMLElement *e, const char *attr, bool *error) |
179 | 0 | { |
180 | 0 | int64_t value = 0; |
181 | 0 | const bool err = (e->QueryInt64Attribute(attr, &value) != tinyxml2::XML_SUCCESS); |
182 | 0 | if (error) |
183 | 0 | *error = err; |
184 | 0 | return value; |
185 | 0 | } |
186 | | |
187 | | bool CTU::FileInfo::CallBase::loadBaseFromXml(const tinyxml2::XMLElement *xmlElement) |
188 | 0 | { |
189 | 0 | bool error = false; |
190 | 0 | callId = readAttrString(xmlElement, ATTR_CALL_ID, &error); |
191 | 0 | callFunctionName = readAttrString(xmlElement, ATTR_CALL_FUNCNAME, &error); |
192 | 0 | callArgNr = readAttrInt(xmlElement, ATTR_CALL_ARGNR, &error); |
193 | 0 | location.fileName = readAttrString(xmlElement, ATTR_LOC_FILENAME, &error); |
194 | 0 | location.lineNumber = readAttrInt(xmlElement, ATTR_LOC_LINENR, &error); |
195 | 0 | location.column = readAttrInt(xmlElement, ATTR_LOC_COLUMN, &error); |
196 | 0 | return !error; |
197 | 0 | } |
198 | | |
199 | | bool CTU::FileInfo::FunctionCall::loadFromXml(const tinyxml2::XMLElement *xmlElement) |
200 | 0 | { |
201 | 0 | if (!loadBaseFromXml(xmlElement)) |
202 | 0 | return false; |
203 | 0 | bool error=false; |
204 | 0 | callArgumentExpression = readAttrString(xmlElement, ATTR_CALL_ARGEXPR, &error); |
205 | 0 | callValueType = (ValueFlow::Value::ValueType)readAttrInt(xmlElement, ATTR_CALL_ARGVALUETYPE, &error); |
206 | 0 | callArgValue = readAttrInt(xmlElement, ATTR_CALL_ARGVALUE, &error); |
207 | 0 | const char *w = xmlElement->Attribute(ATTR_WARNING); |
208 | 0 | warning = w && std::strcmp(w, "true") == 0; |
209 | 0 | for (const tinyxml2::XMLElement *e2 = xmlElement->FirstChildElement(); !error && e2; e2 = e2->NextSiblingElement()) { |
210 | 0 | if (std::strcmp(e2->Name(), "path") != 0) |
211 | 0 | continue; |
212 | 0 | ErrorMessage::FileLocation loc; |
213 | 0 | loc.setfile(readAttrString(e2, ATTR_LOC_FILENAME, &error)); |
214 | 0 | loc.line = readAttrInt(e2, ATTR_LOC_LINENR, &error); |
215 | 0 | loc.column = readAttrInt(e2, ATTR_LOC_COLUMN, &error); |
216 | 0 | loc.setinfo(readAttrString(e2, ATTR_INFO, &error)); |
217 | 0 | } |
218 | 0 | return !error; |
219 | 0 | } |
220 | | |
221 | | bool CTU::FileInfo::NestedCall::loadFromXml(const tinyxml2::XMLElement *xmlElement) |
222 | 0 | { |
223 | 0 | if (!loadBaseFromXml(xmlElement)) |
224 | 0 | return false; |
225 | 0 | bool error = false; |
226 | 0 | myId = readAttrString(xmlElement, ATTR_MY_ID, &error); |
227 | 0 | myArgNr = readAttrInt(xmlElement, ATTR_MY_ARGNR, &error); |
228 | 0 | return !error; |
229 | 0 | } |
230 | | |
231 | | void CTU::FileInfo::loadFromXml(const tinyxml2::XMLElement *xmlElement) |
232 | 0 | { |
233 | 0 | for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { |
234 | 0 | if (std::strcmp(e->Name(), "function-call") == 0) { |
235 | 0 | FunctionCall functionCall; |
236 | 0 | if (functionCall.loadFromXml(e)) |
237 | 0 | functionCalls.push_back(std::move(functionCall)); |
238 | 0 | } else if (std::strcmp(e->Name(), "nested-call") == 0) { |
239 | 0 | NestedCall nestedCall; |
240 | 0 | if (nestedCall.loadFromXml(e)) |
241 | 0 | nestedCalls.push_back(std::move(nestedCall)); |
242 | 0 | } |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | | std::map<std::string, std::list<const CTU::FileInfo::CallBase *>> CTU::FileInfo::getCallsMap() const |
247 | 0 | { |
248 | 0 | std::map<std::string, std::list<const CTU::FileInfo::CallBase *>> ret; |
249 | 0 | for (const CTU::FileInfo::NestedCall &nc : nestedCalls) |
250 | 0 | ret[nc.callId].push_back(&nc); |
251 | 0 | for (const CTU::FileInfo::FunctionCall &fc : functionCalls) |
252 | 0 | ret[fc.callId].push_back(&fc); |
253 | 0 | return ret; |
254 | 0 | } |
255 | | |
256 | | std::list<CTU::FileInfo::UnsafeUsage> CTU::loadUnsafeUsageListFromXml(const tinyxml2::XMLElement *xmlElement) |
257 | 0 | { |
258 | 0 | std::list<CTU::FileInfo::UnsafeUsage> ret; |
259 | 0 | for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { |
260 | 0 | if (std::strcmp(e->Name(), "unsafe-usage") != 0) |
261 | 0 | continue; |
262 | 0 | bool error = false; |
263 | 0 | FileInfo::UnsafeUsage unsafeUsage; |
264 | 0 | unsafeUsage.myId = readAttrString(e, ATTR_MY_ID, &error); |
265 | 0 | unsafeUsage.myArgNr = readAttrInt(e, ATTR_MY_ARGNR, &error); |
266 | 0 | unsafeUsage.myArgumentName = readAttrString(e, ATTR_MY_ARGNAME, &error); |
267 | 0 | unsafeUsage.location.fileName = readAttrString(e, ATTR_LOC_FILENAME, &error); |
268 | 0 | unsafeUsage.location.lineNumber = readAttrInt(e, ATTR_LOC_LINENR, &error); |
269 | 0 | unsafeUsage.location.column = readAttrInt(e, ATTR_LOC_COLUMN, &error); |
270 | 0 | unsafeUsage.value = readAttrInt(e, ATTR_VALUE, &error); |
271 | |
|
272 | 0 | if (!error) |
273 | 0 | ret.push_back(std::move(unsafeUsage)); |
274 | 0 | } |
275 | 0 | return ret; |
276 | 0 | } |
277 | | |
278 | | static int isCallFunction(const Scope *scope, int argnr, const Token **tok) |
279 | 0 | { |
280 | 0 | const Variable * const argvar = scope->function->getArgumentVar(argnr); |
281 | 0 | if (!argvar->isPointer()) |
282 | 0 | return -1; |
283 | 0 | for (const Token *tok2 = scope->bodyStart; tok2 != scope->bodyEnd; tok2 = tok2->next()) { |
284 | 0 | if (tok2->variable() != argvar) |
285 | 0 | continue; |
286 | 0 | if (!Token::Match(tok2->previous(), "[(,] %var% [,)]")) |
287 | 0 | break; |
288 | 0 | int argnr2 = 1; |
289 | 0 | const Token *prev = tok2; |
290 | 0 | while (prev && prev->str() != "(") { |
291 | 0 | if (Token::Match(prev,"]|)")) |
292 | 0 | prev = prev->link(); |
293 | 0 | else if (prev->str() == ",") |
294 | 0 | ++argnr2; |
295 | 0 | prev = prev->previous(); |
296 | 0 | } |
297 | 0 | if (!prev || !Token::Match(prev->previous(), "%name% (")) |
298 | 0 | break; |
299 | 0 | if (!prev->astOperand1() || !prev->astOperand1()->function()) |
300 | 0 | break; |
301 | 0 | *tok = prev->previous(); |
302 | 0 | return argnr2; |
303 | 0 | } |
304 | 0 | return -1; |
305 | 0 | } |
306 | | |
307 | | |
308 | | CTU::FileInfo *CTU::getFileInfo(const Tokenizer *tokenizer) |
309 | 1.36k | { |
310 | 1.36k | const SymbolDatabase * const symbolDatabase = tokenizer->getSymbolDatabase(); |
311 | | |
312 | 1.36k | FileInfo *fileInfo = new FileInfo; |
313 | | |
314 | | // Parse all functions in TU |
315 | 4.94k | for (const Scope &scope : symbolDatabase->scopeList) { |
316 | 4.94k | if (!scope.isExecutable() || scope.type != Scope::eFunction || !scope.function) |
317 | 3.20k | continue; |
318 | 1.73k | const Function *const scopeFunction = scope.function; |
319 | | |
320 | | // source function calls |
321 | 37.6k | for (const Token *tok = scope.bodyStart; tok != scope.bodyEnd; tok = tok->next()) { |
322 | 35.9k | if (tok->str() != "(" || !tok->astOperand1() || !tok->astOperand2()) |
323 | 34.5k | continue; |
324 | 1.38k | const Function* tokFunction = tok->astOperand1()->function(); |
325 | 1.38k | if (!tokFunction) |
326 | 1.38k | continue; |
327 | 0 | const std::vector<const Token *> args(getArguments(tok->previous())); |
328 | 0 | for (int argnr = 0; argnr < args.size(); ++argnr) { |
329 | 0 | const Token *argtok = args[argnr]; |
330 | 0 | if (!argtok) |
331 | 0 | continue; |
332 | 0 | for (const ValueFlow::Value &value : argtok->values()) { |
333 | 0 | if ((!value.isIntValue() || value.intvalue != 0 || value.isInconclusive()) && !value.isBufferSizeValue()) |
334 | 0 | continue; |
335 | | // Skip impossible values since they cannot be represented |
336 | 0 | if (value.isImpossible()) |
337 | 0 | continue; |
338 | 0 | FileInfo::FunctionCall functionCall; |
339 | 0 | functionCall.callValueType = value.valueType; |
340 | 0 | functionCall.callId = getFunctionId(tokenizer, tokFunction); |
341 | 0 | functionCall.callFunctionName = tok->astOperand1()->expressionString(); |
342 | 0 | functionCall.location = FileInfo::Location(tokenizer,tok); |
343 | 0 | functionCall.callArgNr = argnr + 1; |
344 | 0 | functionCall.callArgumentExpression = argtok->expressionString(); |
345 | 0 | functionCall.callArgValue = value.intvalue; |
346 | 0 | functionCall.warning = !value.errorSeverity(); |
347 | 0 | for (const ErrorPathItem &i : value.errorPath) { |
348 | 0 | ErrorMessage::FileLocation loc; |
349 | 0 | loc.setfile(tokenizer->list.file(i.first)); |
350 | 0 | loc.line = i.first->linenr(); |
351 | 0 | loc.column = i.first->column(); |
352 | 0 | loc.setinfo(i.second); |
353 | 0 | functionCall.callValuePath.push_back(std::move(loc)); |
354 | 0 | } |
355 | 0 | fileInfo->functionCalls.push_back(std::move(functionCall)); |
356 | 0 | } |
357 | | // array |
358 | 0 | if (argtok->variable() && argtok->variable()->isArray() && argtok->variable()->dimensions().size() == 1 && argtok->variable()->dimensionKnown(0)) { |
359 | 0 | FileInfo::FunctionCall functionCall; |
360 | 0 | functionCall.callValueType = ValueFlow::Value::ValueType::BUFFER_SIZE; |
361 | 0 | functionCall.callId = getFunctionId(tokenizer, tokFunction); |
362 | 0 | functionCall.callFunctionName = tok->astOperand1()->expressionString(); |
363 | 0 | functionCall.location = FileInfo::Location(tokenizer, tok); |
364 | 0 | functionCall.callArgNr = argnr + 1; |
365 | 0 | functionCall.callArgumentExpression = argtok->expressionString(); |
366 | 0 | const auto typeSize = argtok->valueType()->typeSize(tokenizer->getSettings()->platform); |
367 | 0 | functionCall.callArgValue = typeSize > 0 ? argtok->variable()->dimension(0) * typeSize : -1; |
368 | 0 | functionCall.warning = false; |
369 | 0 | fileInfo->functionCalls.push_back(std::move(functionCall)); |
370 | 0 | } |
371 | | // &var => buffer |
372 | 0 | if (argtok->isUnaryOp("&") && argtok->astOperand1()->variable() && argtok->astOperand1()->valueType() && !argtok->astOperand1()->variable()->isArray()) { |
373 | 0 | FileInfo::FunctionCall functionCall; |
374 | 0 | functionCall.callValueType = ValueFlow::Value::ValueType::BUFFER_SIZE; |
375 | 0 | functionCall.callId = getFunctionId(tokenizer, tokFunction); |
376 | 0 | functionCall.callFunctionName = tok->astOperand1()->expressionString(); |
377 | 0 | functionCall.location = FileInfo::Location(tokenizer, tok); |
378 | 0 | functionCall.callArgNr = argnr + 1; |
379 | 0 | functionCall.callArgumentExpression = argtok->expressionString(); |
380 | 0 | functionCall.callArgValue = argtok->astOperand1()->valueType()->typeSize(tokenizer->getSettings()->platform); |
381 | 0 | functionCall.warning = false; |
382 | 0 | fileInfo->functionCalls.push_back(std::move(functionCall)); |
383 | 0 | } |
384 | | // pointer/reference to uninitialized data |
385 | 0 | auto isAddressOfArg = [](const Token* argtok) -> const Token* { |
386 | 0 | if (!argtok->isUnaryOp("&")) |
387 | 0 | return nullptr; |
388 | 0 | argtok = argtok->astOperand1(); |
389 | 0 | if (!argtok || !argtok->valueType() || argtok->valueType()->pointer != 0) |
390 | 0 | return nullptr; |
391 | 0 | return argtok; |
392 | 0 | }; |
393 | 0 | auto isReferenceArg = [&](const Token* argtok) -> const Token* { |
394 | 0 | const Variable* argvar = tokFunction->getArgumentVar(argnr); |
395 | 0 | if (!argvar || !argvar->valueType() || argvar->valueType()->reference == Reference::None) |
396 | 0 | return nullptr; |
397 | 0 | return argtok; |
398 | 0 | }; |
399 | 0 | const Token* addr = isAddressOfArg(argtok); |
400 | 0 | argtok = addr ? addr : isReferenceArg(argtok); |
401 | 0 | if (!argtok || argtok->values().size() != 1U) |
402 | 0 | continue; |
403 | 0 | if (argtok->variable() && argtok->variable()->isClass()) |
404 | 0 | continue; |
405 | | |
406 | 0 | const ValueFlow::Value &v = argtok->values().front(); |
407 | 0 | if (v.valueType == ValueFlow::Value::ValueType::UNINIT && !v.isInconclusive()) { |
408 | 0 | FileInfo::FunctionCall functionCall; |
409 | 0 | functionCall.callValueType = ValueFlow::Value::ValueType::UNINIT; |
410 | 0 | functionCall.callId = getFunctionId(tokenizer, tokFunction); |
411 | 0 | functionCall.callFunctionName = tok->astOperand1()->expressionString(); |
412 | 0 | functionCall.location = FileInfo::Location(tokenizer, tok); |
413 | 0 | functionCall.callArgNr = argnr + 1; |
414 | 0 | functionCall.callArgValue = 0; |
415 | 0 | functionCall.callArgumentExpression = argtok->expressionString(); |
416 | 0 | functionCall.warning = false; |
417 | 0 | fileInfo->functionCalls.push_back(std::move(functionCall)); |
418 | 0 | continue; |
419 | 0 | } |
420 | 0 | } |
421 | 0 | } |
422 | | |
423 | | // Nested function calls |
424 | 1.73k | for (int argnr = 0; argnr < scopeFunction->argCount(); ++argnr) { |
425 | 0 | const Token *tok; |
426 | 0 | const int argnr2 = isCallFunction(&scope, argnr, &tok); |
427 | 0 | if (argnr2 > 0) { |
428 | 0 | FileInfo::NestedCall nestedCall(tokenizer, scopeFunction, tok); |
429 | 0 | nestedCall.myArgNr = argnr + 1; |
430 | 0 | nestedCall.callArgNr = argnr2; |
431 | 0 | fileInfo->nestedCalls.push_back(std::move(nestedCall)); |
432 | 0 | } |
433 | 0 | } |
434 | 1.73k | } |
435 | | |
436 | 1.36k | return fileInfo; |
437 | 1.36k | } |
438 | | |
439 | | static std::list<std::pair<const Token *, MathLib::bigint>> getUnsafeFunction(const Tokenizer *tokenizer, const Settings *settings, const Scope *scope, int argnr, const Check *check, bool (*isUnsafeUsage)(const Check *check, const Token *argtok, MathLib::bigint *value)) |
440 | 0 | { |
441 | 0 | std::list<std::pair<const Token *, MathLib::bigint>> ret; |
442 | 0 | const Variable * const argvar = scope->function->getArgumentVar(argnr); |
443 | 0 | if (!argvar->isArrayOrPointer() && !argvar->isReference()) |
444 | 0 | return ret; |
445 | 0 | for (const Token *tok2 = scope->bodyStart; tok2 != scope->bodyEnd; tok2 = tok2->next()) { |
446 | 0 | if (Token::Match(tok2, ")|else {")) { |
447 | 0 | tok2 = tok2->linkAt(1); |
448 | 0 | if (Token::findmatch(tok2->link(), "return|throw", tok2)) |
449 | 0 | return ret; |
450 | 0 | int indirect = 0; |
451 | 0 | if (argvar->valueType()) |
452 | 0 | indirect = argvar->valueType()->pointer; |
453 | 0 | if (isVariableChanged(tok2->link(), tok2, indirect, argvar->declarationId(), false, settings, tokenizer->isCPP())) |
454 | 0 | return ret; |
455 | 0 | } |
456 | 0 | if (Token::Match(tok2, "%oror%|&&|?")) { |
457 | 0 | tok2 = tok2->findExpressionStartEndTokens().second; |
458 | 0 | continue; |
459 | 0 | } |
460 | 0 | if (tok2->variable() != argvar) |
461 | 0 | continue; |
462 | 0 | MathLib::bigint value = 0; |
463 | 0 | if (!isUnsafeUsage(check, tok2, &value)) |
464 | 0 | return ret; // TODO: Is this a read? then continue.. |
465 | 0 | ret.emplace_back(tok2, value); |
466 | 0 | return ret; |
467 | 0 | } |
468 | 0 | return ret; |
469 | 0 | } |
470 | | |
471 | | std::list<CTU::FileInfo::UnsafeUsage> CTU::getUnsafeUsage(const Tokenizer *tokenizer, const Settings *settings, const Check *check, bool (*isUnsafeUsage)(const Check *check, const Token *argtok, MathLib::bigint *value)) |
472 | 5.44k | { |
473 | 5.44k | std::list<CTU::FileInfo::UnsafeUsage> unsafeUsage; |
474 | | |
475 | | // Parse all functions in TU |
476 | 5.44k | const SymbolDatabase * const symbolDatabase = tokenizer->getSymbolDatabase(); |
477 | | |
478 | 19.7k | for (const Scope &scope : symbolDatabase->scopeList) { |
479 | 19.7k | if (!scope.isExecutable() || scope.type != Scope::eFunction || !scope.function) |
480 | 12.8k | continue; |
481 | 6.95k | const Function *const function = scope.function; |
482 | | |
483 | | // "Unsafe" functions unconditionally reads data before it is written.. |
484 | 6.95k | for (int argnr = 0; argnr < function->argCount(); ++argnr) { |
485 | 0 | for (const std::pair<const Token *, MathLib::bigint> &v : getUnsafeFunction(tokenizer, settings, &scope, argnr, check, isUnsafeUsage)) { |
486 | 0 | const Token *tok = v.first; |
487 | 0 | const MathLib::bigint val = v.second; |
488 | 0 | unsafeUsage.emplace_back(CTU::getFunctionId(tokenizer, function), argnr+1, tok->str(), CTU::FileInfo::Location(tokenizer,tok), val); |
489 | 0 | } |
490 | 0 | } |
491 | 6.95k | } |
492 | | |
493 | 5.44k | return unsafeUsage; |
494 | 5.44k | } |
495 | | |
496 | | static bool findPath(const std::string &callId, |
497 | | nonneg int callArgNr, |
498 | | MathLib::bigint unsafeValue, |
499 | | CTU::FileInfo::InvalidValueType invalidValue, |
500 | | const std::map<std::string, std::list<const CTU::FileInfo::CallBase *>> &callsMap, |
501 | | const CTU::FileInfo::CallBase *path[10], |
502 | | int index, |
503 | | bool warning) |
504 | 0 | { |
505 | 0 | if (index >= CTU::maxCtuDepth || index >= 10) |
506 | 0 | return false; |
507 | | |
508 | 0 | const std::map<std::string, std::list<const CTU::FileInfo::CallBase *>>::const_iterator it = callsMap.find(callId); |
509 | 0 | if (it == callsMap.end()) |
510 | 0 | return false; |
511 | | |
512 | 0 | for (const CTU::FileInfo::CallBase *c : it->second) { |
513 | 0 | if (c->callArgNr != callArgNr) |
514 | 0 | continue; |
515 | | |
516 | 0 | const CTU::FileInfo::FunctionCall *functionCall = dynamic_cast<const CTU::FileInfo::FunctionCall *>(c); |
517 | 0 | if (functionCall) { |
518 | 0 | if (!warning && functionCall->warning) |
519 | 0 | continue; |
520 | 0 | switch (invalidValue) { |
521 | 0 | case CTU::FileInfo::InvalidValueType::null: |
522 | 0 | if (functionCall->callValueType != ValueFlow::Value::ValueType::INT || functionCall->callArgValue != 0) |
523 | 0 | continue; |
524 | 0 | break; |
525 | 0 | case CTU::FileInfo::InvalidValueType::uninit: |
526 | 0 | if (functionCall->callValueType != ValueFlow::Value::ValueType::UNINIT) |
527 | 0 | continue; |
528 | 0 | break; |
529 | 0 | case CTU::FileInfo::InvalidValueType::bufferOverflow: |
530 | 0 | if (functionCall->callValueType != ValueFlow::Value::ValueType::BUFFER_SIZE) |
531 | 0 | continue; |
532 | 0 | if (unsafeValue < 0 || (unsafeValue >= functionCall->callArgValue && functionCall->callArgValue >= 0)) |
533 | 0 | break; |
534 | 0 | continue; |
535 | 0 | } |
536 | 0 | path[index] = functionCall; |
537 | 0 | return true; |
538 | 0 | } |
539 | | |
540 | 0 | const CTU::FileInfo::NestedCall *nestedCall = dynamic_cast<const CTU::FileInfo::NestedCall *>(c); |
541 | 0 | if (!nestedCall) |
542 | 0 | continue; |
543 | | |
544 | 0 | if (findPath(nestedCall->myId, nestedCall->myArgNr, unsafeValue, invalidValue, callsMap, path, index + 1, warning)) { |
545 | 0 | path[index] = nestedCall; |
546 | 0 | return true; |
547 | 0 | } |
548 | 0 | } |
549 | | |
550 | 0 | return false; |
551 | 0 | } |
552 | | |
553 | | std::list<ErrorMessage::FileLocation> CTU::FileInfo::getErrorPath(InvalidValueType invalidValue, |
554 | | const CTU::FileInfo::UnsafeUsage &unsafeUsage, |
555 | | const std::map<std::string, std::list<const CTU::FileInfo::CallBase *>> &callsMap, |
556 | | const char info[], |
557 | | const FunctionCall ** const functionCallPtr, |
558 | | bool warning) |
559 | 0 | { |
560 | 0 | std::list<ErrorMessage::FileLocation> locationList; |
561 | |
|
562 | 0 | const CTU::FileInfo::CallBase *path[10] = {nullptr}; |
563 | |
|
564 | 0 | if (!findPath(unsafeUsage.myId, unsafeUsage.myArgNr, unsafeUsage.value, invalidValue, callsMap, path, 0, warning)) |
565 | 0 | return locationList; |
566 | | |
567 | 0 | const std::string value1 = (invalidValue == InvalidValueType::null) ? "null" : "uninitialized"; |
568 | |
|
569 | 0 | for (int index = 9; index >= 0; index--) { |
570 | 0 | if (!path[index]) |
571 | 0 | continue; |
572 | | |
573 | 0 | const CTU::FileInfo::FunctionCall *functionCall = dynamic_cast<const CTU::FileInfo::FunctionCall *>(path[index]); |
574 | |
|
575 | 0 | if (functionCall) { |
576 | 0 | if (functionCallPtr) |
577 | 0 | *functionCallPtr = functionCall; |
578 | 0 | std::copy(functionCall->callValuePath.cbegin(), functionCall->callValuePath.cend(), std::back_inserter(locationList)); |
579 | 0 | } |
580 | |
|
581 | 0 | ErrorMessage::FileLocation fileLoc(path[index]->location.fileName, path[index]->location.lineNumber, path[index]->location.column); |
582 | 0 | fileLoc.setinfo("Calling function " + path[index]->callFunctionName + ", " + std::to_string(path[index]->callArgNr) + getOrdinalText(path[index]->callArgNr) + " argument is " + value1); |
583 | 0 | locationList.push_back(std::move(fileLoc)); |
584 | 0 | } |
585 | |
|
586 | 0 | ErrorMessage::FileLocation fileLoc2(unsafeUsage.location.fileName, unsafeUsage.location.lineNumber, unsafeUsage.location.column); |
587 | 0 | fileLoc2.setinfo(replaceStr(info, "ARG", unsafeUsage.myArgumentName)); |
588 | 0 | locationList.push_back(std::move(fileLoc2)); |
589 | |
|
590 | 0 | return locationList; |
591 | 0 | } |