Skip to content

Commit 181207c

Browse files
Fix #12847 Stack overflow in setParentExprId() with huge array (#6531)
1 parent 02866e3 commit 181207c

File tree

3 files changed

+88
-67
lines changed

3 files changed

+88
-67
lines changed

lib/symboldatabase.cpp

+68-66
Original file line numberDiff line numberDiff line change
@@ -1565,82 +1565,84 @@ namespace {
15651565
};
15661566
using ExprIdMap = std::map<ExprIdKey, nonneg int>;
15671567
void setParentExprId(Token* tok, ExprIdMap& exprIdMap, nonneg int &id) {
1568-
if (!tok->astParent() || tok->astParent()->isControlFlowKeyword())
1569-
return;
1570-
const Token* op1 = tok->astParent()->astOperand1();
1571-
if (op1 && op1->exprId() == 0 && !Token::Match(op1, "[{[]"))
1572-
return;
1573-
const Token* op2 = tok->astParent()->astOperand2();
1574-
if (op2 && op2->exprId() == 0 &&
1575-
!((tok->astParent()->astParent() && tok->astParent()->isAssignmentOp() && tok->astParent()->astParent()->isAssignmentOp()) ||
1576-
isLambdaCaptureList(op2) ||
1577-
(op2->str() == "(" && isLambdaCaptureList(op2->astOperand1())) ||
1578-
Token::simpleMatch(op2, "{ }")))
1579-
return;
1568+
for (;;) {
1569+
if (!tok->astParent() || tok->astParent()->isControlFlowKeyword())
1570+
break;
1571+
const Token* op1 = tok->astParent()->astOperand1();
1572+
if (op1 && op1->exprId() == 0 && !Token::Match(op1, "[{[]"))
1573+
break;
1574+
const Token* op2 = tok->astParent()->astOperand2();
1575+
if (op2 && op2->exprId() == 0 &&
1576+
!((tok->astParent()->astParent() && tok->astParent()->isAssignmentOp() && tok->astParent()->astParent()->isAssignmentOp()) ||
1577+
isLambdaCaptureList(op2) ||
1578+
(op2->str() == "(" && isLambdaCaptureList(op2->astOperand1())) ||
1579+
Token::simpleMatch(op2, "{ }")))
1580+
break;
15801581

1581-
if (tok->astParent()->isExpandedMacro() || Token::Match(tok->astParent(), "++|--")) {
1582-
tok->astParent()->exprId(id);
1583-
++id;
1584-
setParentExprId(tok->astParent(), exprIdMap, id);
1585-
return;
1586-
}
1582+
if (tok->astParent()->isExpandedMacro() || Token::Match(tok->astParent(), "++|--")) {
1583+
tok->astParent()->exprId(id);
1584+
++id;
1585+
tok = tok->astParent();
1586+
continue;
1587+
}
15871588

1588-
ExprIdKey key;
1589-
key.parentOp = tok->astParent()->str();
1590-
key.operand1 = op1 ? op1->exprId() : 0;
1591-
key.operand2 = op2 ? op2->exprId() : 0;
1589+
ExprIdKey key;
1590+
key.parentOp = tok->astParent()->str();
1591+
key.operand1 = op1 ? op1->exprId() : 0;
1592+
key.operand2 = op2 ? op2->exprId() : 0;
15921593

1593-
if (tok->astParent()->isCast() && tok->astParent()->str() == "(") {
1594-
const Token* typeStartToken;
1595-
const Token* typeEndToken;
1596-
if (tok->astParent()->astOperand2()) {
1597-
typeStartToken = tok->astParent()->astOperand1();
1598-
typeEndToken = tok;
1599-
} else {
1600-
typeStartToken = tok->astParent()->next();
1601-
typeEndToken = tok->astParent()->link();
1602-
}
1603-
std::string type;
1604-
for (const Token* t = typeStartToken; t != typeEndToken; t = t->next()) {
1605-
type += " " + t->str();
1594+
if (tok->astParent()->isCast() && tok->astParent()->str() == "(") {
1595+
const Token* typeStartToken;
1596+
const Token* typeEndToken;
1597+
if (tok->astParent()->astOperand2()) {
1598+
typeStartToken = tok->astParent()->astOperand1();
1599+
typeEndToken = tok;
1600+
} else {
1601+
typeStartToken = tok->astParent()->next();
1602+
typeEndToken = tok->astParent()->link();
1603+
}
1604+
std::string type;
1605+
for (const Token* t = typeStartToken; t != typeEndToken; t = t->next()) {
1606+
type += " " + t->str();
1607+
}
1608+
key.parentOp += type;
16061609
}
1607-
key.parentOp += type;
1608-
}
16091610

1610-
for (const auto& ref: followAllReferences(op1)) {
1611-
if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm
1612-
key.operand1 = ref.token->exprId();
1613-
break;
1611+
for (const auto& ref: followAllReferences(op1)) {
1612+
if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm
1613+
key.operand1 = ref.token->exprId();
1614+
break;
1615+
}
16141616
}
1615-
}
1616-
for (const auto& ref: followAllReferences(op2)) {
1617-
if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm
1618-
key.operand2 = ref.token->exprId();
1619-
break;
1617+
for (const auto& ref: followAllReferences(op2)) {
1618+
if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm
1619+
key.operand2 = ref.token->exprId();
1620+
break;
1621+
}
16201622
}
1621-
}
16221623

1623-
if (key.operand1 > key.operand2 && key.operand2 &&
1624-
Token::Match(tok->astParent(), "%or%|%oror%|+|*|&|&&|^|==|!=")) {
1625-
// In C++ the order of operands of + might matter
1626-
if (!tok->isCpp() ||
1627-
key.parentOp != "+" ||
1628-
!tok->astParent()->valueType() ||
1629-
tok->astParent()->valueType()->isIntegral() ||
1630-
tok->astParent()->valueType()->isFloat() ||
1631-
tok->astParent()->valueType()->pointer > 0)
1632-
std::swap(key.operand1, key.operand2);
1633-
}
1624+
if (key.operand1 > key.operand2 && key.operand2 &&
1625+
Token::Match(tok->astParent(), "%or%|%oror%|+|*|&|&&|^|==|!=")) {
1626+
// In C++ the order of operands of + might matter
1627+
if (!tok->isCpp() ||
1628+
key.parentOp != "+" ||
1629+
!tok->astParent()->valueType() ||
1630+
tok->astParent()->valueType()->isIntegral() ||
1631+
tok->astParent()->valueType()->isFloat() ||
1632+
tok->astParent()->valueType()->pointer > 0)
1633+
std::swap(key.operand1, key.operand2);
1634+
}
16341635

1635-
const auto it = exprIdMap.find(key);
1636-
if (it == exprIdMap.end()) {
1637-
exprIdMap[key] = id;
1638-
tok->astParent()->exprId(id);
1639-
++id;
1640-
} else {
1641-
tok->astParent()->exprId(it->second);
1636+
const auto it = exprIdMap.find(key);
1637+
if (it == exprIdMap.end()) {
1638+
exprIdMap[key] = id;
1639+
tok->astParent()->exprId(id);
1640+
++id;
1641+
} else {
1642+
tok->astParent()->exprId(it->second);
1643+
}
1644+
tok = tok->astParent();
16421645
}
1643-
setParentExprId(tok->astParent(), exprIdMap, id);
16441646
}
16451647
}
16461648

lib/tokenize.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -4838,7 +4838,7 @@ void Tokenizer::setVarIdPass1()
48384838
}
48394839

48404840
// function declaration inside executable scope? Function declaration is of form: type name "(" args ")"
4841-
if (scopeStack.top().isExecutable && Token::Match(tok, "%name% [,)[]")) {
4841+
if (scopeStack.top().isExecutable && !scopeStack.top().isStructInit && Token::Match(tok, "%name% [,)[]")) {
48424842
bool par = false;
48434843
const Token* start;
48444844
Token* end;

test/cli/performance_test.py

+19
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,22 @@ def test_slow_many_scopes(tmpdir):
219219
return EXIT_SUCCESS;
220220
}""")
221221
cppcheck([filename]) # should not take more than ~1 second
222+
223+
@pytest.mark.timeout(20)
224+
def test_crash_array_in_namespace(tmpdir):
225+
# 12847
226+
filename = os.path.join(tmpdir, 'hang.cpp')
227+
with open(filename, 'wt') as f:
228+
f.write(r"""
229+
#define ROW A, A, A, A, A, A, A, A,
230+
#define ROW8 ROW ROW ROW ROW ROW ROW ROW ROW
231+
#define ROW64 ROW8 ROW8 ROW8 ROW8 ROW8 ROW8 ROW8 ROW8
232+
#define ROW512 ROW64 ROW64 ROW64 ROW64 ROW64 ROW64 ROW64 ROW64
233+
#define ROW4096 ROW512 ROW512 ROW512 ROW512 ROW512 ROW512 ROW512 ROW512
234+
namespace N {
235+
static const char A = 'a';
236+
const char a[] = {
237+
ROW4096 ROW4096 ROW4096 ROW4096
238+
};
239+
}""")
240+
cppcheck([filename]) # should not take more than ~5 seconds

0 commit comments

Comments
 (0)