Skip to content

Commit 9c18446

Browse files
Fix #10720 Hang/Crash with big variadic template (#5018)
* Fix #10720 Hang/Crash with big variadic template * Fix CI
1 parent 77717f7 commit 9c18446

4 files changed

Lines changed: 64 additions & 14 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,7 @@ test/testsimplifytokens.o: test/testsimplifytokens.cpp lib/check.h lib/color.h l
805805
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
806806
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsimplifytypedef.cpp
807807

808-
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
808+
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
809809
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsimplifyusing.cpp
810810

811811
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

lib/tokenize.cpp

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2760,11 +2760,23 @@ static bool scopesMatch(const std::string &scope1, const std::string &scope2, co
27602760
return false;
27612761
}
27622762

2763+
static unsigned int tokDistance(const Token* tok1, const Token* tok2) {
2764+
unsigned int dist = 0;
2765+
const Token* tok = tok1;
2766+
while (tok != tok2) {
2767+
++dist;
2768+
tok = tok->next();
2769+
}
2770+
return dist;
2771+
};
2772+
27632773
bool Tokenizer::simplifyUsing()
27642774
{
27652775
if (!isCPP() || mSettings->standards.cpp < Standards::CPP11)
27662776
return false;
27672777

2778+
const unsigned int maxReplacementTokens = 1000; // limit the number of tokens we replace
2779+
27682780
bool substitute = false;
27692781
ScopeInfo3 scopeInfo;
27702782
ScopeInfo3 *currentScope = &scopeInfo;
@@ -3006,6 +3018,12 @@ bool Tokenizer::simplifyUsing()
30063018
} else if (!usingMatch(nameToken, scope, &tok1, scope1, currentScope1, nullptr))
30073019
continue;
30083020

3021+
const auto nReplace = tokDistance(start, usingEnd);
3022+
if (nReplace > maxReplacementTokens) {
3023+
simplifyUsingError(usingStart, usingEnd);
3024+
continue;
3025+
}
3026+
30093027
// remove the qualification
30103028
std::string fullScope = scope;
30113029
std::string removed;
@@ -3187,18 +3205,7 @@ bool Tokenizer::simplifyUsing()
31873205
}
31883206
} else {
31893207
skip = true;
3190-
if (mSettings->debugwarnings && mErrorLogger) {
3191-
std::string str;
3192-
for (Token *tok3 = usingStart; tok3 && tok3 != usingEnd; tok3 = tok3->next()) {
3193-
if (!str.empty())
3194-
str += ' ';
3195-
str += tok3->str();
3196-
}
3197-
str += " ;";
3198-
std::list<const Token *> callstack(1, usingStart);
3199-
mErrorLogger->reportErr(ErrorMessage(callstack, &list, Severity::debug, "simplifyUsing",
3200-
"Failed to parse \'" + str + "\'. The checking continues anyway.", Certainty::normal));
3201-
}
3208+
simplifyUsingError(usingStart, usingEnd);
32023209
}
32033210
tok1 = after;
32043211
}
@@ -3233,6 +3240,22 @@ bool Tokenizer::simplifyUsing()
32333240
return substitute;
32343241
}
32353242

3243+
void Tokenizer::simplifyUsingError(const Token* usingStart, const Token* usingEnd)
3244+
{
3245+
if (mSettings->debugwarnings && mErrorLogger) {
3246+
std::string str;
3247+
for (const Token *tok = usingStart; tok && tok != usingEnd; tok = tok->next()) {
3248+
if (!str.empty())
3249+
str += ' ';
3250+
str += tok->str();
3251+
}
3252+
str += " ;";
3253+
std::list<const Token *> callstack(1, usingStart);
3254+
mErrorLogger->reportErr(ErrorMessage(callstack, &list, Severity::debug, "simplifyUsing",
3255+
"Failed to parse \'" + str + "\'. The checking continues anyway.", Certainty::normal));
3256+
}
3257+
}
3258+
32363259
bool Tokenizer::createTokens(std::istream &code,
32373260
const std::string& FileName)
32383261
{

lib/tokenize.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ class CPPCHECKLIB Tokenizer {
280280
/**
281281
*/
282282
bool simplifyUsing();
283+
void simplifyUsingError(const Token* usingStart, const Token* usingEnd);
283284

284285
/** Simplify useless C++ empty namespaces, like: 'namespace %name% { }'*/
285286
void simplifyEmptyNamespaces();

test/testsimplifyusing.cpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include "token.h"
2525
#include "tokenize.h"
2626

27+
#include <simplecpp.h>
28+
2729
#include <sstream> // IWYU pragma: keep
2830
#include <string>
2931

@@ -92,20 +94,33 @@ class TestSimplifyUsing : public TestFixture {
9294
TEST_CASE(simplifyUsing10172);
9395
TEST_CASE(simplifyUsing10173);
9496
TEST_CASE(simplifyUsing10335);
97+
TEST_CASE(simplifyUsing10720);
9598

9699
TEST_CASE(scopeInfo1);
97100
TEST_CASE(scopeInfo2);
98101
}
99102

100103
#define tok(...) tok_(__FILE__, __LINE__, __VA_ARGS__)
101-
std::string tok_(const char* file, int line, const char code[], cppcheck::Platform::Type type = cppcheck::Platform::Type::Native, bool debugwarnings = true) {
104+
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) {
102105
errout.str("");
103106

104107
settings0.certainty.enable(Certainty::inconclusive);
105108
settings0.debugwarnings = debugwarnings;
106109
PLATFORM(settings0.platform, type);
107110
Tokenizer tokenizer(&settings0, this);
108111

112+
if (preprocess) {
113+
std::vector<std::string> files{ "test.cpp" };
114+
std::istringstream istr(code);
115+
const simplecpp::TokenList tokens1(istr, files, files[0]);
116+
117+
simplecpp::TokenList tokens2(files);
118+
std::map<std::string, simplecpp::TokenList*> filedata;
119+
simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI());
120+
121+
tokenizer.createTokens(std::move(tokens2));
122+
}
123+
109124
std::istringstream istr(code);
110125
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
111126

@@ -1346,6 +1361,17 @@ class TestSimplifyUsing : public TestFixture {
13461361
ASSERT_EQUALS(exp, tok(code));
13471362
}
13481363

1364+
void simplifyUsing10720() {
1365+
const char code[] = "template <typename... Ts>\n"
1366+
"struct S {};\n"
1367+
"#define STAMP(thiz, prev) using thiz = S<prev, prev, prev, prev, prev, prev, prev, prev, prev, prev>;\n"
1368+
"STAMP(A, int);\n"
1369+
"STAMP(B, A);\n"
1370+
"STAMP(C, B);\n";
1371+
tok(code, cppcheck::Platform::Type::Native, /*debugwarnings*/ true, /*preprocess*/ true);
1372+
ASSERT_EQUALS(errout.str().compare(0, 64, "[test.cpp:6]: (debug) Failed to parse 'using C = S < S < S < int"), 0);
1373+
}
1374+
13491375
void scopeInfo1() {
13501376
const char code[] = "struct A {\n"
13511377
" enum class Mode { UNKNOWN, ENABLED, NONE, };\n"

0 commit comments

Comments
 (0)