diff --git a/Makefile b/Makefile index 93225e9a3..c3c767b48 100644 --- a/Makefile +++ b/Makefile @@ -522,7 +522,7 @@ test/testtimer.o: test/testtimer.cpp lib/cxx11emu.h lib/timer.h lib/config.h tes test/testtoken.o: test/testtoken.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h test/testutils.h lib/settings.h lib/standards.h lib/timer.h lib/tokenize.h lib/tokenlist.h lib/token.h lib/valueflow.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) -std=c++0x -c -o test/testtoken.o test/testtoken.cpp -test/testtokenize.o: test/testtokenize.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/tokenize.h lib/tokenlist.h lib/token.h lib/valueflow.h lib/settings.h lib/standards.h lib/timer.h +test/testtokenize.o: test/testtokenize.cpp lib/cxx11emu.h test/testsuite.h lib/errorlogger.h lib/config.h lib/suppressions.h test/redirect.h lib/library.h lib/path.h lib/mathlib.h lib/tokenize.h lib/tokenlist.h lib/token.h lib/valueflow.h lib/settings.h lib/standards.h lib/timer.h lib/preprocessor.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CFG) $(CXXFLAGS) -std=c++0x -c -o test/testtokenize.o test/testtokenize.cpp test/testuninitvar.o: test/testuninitvar.cpp lib/cxx11emu.h lib/tokenize.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/tokenlist.h lib/checkuninitvar.h lib/check.h lib/token.h lib/valueflow.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h test/testsuite.h test/redirect.h diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index 4d38b4123..d34f47d4b 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -392,11 +392,32 @@ static bool iscast(const Token *tok) return false; } -static void compileUnaryOp(Token *&tok, void (*f)(Token *&, std::stack &), std::stack &op) +/*! Instances of this class are used to track the depth of the callstack. Simply pass the current instance down + * the callstack! + */ +class CallstackDepth { +public: + explicit CallstackDepth(unsigned _depth) : + depth(_depth) { + } + CallstackDepth(const CallstackDepth& rhs) + : depth(rhs.depth+1) { + } + bool exhausted() const { + return depth >= MAX_CALLSTACK_DEPTH; + } +private: + CallstackDepth& operator=(const CallstackDepth& rhs); + const unsigned depth; + static const unsigned MAX_CALLSTACK_DEPTH=300; // arbitrary limit: was sufficient to fix #5592 and run on cppcheck's own code +}; +#define UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW if (callstackDepth.exhausted()) { return;} + +static void compileUnaryOp(Token *&tok, void (*f)(Token *&, std::stack &, const CallstackDepth callstackDepth), std::stack &op, const CallstackDepth callstackDepth) { Token *unaryop = tok; tok = tok->next(); - f(tok,op); + f(tok,op, callstackDepth); if (!op.empty()) { unaryop->astOperand1(op.top()); @@ -405,12 +426,12 @@ static void compileUnaryOp(Token *&tok, void (*f)(Token *&, std::stack & op.push(unaryop); } -static void compileBinOp(Token *&tok, void (*f)(Token *&, std::stack &), std::stack &op) +static void compileBinOp(Token *&tok, void (*f)(Token *&, std::stack &, const CallstackDepth callstackDepth), std::stack &op, const CallstackDepth callstackDepth) { Token *binop = tok; tok = tok->next(); if (tok) - f(tok,op); + f(tok,op, callstackDepth); // TODO: Should we check if op is empty. // * Is it better to add assertion that it isn't? @@ -426,11 +447,12 @@ static void compileBinOp(Token *&tok, void (*f)(Token *&, std::stack &), op.push(binop); } -static void compileDot(Token *&tok, std::stack &op); -static void compileExpression(Token *&tok, std::stack &op); +static void compileDot(Token *&tok, std::stack &op, const CallstackDepth callstackDepth); +static void compileExpression(Token *&tok, std::stack &op, const CallstackDepth callstackDepth); -static void compileTerm(Token *& tok, std::stack &op) +static void compileTerm(Token *& tok, std::stack &op, const CallstackDepth callstackDepth) { + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW if (!tok) return; if (Token::Match(tok, "L %str%|%char%")) @@ -439,9 +461,9 @@ static void compileTerm(Token *& tok, std::stack &op) op.push(tok); tok = tok->next(); } else if (Token::Match(tok, "+|-|~|*|&|!")) { - compileUnaryOp(tok, compileDot, op); + compileUnaryOp(tok, compileDot, op, callstackDepth); } else if (tok->str() == "return") { - compileUnaryOp(tok, compileExpression, op); + compileUnaryOp(tok, compileExpression, op, callstackDepth); } else if (tok->isName()) { const bool templatefunc = Token::Match(tok, "%var% <") && Token::simpleMatch(tok->linkAt(1), "> ("); if (Token::Match(tok->next(), "++|--")) { // post increment / decrement @@ -453,7 +475,7 @@ static void compileTerm(Token *& tok, std::stack &op) op.push(tok); tok = tok->next()->link()->next(); if (!Token::simpleMatch(tok, "{")) - compileTerm(tok,op); + compileTerm(tok,op, callstackDepth); } else if (!Token::Match(tok->next(), "(|[") && !templatefunc) { op.push(tok); tok = tok->next(); @@ -473,7 +495,7 @@ static void compileTerm(Token *& tok, std::stack &op) tok = tok->next(); while (Token::Match(tok,"%var% %var%")) // example: sizeof(struct S) tok = tok->next(); - compileExpression(tok, op); + compileExpression(tok, op, callstackDepth); if (!op.empty()) { tok1->astOperand2(op.top()); op.pop(); @@ -496,7 +518,7 @@ static void compileTerm(Token *& tok, std::stack &op) if (pre) { // pre increment/decrement - compileUnaryOp(tok, compileDot, op); + compileUnaryOp(tok, compileDot, op, callstackDepth); } else { // post increment/decrement tok->astOperand1(op.top()); @@ -508,7 +530,7 @@ static void compileTerm(Token *& tok, std::stack &op) if (iscast(tok)) { Token *unaryop = tok; tok = tok->link()->next(); - compileDot(tok,op); + compileDot(tok,op, callstackDepth); if (!op.empty()) { unaryop->astOperand1(op.top()); @@ -519,14 +541,14 @@ static void compileTerm(Token *& tok, std::stack &op) // Parenthesized sub-expression Token *nextpar = tok->link()->next(); tok = tok->next(); - compileExpression(tok,op); + compileExpression(tok,op, callstackDepth); tok = nextpar; - compileBinOp(tok, compileExpression, op); + compileBinOp(tok, compileExpression, op, callstackDepth); tok = tok->next(); } else { // Parenthesized sub-expression tok = tok->next(); - compileExpression(tok,op); + compileExpression(tok,op, callstackDepth); tok = tok->next(); } } else if (tok->str() == "{") { @@ -535,175 +557,194 @@ static void compileTerm(Token *& tok, std::stack &op) } } -static void compileScope(Token *&tok, std::stack &op) +static void compileScope(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileTerm(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileTerm(tok,op, callstackDepth); while (tok) { if (tok->str() == "::") { if (tok->previous() && tok->previous()->isName()) - compileBinOp(tok, compileTerm, op); + compileBinOp(tok, compileTerm, op, callstackDepth); else - compileUnaryOp(tok, compileDot, op); + compileUnaryOp(tok, compileDot, op, callstackDepth); } else break; } } -static void compileParAndBrackets(Token *&tok, std::stack &op) +static void compileParAndBrackets(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileScope(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileScope(tok,op, callstackDepth); while (tok) { if (tok->str() == "[") { - compileBinOp(tok, compileScope, op); + compileBinOp(tok, compileScope, op, callstackDepth); } else break; } } -static void compileDot(Token *&tok, std::stack &op) +static void compileDot(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileParAndBrackets(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileParAndBrackets(tok,op, callstackDepth); while (tok) { if (tok->str() == ".") { - compileBinOp(tok, compileParAndBrackets, op); + compileBinOp(tok, compileParAndBrackets, op, callstackDepth); } else break; } } -static void compileMulDiv(Token *&tok, std::stack &op) +static void compileMulDiv(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileDot(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileDot(tok,op, callstackDepth); while (tok) { if (Token::Match(tok, "[*/%]")) { if (Token::Match(tok, "* [,)]")) break; - compileBinOp(tok, compileDot, op); + compileBinOp(tok, compileDot, op, callstackDepth); } else break; } } -static void compileAddSub(Token *&tok, std::stack &op) +static void compileAddSub(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileMulDiv(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileMulDiv(tok,op, callstackDepth); while (tok) { if (Token::Match(tok, "+|-")) { - compileBinOp(tok, compileMulDiv, op); + compileBinOp(tok, compileMulDiv, op, callstackDepth); } else break; } } -static void compileShift(Token *&tok, std::stack &op) +static void compileShift(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileAddSub(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileAddSub(tok,op, callstackDepth); while (tok) { if (Token::Match(tok, "<<|>>")) { - compileBinOp(tok, compileAddSub, op); + compileBinOp(tok, compileAddSub, op, callstackDepth); } else break; } } -static void compileRelComp(Token *&tok, std::stack &op) +static void compileRelComp(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileShift(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileShift(tok,op, callstackDepth); while (tok) { if (Token::Match(tok, "<|<=|>=|>")) { - compileBinOp(tok, compileShift, op); + compileBinOp(tok, compileShift, op, callstackDepth); } else break; } } -static void compileEqComp(Token *&tok, std::stack &op) +static void compileEqComp(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileRelComp(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileRelComp(tok,op, callstackDepth); while (tok) { if (Token::Match(tok, "==|!=")) { - compileBinOp(tok, compileRelComp, op); + compileBinOp(tok, compileRelComp, op, callstackDepth); } else break; } } -static void compileAnd(Token *&tok, std::stack &op) +static void compileAnd(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileEqComp(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileEqComp(tok,op, callstackDepth); while (tok) { if (tok->str() == "&") { - compileBinOp(tok, compileEqComp, op); + compileBinOp(tok, compileEqComp, op, callstackDepth); } else break; } } -static void compileXor(Token *&tok, std::stack &op) +static void compileXor(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileAnd(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileAnd(tok,op, callstackDepth); while (tok) { if (tok->str() == "^") { - compileBinOp(tok, compileAnd, op); + compileBinOp(tok, compileAnd, op, callstackDepth); } else break; } } -static void compileOr(Token *&tok, std::stack &op) +static void compileOr(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileXor(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileXor(tok,op, callstackDepth); while (tok) { if (tok->str() == "|") { - compileBinOp(tok, compileXor, op); + compileBinOp(tok, compileXor, op, callstackDepth); } else break; } } -static void compileLogicAnd(Token *&tok, std::stack &op) +static void compileLogicAnd(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileOr(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileOr(tok,op, callstackDepth); while (tok) { if (tok->str() == "&&") { - compileBinOp(tok, compileOr, op); + compileBinOp(tok, compileOr, op, callstackDepth); } else break; } } -static void compileLogicOr(Token *&tok, std::stack &op) +static void compileLogicOr(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileLogicAnd(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileLogicAnd(tok,op, callstackDepth); while (tok) { if (tok->str() == "||") { - compileBinOp(tok, compileLogicAnd, op); + compileBinOp(tok, compileLogicAnd, op, callstackDepth); } else break; } } -static void compileTernaryOp(Token *&tok, std::stack &op) +static void compileTernaryOp(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileLogicOr(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileLogicOr(tok,op, callstackDepth); while (tok) { if (Token::Match(tok, "[?:]")) { - compileBinOp(tok, compileLogicOr, op); + compileBinOp(tok, compileLogicOr, op, callstackDepth); } else break; } } -static void compileAssign(Token *&tok, std::stack &op) +static void compileAssign(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileTernaryOp(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileTernaryOp(tok,op, callstackDepth); while (tok) { if (tok->str() == "=") { - compileBinOp(tok, compileTernaryOp, op); + compileBinOp(tok, compileTernaryOp, op, callstackDepth); } else break; } } -static void compileComma(Token *&tok, std::stack &op) +static void compileComma(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { - compileAssign(tok,op); + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + compileAssign(tok,op, callstackDepth); while (tok) { if (tok->str() == ",") { - compileBinOp(tok, compileAssign, op); + compileBinOp(tok, compileAssign, op, callstackDepth); } else break; } } -static void compileExpression(Token *&tok, std::stack &op) +static void compileExpression(Token *&tok, std::stack &op, const CallstackDepth callstackDepth) { + UGLY_BAILOUT_TO_AVOID_CALLSTACKOVERFLOW + if (callstackDepth.exhausted()) + return; // ticket #5592 if (tok) - compileComma(tok,op); + compileComma(tok,op, callstackDepth); } static Token * createAstAtToken(Token *tok) @@ -720,7 +761,7 @@ static Token * createAstAtToken(Token *tok) } else if (Token::Match(tok2, "%var% %op%|(|[|.|=|:|::") || Token::Match(tok2->previous(), "[(;{}] %cop%|(")) { init1 = tok2; std::stack operands; - compileExpression(tok2, operands); + compileExpression(tok2, operands, CallstackDepth(0)); if (tok2->str() == ";" || tok2->str() == ")") break; init1 = 0; @@ -740,12 +781,12 @@ static Token * createAstAtToken(Token *tok) Token * const semicolon1 = tok2; tok2 = tok2->next(); std::stack operands2; - compileExpression(tok2, operands2); + compileExpression(tok2, operands2, CallstackDepth(0)); Token * const semicolon2 = tok2; tok2 = tok2->next(); std::stack operands3; - compileExpression(tok2, operands3); + compileExpression(tok2, operands3, CallstackDepth(0)); if (init != semicolon1) semicolon1->astOperand1(const_cast(init->astTop())); @@ -776,7 +817,7 @@ static Token * createAstAtToken(Token *tok) if (tok->str() == "return" || !tok->previous() || Token::Match(tok, "%var% %op%|(|[|.|=|::") || Token::Match(tok->previous(), "[;{}] %cop%|( !!{")) { std::stack operands; Token * const tok1 = tok; - compileExpression(tok, operands); + compileExpression(tok, operands, CallstackDepth(0)); Token * const endToken = tok; if (endToken == tok1) return tok1; diff --git a/test/testtokenize.cpp b/test/testtokenize.cpp index d03f927af..320923a7f 100644 --- a/test/testtokenize.cpp +++ b/test/testtokenize.cpp @@ -22,6 +22,7 @@ #include "token.h" #include "settings.h" #include "path.h" +#include "preprocessor.h" // usually tests here should not use preprocessor... #include #include @@ -561,6 +562,8 @@ private: TEST_CASE(simplifyMathExpressions); //ticket #1620 + TEST_CASE(compileLimits); // #5592 crash: gcc: testsuit: gcc.c-torture/compile/limits-declparen.c + // AST data TEST_CASE(astexpr); TEST_CASE(astpar); @@ -10145,6 +10148,7 @@ private: ASSERT_EQUALS(code6, tokenizeAndStringify(code6)); } + static std::string testAst(const char code[]) { // tokenize given code.. const Settings settings; @@ -10296,6 +10300,36 @@ private: ASSERT_EQUALS("publica::b::", testAst("class C : public ::a::b { };")); ASSERT_EQUALS("f( abc+=", testAst("struct A : public B { void f() { a=b+c; } };")); } + + void compileLimits() { + const char raw_code[] = "#define PTR1 (* (* (* (* (* (* (* (* (* (*\n" + "#define PTR2 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1 PTR1\n" + "#define PTR3 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2 PTR2\n" + "#define PTR4 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3 PTR3\n" + "#define PTR5 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4 PTR4\n" + "#define PTR6 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5 PTR5\n" + "\n" + "#define RBR1 ) ) ) ) ) ) ) ) ) )\n" + "#define RBR2 RBR1 RBR1 RBR1 RBR1 RBR1 RBR1 RBR1 RBR1 RBR1 RBR1\n" + "#define RBR3 RBR2 RBR2 RBR2 RBR2 RBR2 RBR2 RBR2 RBR2 RBR2 RBR2\n" + "#define RBR4 RBR3 RBR3 RBR3 RBR3 RBR3 RBR3 RBR3 RBR3 RBR3 RBR3\n" + "#define RBR5 RBR4 RBR4 RBR4 RBR4 RBR4 RBR4 RBR4 RBR4 RBR4 RBR4\n" + "#define RBR6 RBR5 RBR5 RBR5 RBR5 RBR5 RBR5 RBR5 RBR5 RBR5 RBR5\n" + "\n" + "int PTR4 q4_var RBR4 = 0;\n"; + + // Preprocess file.. + Settings settings; + Preprocessor preprocessor(&settings); + std::list configurations; + std::string filedata = ""; + std::istringstream fin(raw_code); + preprocessor.preprocess(fin, filedata, configurations, "", settings._includePaths); + const std::string code = preprocessor.getcode(filedata, "", ""); + + tokenizeAndStringify(code.c_str()); // just survive... + } + }; REGISTER_TEST(TestTokenizer)