Fix #10720 Hang/Crash with big variadic template (#5018)

* Fix #10720 Hang/Crash with big variadic template

* Fix CI
This commit is contained in:
chrchr-github 2023-04-28 08:26:35 +02:00 committed by GitHub
parent 77717f73fd
commit 9c184462ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 14 deletions

View File

@ -805,7 +805,7 @@ test/testsimplifytokens.o: test/testsimplifytokens.cpp lib/check.h lib/color.h l
test/testsimplifytypedef.o: test/testsimplifytypedef.cpp externals/simplecpp/simplecpp.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsimplifytypedef.cpp
test/testsimplifyusing.o: test/testsimplifyusing.cpp lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h
test/testsimplifyusing.o: test/testsimplifyusing.cpp externals/simplecpp/simplecpp.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsimplifyusing.cpp
test/testsingleexecutor.o: test/testsingleexecutor.cpp cli/executor.h cli/singleexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h test/redirect.h

View File

@ -2760,11 +2760,23 @@ static bool scopesMatch(const std::string &scope1, const std::string &scope2, co
return false;
}
static unsigned int tokDistance(const Token* tok1, const Token* tok2) {
unsigned int dist = 0;
const Token* tok = tok1;
while (tok != tok2) {
++dist;
tok = tok->next();
}
return dist;
};
bool Tokenizer::simplifyUsing()
{
if (!isCPP() || mSettings->standards.cpp < Standards::CPP11)
return false;
const unsigned int maxReplacementTokens = 1000; // limit the number of tokens we replace
bool substitute = false;
ScopeInfo3 scopeInfo;
ScopeInfo3 *currentScope = &scopeInfo;
@ -3006,6 +3018,12 @@ bool Tokenizer::simplifyUsing()
} else if (!usingMatch(nameToken, scope, &tok1, scope1, currentScope1, nullptr))
continue;
const auto nReplace = tokDistance(start, usingEnd);
if (nReplace > maxReplacementTokens) {
simplifyUsingError(usingStart, usingEnd);
continue;
}
// remove the qualification
std::string fullScope = scope;
std::string removed;
@ -3187,18 +3205,7 @@ bool Tokenizer::simplifyUsing()
}
} else {
skip = true;
if (mSettings->debugwarnings && mErrorLogger) {
std::string str;
for (Token *tok3 = usingStart; tok3 && tok3 != usingEnd; tok3 = tok3->next()) {
if (!str.empty())
str += ' ';
str += tok3->str();
}
str += " ;";
std::list<const Token *> callstack(1, usingStart);
mErrorLogger->reportErr(ErrorMessage(callstack, &list, Severity::debug, "simplifyUsing",
"Failed to parse \'" + str + "\'. The checking continues anyway.", Certainty::normal));
}
simplifyUsingError(usingStart, usingEnd);
}
tok1 = after;
}
@ -3233,6 +3240,22 @@ bool Tokenizer::simplifyUsing()
return substitute;
}
void Tokenizer::simplifyUsingError(const Token* usingStart, const Token* usingEnd)
{
if (mSettings->debugwarnings && mErrorLogger) {
std::string str;
for (const Token *tok = usingStart; tok && tok != usingEnd; tok = tok->next()) {
if (!str.empty())
str += ' ';
str += tok->str();
}
str += " ;";
std::list<const Token *> callstack(1, usingStart);
mErrorLogger->reportErr(ErrorMessage(callstack, &list, Severity::debug, "simplifyUsing",
"Failed to parse \'" + str + "\'. The checking continues anyway.", Certainty::normal));
}
}
bool Tokenizer::createTokens(std::istream &code,
const std::string& FileName)
{

View File

@ -280,6 +280,7 @@ public:
/**
*/
bool simplifyUsing();
void simplifyUsingError(const Token* usingStart, const Token* usingEnd);
/** Simplify useless C++ empty namespaces, like: 'namespace %name% { }'*/
void simplifyEmptyNamespaces();

View File

@ -24,6 +24,8 @@
#include "token.h"
#include "tokenize.h"
#include <simplecpp.h>
#include <sstream> // IWYU pragma: keep
#include <string>
@ -92,13 +94,14 @@ private:
TEST_CASE(simplifyUsing10172);
TEST_CASE(simplifyUsing10173);
TEST_CASE(simplifyUsing10335);
TEST_CASE(simplifyUsing10720);
TEST_CASE(scopeInfo1);
TEST_CASE(scopeInfo2);
}
#define tok(...) tok_(__FILE__, __LINE__, __VA_ARGS__)
std::string tok_(const char* file, int line, const char code[], cppcheck::Platform::Type type = cppcheck::Platform::Type::Native, bool debugwarnings = true) {
std::string tok_(const char* file, int line, const char code[], cppcheck::Platform::Type type = cppcheck::Platform::Type::Native, bool debugwarnings = true, bool preprocess = false) {
errout.str("");
settings0.certainty.enable(Certainty::inconclusive);
@ -106,6 +109,18 @@ private:
PLATFORM(settings0.platform, type);
Tokenizer tokenizer(&settings0, this);
if (preprocess) {
std::vector<std::string> files{ "test.cpp" };
std::istringstream istr(code);
const simplecpp::TokenList tokens1(istr, files, files[0]);
simplecpp::TokenList tokens2(files);
std::map<std::string, simplecpp::TokenList*> filedata;
simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI());
tokenizer.createTokens(std::move(tokens2));
}
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
@ -1346,6 +1361,17 @@ private:
ASSERT_EQUALS(exp, tok(code));
}
void simplifyUsing10720() {
const char code[] = "template <typename... Ts>\n"
"struct S {};\n"
"#define STAMP(thiz, prev) using thiz = S<prev, prev, prev, prev, prev, prev, prev, prev, prev, prev>;\n"
"STAMP(A, int);\n"
"STAMP(B, A);\n"
"STAMP(C, B);\n";
tok(code, cppcheck::Platform::Type::Native, /*debugwarnings*/ true, /*preprocess*/ true);
ASSERT_EQUALS(errout.str().compare(0, 64, "[test.cpp:6]: (debug) Failed to parse 'using C = S < S < S < int"), 0);
}
void scopeInfo1() {
const char code[] = "struct A {\n"
" enum class Mode { UNKNOWN, ENABLED, NONE, };\n"