Skip to content

Commit

Permalink
Fix #12847 Stack overflow in setParentExprId() with huge array
Browse files Browse the repository at this point in the history
  • Loading branch information
chrchr-github committed Jun 19, 2024
1 parent 3c772a4 commit 5c82a24
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 67 deletions.
134 changes: 68 additions & 66 deletions lib/symboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1565,82 +1565,84 @@ namespace {
};
using ExprIdMap = std::map<ExprIdKey, nonneg int>;
void setParentExprId(Token* tok, ExprIdMap& exprIdMap, nonneg int &id) {
if (!tok->astParent() || tok->astParent()->isControlFlowKeyword())
return;
const Token* op1 = tok->astParent()->astOperand1();
if (op1 && op1->exprId() == 0 && !Token::Match(op1, "[{[]"))
return;
const Token* op2 = tok->astParent()->astOperand2();
if (op2 && op2->exprId() == 0 &&
!((tok->astParent()->astParent() && tok->astParent()->isAssignmentOp() && tok->astParent()->astParent()->isAssignmentOp()) ||
isLambdaCaptureList(op2) ||
(op2->str() == "(" && isLambdaCaptureList(op2->astOperand1())) ||
Token::simpleMatch(op2, "{ }")))
return;
for (;;) {
if (!tok->astParent() || tok->astParent()->isControlFlowKeyword())
break;
const Token* op1 = tok->astParent()->astOperand1();
if (op1 && op1->exprId() == 0 && !Token::Match(op1, "[{[]"))
break;
const Token* op2 = tok->astParent()->astOperand2();
if (op2 && op2->exprId() == 0 &&
!((tok->astParent()->astParent() && tok->astParent()->isAssignmentOp() && tok->astParent()->astParent()->isAssignmentOp()) ||
isLambdaCaptureList(op2) ||
(op2->str() == "(" && isLambdaCaptureList(op2->astOperand1())) ||
Token::simpleMatch(op2, "{ }")))
break;

if (tok->astParent()->isExpandedMacro() || Token::Match(tok->astParent(), "++|--")) {
tok->astParent()->exprId(id);
++id;
setParentExprId(tok->astParent(), exprIdMap, id);
return;
}
if (tok->astParent()->isExpandedMacro() || Token::Match(tok->astParent(), "++|--")) {
tok->astParent()->exprId(id);
++id;
tok = tok->astParent();
continue;
}

ExprIdKey key;
key.parentOp = tok->astParent()->str();
key.operand1 = op1 ? op1->exprId() : 0;
key.operand2 = op2 ? op2->exprId() : 0;
ExprIdKey key;
key.parentOp = tok->astParent()->str();
key.operand1 = op1 ? op1->exprId() : 0;
key.operand2 = op2 ? op2->exprId() : 0;

if (tok->astParent()->isCast() && tok->astParent()->str() == "(") {
const Token* typeStartToken;
const Token* typeEndToken;
if (tok->astParent()->astOperand2()) {
typeStartToken = tok->astParent()->astOperand1();
typeEndToken = tok;
} else {
typeStartToken = tok->astParent()->next();
typeEndToken = tok->astParent()->link();
}
std::string type;
for (const Token* t = typeStartToken; t != typeEndToken; t = t->next()) {
type += " " + t->str();
if (tok->astParent()->isCast() && tok->astParent()->str() == "(") {
const Token* typeStartToken;
const Token* typeEndToken;
if (tok->astParent()->astOperand2()) {
typeStartToken = tok->astParent()->astOperand1();
typeEndToken = tok;
} else {
typeStartToken = tok->astParent()->next();
typeEndToken = tok->astParent()->link();
}
std::string type;
for (const Token* t = typeStartToken; t != typeEndToken; t = t->next()) {
type += " " + t->str();
}
key.parentOp += type;
}
key.parentOp += type;
}

for (const auto& ref: followAllReferences(op1)) {
if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm
key.operand1 = ref.token->exprId();
break;
for (const auto& ref: followAllReferences(op1)) {
if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm
key.operand1 = ref.token->exprId();
break;
}
}
}
for (const auto& ref: followAllReferences(op2)) {
if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm
key.operand2 = ref.token->exprId();
break;
for (const auto& ref: followAllReferences(op2)) {
if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm
key.operand2 = ref.token->exprId();
break;
}
}
}

if (key.operand1 > key.operand2 && key.operand2 &&
Token::Match(tok->astParent(), "%or%|%oror%|+|*|&|&&|^|==|!=")) {
// In C++ the order of operands of + might matter
if (!tok->isCpp() ||
key.parentOp != "+" ||
!tok->astParent()->valueType() ||
tok->astParent()->valueType()->isIntegral() ||
tok->astParent()->valueType()->isFloat() ||
tok->astParent()->valueType()->pointer > 0)
std::swap(key.operand1, key.operand2);
}
if (key.operand1 > key.operand2 && key.operand2 &&
Token::Match(tok->astParent(), "%or%|%oror%|+|*|&|&&|^|==|!=")) {
// In C++ the order of operands of + might matter
if (!tok->isCpp() ||
key.parentOp != "+" ||
!tok->astParent()->valueType() ||
tok->astParent()->valueType()->isIntegral() ||
tok->astParent()->valueType()->isFloat() ||
tok->astParent()->valueType()->pointer > 0)
std::swap(key.operand1, key.operand2);
}

const auto it = exprIdMap.find(key);
if (it == exprIdMap.end()) {
exprIdMap[key] = id;
tok->astParent()->exprId(id);
++id;
} else {
tok->astParent()->exprId(it->second);
const auto it = exprIdMap.find(key);
if (it == exprIdMap.end()) {
exprIdMap[key] = id;
tok->astParent()->exprId(id);
++id;
} else {
tok->astParent()->exprId(it->second);
}
tok = tok->astParent();
}
setParentExprId(tok->astParent(), exprIdMap, id);
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/tokenize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4838,7 +4838,7 @@ void Tokenizer::setVarIdPass1()
}

// function declaration inside executable scope? Function declaration is of form: type name "(" args ")"
if (scopeStack.top().isExecutable && Token::Match(tok, "%name% [,)[]")) {
if (scopeStack.top().isExecutable && !scopeStack.top().isStructInit && Token::Match(tok, "%name% [,)[]") && !tok->next()->isInitComma()) {
bool par = false;
const Token* start;
Token* end;
Expand Down
19 changes: 19 additions & 0 deletions test/cli/performance_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,22 @@ def test_slow_many_scopes(tmpdir):
return EXIT_SUCCESS;
}""")
cppcheck([filename]) # should not take more than ~1 second

@pytest.mark.timeout(20)
def test_crash_array_in_namespace(tmpdir):
# 12847
filename = os.path.join(tmpdir, 'hang.cpp')
with open(filename, 'wt') as f:
f.write(r"""
#define ROW A, A, A, A, A, A, A, A,
#define ROW8 ROW ROW ROW ROW ROW ROW ROW ROW
#define ROW64 ROW8 ROW8 ROW8 ROW8 ROW8 ROW8 ROW8 ROW8
#define ROW512 ROW64 ROW64 ROW64 ROW64 ROW64 ROW64 ROW64 ROW64
#define ROW4096 ROW512 ROW512 ROW512 ROW512 ROW512 ROW512 ROW512 ROW512
namespace N {
static const char A = 'a';
const char a[] = {
ROW4096 ROW4096 ROW4096 ROW4096
};
}""")
cppcheck([filename]) # should not take more than ~5 seconds

0 comments on commit 5c82a24

Please sign in to comment.