#5592 - crash: gcc: testsuit: gcc.c-torture/compile/limits-declparen.c (Limit stack depth below createAst()

This commit is contained in:
Alexander Mai 2014-04-13 12:47:54 +02:00
parent 9bfc2b618b
commit 547803f581
3 changed files with 147 additions and 72 deletions

View File

@ -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

View File

@ -392,11 +392,32 @@ static bool iscast(const Token *tok)
return false;
}
static void compileUnaryOp(Token *&tok, void (*f)(Token *&, std::stack<Token*> &), std::stack<Token*> &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<Token*> &, const CallstackDepth callstackDepth), std::stack<Token*> &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<Token*> &
op.push(unaryop);
}
static void compileBinOp(Token *&tok, void (*f)(Token *&, std::stack<Token*> &), std::stack<Token*> &op)
static void compileBinOp(Token *&tok, void (*f)(Token *&, std::stack<Token*> &, const CallstackDepth callstackDepth), std::stack<Token*> &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<Token*> &),
op.push(binop);
}
static void compileDot(Token *&tok, std::stack<Token*> &op);
static void compileExpression(Token *&tok, std::stack<Token*> &op);
static void compileDot(Token *&tok, std::stack<Token*> &op, const CallstackDepth callstackDepth);
static void compileExpression(Token *&tok, std::stack<Token*> &op, const CallstackDepth callstackDepth);
static void compileTerm(Token *& tok, std::stack<Token*> &op)
static void compileTerm(Token *& tok, std::stack<Token*> &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<Token*> &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<Token*> &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<Token*> &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<Token*> &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<Token*> &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<Token*> &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<Token*> &op)
}
}
static void compileScope(Token *&tok, std::stack<Token*> &op)
static void compileScope(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileParAndBrackets(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileDot(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileMulDiv(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileAddSub(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileShift(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileRelComp(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileEqComp(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileAnd(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileXor(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileOr(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileLogicAnd(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileLogicOr(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileTernaryOp(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileAssign(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileComma(Token *&tok, std::stack<Token*> &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<Token*> &op)
static void compileExpression(Token *&tok, std::stack<Token*> &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<Token *> 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<Token *> operands2;
compileExpression(tok2, operands2);
compileExpression(tok2, operands2, CallstackDepth(0));
Token * const semicolon2 = tok2;
tok2 = tok2->next();
std::stack<Token *> operands3;
compileExpression(tok2, operands3);
compileExpression(tok2, operands3, CallstackDepth(0));
if (init != semicolon1)
semicolon1->astOperand1(const_cast<Token*>(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<Token *> operands;
Token * const tok1 = tok;
compileExpression(tok, operands);
compileExpression(tok, operands, CallstackDepth(0));
Token * const endToken = tok;
if (endToken == tok1)
return tok1;

View File

@ -22,6 +22,7 @@
#include "token.h"
#include "settings.h"
#include "path.h"
#include "preprocessor.h" // usually tests here should not use preprocessor...
#include <cstring>
#include <stack>
@ -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<bool> { };"));
ASSERT_EQUALS("f( abc+=", testAst("struct A : public B<C*> { 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<std::string> 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)