Skip to content

Commit 74e3114

Browse files
IOBYTEdanmar
authored andcommitted
Fix #9097 (Crash on thousands of "else ifs"s in gcc-avr package) (#1982)
* Fix #9097 (Crash on thousands of "else ifs"s in gcc-avr package) * increase recursion count maximum to 512 because cppcheck was hitting the 256 limit * 512 was too much for windows
1 parent 4a70208 commit 74e3114

5 files changed

Lines changed: 56 additions & 3 deletions

File tree

lib/checkleakautovar.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ void CheckLeakAutoVar::check()
159159
if (scope->hasInlineOrLambdaFunction())
160160
continue;
161161

162+
recursiveCount = 0;
163+
162164
// Empty variable info
163165
VarInfo varInfo;
164166

@@ -236,6 +238,11 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken,
236238
VarInfo *varInfo,
237239
std::set<unsigned int> notzero)
238240
{
241+
// The C++ standard suggests a minimum of 256 nested control statements
242+
// but MSVC has a limit of 100. Cppcheck is hitting 256 when checking itself.
243+
if (++recursiveCount > 384)
244+
throw InternalError(startToken, "Internal limit: CheckLeakAutoVar::checkScope() Maximum recursive count of 384 reached.", InternalError::LIMIT);
245+
239246
std::map<unsigned int, VarInfo::AllocInfo> &alloctype = varInfo->alloctype;
240247
std::map<unsigned int, std::string> &possibleUsage = varInfo->possibleUsage;
241248
const std::set<unsigned int> conditionalAlloc(varInfo->conditionalAlloc);

lib/checkleakautovar.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,12 @@ class CPPCHECKLIB VarInfo {
9595
class CPPCHECKLIB CheckLeakAutoVar : public Check {
9696
public:
9797
/** This constructor is used when registering the CheckLeakAutoVar */
98-
CheckLeakAutoVar() : Check(myName()) {
98+
CheckLeakAutoVar() : Check(myName()), recursiveCount(0) {
9999
}
100100

101101
/** This constructor is used when running checks. */
102102
CheckLeakAutoVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
103-
: Check(myName(), tokenizer, settings, errorLogger) {
103+
: Check(myName(), tokenizer, settings, errorLogger), recursiveCount(0) {
104104
}
105105

106106
void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE {
@@ -163,6 +163,8 @@ class CPPCHECKLIB CheckLeakAutoVar : public Check {
163163
std::string classInfo() const OVERRIDE {
164164
return "Detect when a auto variable is allocated but not deallocated or deallocated twice.\n";
165165
}
166+
167+
unsigned int recursiveCount;
166168
};
167169
/// @}
168170
//---------------------------------------------------------------------------

lib/errorlogger.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ InternalError::InternalError(const Token *tok, const std::string &errorMsg, Type
4949
case INTERNAL:
5050
id = "cppcheckError";
5151
break;
52+
case LIMIT:
53+
id = "cppcheckLimit";
54+
break;
5255
}
5356
}
5457

lib/errorlogger.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ namespace tinyxml2 {
5454

5555
/** @brief Simple container to be thrown when internal error is detected. */
5656
struct InternalError {
57-
enum Type {AST, SYNTAX, UNKNOWN_MACRO, INTERNAL};
57+
enum Type {AST, SYNTAX, UNKNOWN_MACRO, INTERNAL, LIMIT};
5858
InternalError(const Token *tok, const std::string &errorMsg, Type type = INTERNAL);
5959
const Token *token;
6060
std::string errorMessage;

test/testleakautovar.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include "testsuite.h"
2424
#include "tokenize.h"
2525

26+
#include <simplecpp.h>
27+
#include <vector>
2628

2729
class TestLeakAutoVar : public TestFixture {
2830
public:
@@ -163,6 +165,8 @@ class TestLeakAutoVar : public TestFixture {
163165
TEST_CASE(inlineFunction); // #3989
164166

165167
TEST_CASE(smartPtrInContainer); // #8262
168+
169+
TEST_CASE(recursiveCountLimit); // #5872 #6157 #9097
166170
}
167171

168172
void check(const char code[], bool cpp = false) {
@@ -181,6 +185,32 @@ class TestLeakAutoVar : public TestFixture {
181185
c.runChecks(&tokenizer, &settings, this);
182186
}
183187

188+
void checkP(const char code[], bool cpp = false) {
189+
// Clear the error buffer..
190+
errout.str("");
191+
192+
// Raw tokens..
193+
std::vector<std::string> files(1, cpp?"test.cpp":"test.c");
194+
std::istringstream istr(code);
195+
const simplecpp::TokenList tokens1(istr, files, files[0]);
196+
197+
// Preprocess..
198+
simplecpp::TokenList tokens2(files);
199+
std::map<std::string, simplecpp::TokenList*> filedata;
200+
simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI());
201+
202+
// Tokenizer..
203+
Tokenizer tokenizer(&settings, this);
204+
tokenizer.createTokens(&tokens2);
205+
tokenizer.simplifyTokens1("");
206+
207+
// Check for leaks..
208+
CheckLeakAutoVar c;
209+
settings.checkLibrary = true;
210+
settings.addEnabled("information");
211+
c.runChecks(&tokenizer, &settings, this);
212+
}
213+
184214
void assign1() {
185215
check("void f() {\n"
186216
" char *p = malloc(10);\n"
@@ -1776,6 +1806,17 @@ class TestLeakAutoVar : public TestFixture {
17761806
ASSERT_EQUALS("", errout.str());
17771807
}
17781808

1809+
void recursiveCountLimit() { // #5872 #6157 #9097
1810+
ASSERT_THROW(checkP("#define ONE else if (0) { }\n"
1811+
"#define TEN ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE\n"
1812+
"#define HUN TEN TEN TEN TEN TEN TEN TEN TEN TEN TEN\n"
1813+
"#define THOU HUN HUN HUN HUN HUN HUN HUN HUN HUN HUN\n"
1814+
"void foo() {\n"
1815+
" if (0) { }\n"
1816+
" THOU\n"
1817+
"}"), InternalError);
1818+
}
1819+
17791820
};
17801821

17811822
REGISTER_TEST(TestLeakAutoVar)

0 commit comments

Comments
 (0)