From 74e3114a647c1926a4da012fc14fb478ecf6df5e Mon Sep 17 00:00:00 2001 From: IOBYTE Date: Fri, 12 Jul 2019 01:56:05 -0400 Subject: [PATCH] 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 --- lib/checkleakautovar.cpp | 7 +++++++ lib/checkleakautovar.h | 6 ++++-- lib/errorlogger.cpp | 3 +++ lib/errorlogger.h | 2 +- test/testleakautovar.cpp | 41 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 3 deletions(-) diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index 03dd7bd42..e4fe9462d 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -159,6 +159,8 @@ void CheckLeakAutoVar::check() if (scope->hasInlineOrLambdaFunction()) continue; + recursiveCount = 0; + // Empty variable info VarInfo varInfo; @@ -236,6 +238,11 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken, VarInfo *varInfo, std::set notzero) { + // The C++ standard suggests a minimum of 256 nested control statements + // but MSVC has a limit of 100. Cppcheck is hitting 256 when checking itself. + if (++recursiveCount > 384) + throw InternalError(startToken, "Internal limit: CheckLeakAutoVar::checkScope() Maximum recursive count of 384 reached.", InternalError::LIMIT); + std::map &alloctype = varInfo->alloctype; std::map &possibleUsage = varInfo->possibleUsage; const std::set conditionalAlloc(varInfo->conditionalAlloc); diff --git a/lib/checkleakautovar.h b/lib/checkleakautovar.h index a7acc2ee4..a27ce5553 100644 --- a/lib/checkleakautovar.h +++ b/lib/checkleakautovar.h @@ -95,12 +95,12 @@ public: class CPPCHECKLIB CheckLeakAutoVar : public Check { public: /** This constructor is used when registering the CheckLeakAutoVar */ - CheckLeakAutoVar() : Check(myName()) { + CheckLeakAutoVar() : Check(myName()), recursiveCount(0) { } /** This constructor is used when running checks. */ CheckLeakAutoVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) { + : Check(myName(), tokenizer, settings, errorLogger), recursiveCount(0) { } void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE { @@ -163,6 +163,8 @@ private: std::string classInfo() const OVERRIDE { return "Detect when a auto variable is allocated but not deallocated or deallocated twice.\n"; } + + unsigned int recursiveCount; }; /// @} //--------------------------------------------------------------------------- diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp index c9b545c56..475ffd372 100644 --- a/lib/errorlogger.cpp +++ b/lib/errorlogger.cpp @@ -49,6 +49,9 @@ InternalError::InternalError(const Token *tok, const std::string &errorMsg, Type case INTERNAL: id = "cppcheckError"; break; + case LIMIT: + id = "cppcheckLimit"; + break; } } diff --git a/lib/errorlogger.h b/lib/errorlogger.h index 9304da47c..c77a0ceab 100644 --- a/lib/errorlogger.h +++ b/lib/errorlogger.h @@ -54,7 +54,7 @@ namespace tinyxml2 { /** @brief Simple container to be thrown when internal error is detected. */ struct InternalError { - enum Type {AST, SYNTAX, UNKNOWN_MACRO, INTERNAL}; + enum Type {AST, SYNTAX, UNKNOWN_MACRO, INTERNAL, LIMIT}; InternalError(const Token *tok, const std::string &errorMsg, Type type = INTERNAL); const Token *token; std::string errorMessage; diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp index 07c2c2aaa..349f79056 100644 --- a/test/testleakautovar.cpp +++ b/test/testleakautovar.cpp @@ -23,6 +23,8 @@ #include "testsuite.h" #include "tokenize.h" +#include +#include class TestLeakAutoVar : public TestFixture { public: @@ -163,6 +165,8 @@ private: TEST_CASE(inlineFunction); // #3989 TEST_CASE(smartPtrInContainer); // #8262 + + TEST_CASE(recursiveCountLimit); // #5872 #6157 #9097 } void check(const char code[], bool cpp = false) { @@ -181,6 +185,32 @@ private: c.runChecks(&tokenizer, &settings, this); } + void checkP(const char code[], bool cpp = false) { + // Clear the error buffer.. + errout.str(""); + + // Raw tokens.. + std::vector files(1, cpp?"test.cpp":"test.c"); + std::istringstream istr(code); + const simplecpp::TokenList tokens1(istr, files, files[0]); + + // Preprocess.. + simplecpp::TokenList tokens2(files); + std::map filedata; + simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI()); + + // Tokenizer.. + Tokenizer tokenizer(&settings, this); + tokenizer.createTokens(&tokens2); + tokenizer.simplifyTokens1(""); + + // Check for leaks.. + CheckLeakAutoVar c; + settings.checkLibrary = true; + settings.addEnabled("information"); + c.runChecks(&tokenizer, &settings, this); + } + void assign1() { check("void f() {\n" " char *p = malloc(10);\n" @@ -1776,6 +1806,17 @@ private: ASSERT_EQUALS("", errout.str()); } + void recursiveCountLimit() { // #5872 #6157 #9097 + ASSERT_THROW(checkP("#define ONE else if (0) { }\n" + "#define TEN ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE\n" + "#define HUN TEN TEN TEN TEN TEN TEN TEN TEN TEN TEN\n" + "#define THOU HUN HUN HUN HUN HUN HUN HUN HUN HUN HUN\n" + "void foo() {\n" + " if (0) { }\n" + " THOU\n" + "}"), InternalError); + } + }; REGISTER_TEST(TestLeakAutoVar)