Skip to content

Commit a202452

Browse files
authored
Fix #15123: clang import: reject AST nodes without the ext tokens the handlers read (#8948)
Follow-up to #8939. Several `createTokens` handlers call `mExtTokens.back()`/`front()` or index relative to `size()` without checking that the node line had any ext tokens, so a `--clang` dump line with nothing after the node name (e.g. `` `-IntegerLiteral `` followed by a space) reads out of bounds. The same happens for StringLiteral, CharacterLiteral, CXXBoolLiteralExpr, LabelStmt, EnumDecl, DeclRefExpr and others, for a VarDecl that only has an address (`mExtTokens[typeIndex - 1]` with `typeIndex` 0), and for a FunctionDecl ending in `prev` (dereferences `end()`). The AstNode constructor now throws an AST internal error when the node has no ext tokens, and `createTokensVarDecl` / `createTokensFunctionDecl` throw the same error for the other two shapes. An address-only FunctionDecl / CXXMethodDecl / CXXConstructorDecl or EnumConstantDecl (`` `-FunctionDecl 0x1 ``) is rejected as well: `getSpelling()` is empty there, `addtoken("")` adds nothing and returns a null `back()`, so both handlers now check the spelling before adding any token. Real clang dumps always carry the node address, location, name and type, so they are unaffected (checked `--clang` on the samples and a few lib files). `crash2` now expects the internal error instead of silently producing no tokens. The size()-2 sites are guarded in #8939 and not touched here.
1 parent 07254e9 commit a202452

2 files changed

Lines changed: 48 additions & 5 deletions

File tree

‎lib/clangimport.cpp‎

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,10 @@ namespace clangimport {
336336
public:
337337
AstNode(std::string nodeType, const std::string &ext, Data *data)
338338
: nodeType(std::move(nodeType)), mExtTokens(splitString(ext)), mData(data)
339-
{}
339+
{
340+
if (mExtTokens.empty())
341+
throw InternalError(nullptr, "invalid AST node: " + this->nodeType, InternalError::AST);
342+
}
340343
std::string nodeType;
341344
std::vector<AstNodePtr> children;
342345

@@ -1052,7 +1055,10 @@ Token *clangimport::AstNode::createTokens(TokenList &tokenList)
10521055
return nullptr;
10531056
}
10541057
if (nodeType == EnumConstantDecl) {
1055-
Token *nameToken = addtoken(tokenList, getSpelling());
1058+
const std::string name = getSpelling();
1059+
if (name.empty())
1060+
throw InternalError(nullptr, "invalid AST node: " + nodeType, InternalError::AST);
1061+
Token *nameToken = addtoken(tokenList, name);
10561062
auto *scope = const_cast<Scope *>(nameToken->scope());
10571063
scope->enumeratorList.emplace_back(nameToken->scope());
10581064
Enumerator *e = &scope->enumeratorList.back();
@@ -1388,6 +1394,10 @@ Token * clangimport::AstNode::createTokensCall(TokenList &tokenList)
13881394

13891395
void clangimport::AstNode::createTokensFunctionDecl(TokenList &tokenList)
13901396
{
1397+
// an address-only node has no name: addtoken("") adds nothing and would return a null back()
1398+
const std::string name = getSpelling();
1399+
if (name.empty())
1400+
throw InternalError(nullptr, "invalid AST node: " + nodeType, InternalError::AST);
13911401
const bool prev = contains(mExtTokens, "prev");
13921402
const bool hasBody = !children.empty() && children.back()->nodeType == CompoundStmt;
13931403
const bool isStatic = contains(mExtTokens, "static");
@@ -1409,11 +1419,14 @@ void clangimport::AstNode::createTokensFunctionDecl(TokenList &tokenList)
14091419
if (mExtTokens.size() > 4 && mExtTokens[1] == "parent")
14101420
addFullScopeNameTokens(tokenList, mData->getScope(mExtTokens[2]));
14111421

1412-
Token *nameToken = addtoken(tokenList, getSpelling() + getTemplateParameters());
1422+
Token *nameToken = addtoken(tokenList, name + getTemplateParameters());
14131423
auto *nestedIn = const_cast<Scope *>(nameToken->scope());
14141424

14151425
if (prev) {
1416-
const std::string addr = *(std::find(mExtTokens.cbegin(), mExtTokens.cend(), "prev") + 1);
1426+
const auto it = std::find(mExtTokens.cbegin(), mExtTokens.cend(), "prev");
1427+
if (it + 1 == mExtTokens.cend())
1428+
throw InternalError(nullptr, "invalid AST node: " + nodeType, InternalError::AST);
1429+
const std::string addr = *(it + 1);
14171430
mData->ref(addr, nameToken);
14181431
}
14191432
if (!nameToken->function()) {
@@ -1460,6 +1473,9 @@ void clangimport::AstNode::createTokensFunctionDecl(TokenList &tokenList)
14601473
AstNodePtr child = children[i];
14611474
if (child->nodeType != ParmVarDecl)
14621475
continue;
1476+
// there must be a token between the address and the type
1477+
if (child->mExtTokens.size() < 3)
1478+
throw InternalError(nullptr, "invalid AST node: " + child->nodeType, InternalError::AST);
14631479
if (tokenList.back() != par1)
14641480
addtoken(tokenList, ",");
14651481
const Type *recordType = addTypeTokens(tokenList, child->mExtTokens.back(), nestedIn);
@@ -1562,6 +1578,9 @@ Token * clangimport::AstNode::createTokensVarDecl(TokenList &tokenList)
15621578
int typeIndex = mExtTokens.size() - 1;
15631579
while (typeIndex > 1 && std::isalpha(mExtTokens[typeIndex][0]))
15641580
typeIndex--;
1581+
// there must be a name token between the address and the type
1582+
if (typeIndex < 2)
1583+
throw InternalError(nullptr, "invalid AST node: " + nodeType, InternalError::AST);
15651584
const std::string type = mExtTokens[typeIndex];
15661585
const std::string name = mExtTokens[typeIndex - 1];
15671586
const Token *startToken = tokenList.back();

‎test/testclangimport.cpp‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ class TestClangImport : public TestFixture {
144144
TEST_CASE(crash2);
145145
TEST_CASE(crash3);
146146
TEST_CASE(nullNodeInvalidLevel);
147+
TEST_CASE(invalidExtTokens);
147148
}
148149

149150
std::string parse(const char clang[]) {
@@ -1421,7 +1422,7 @@ class TestClangImport : public TestFixture {
14211422
// getSpelling() indexed mExtTokens[typeIndex - 1] without a lower-bound
14221423
// check, so a node whose line carries no ext tokens (typeIndex <= 0) read
14231424
// out of bounds.
1424-
(void)parse("`-RecordDecl "); // don't crash
1425+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-RecordDecl "), AST, "invalid AST node: RecordDecl");
14251426
}
14261427

14271428
void nullNodeInvalidLevel() {
@@ -1442,6 +1443,29 @@ class TestClangImport : public TestFixture {
14421443
" `-CXXRecordDecl 0x2\n"); // don't crash
14431444
(void)parse("`-CXXRecordDecl 0x1"); // don't crash
14441445
}
1446+
1447+
void invalidExtTokens() {
1448+
// nodes without the ext tokens the handlers read must not index out of bounds
1449+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-FunctionDecl 0x1 <a.cpp:1:1, col:20> col:5 f 'int ()'\n"
1450+
" `-CompoundStmt 0x2 <col:9, col:20>\n"
1451+
" `-ReturnStmt 0x3 <col:11, col:18>\n"
1452+
" `-IntegerLiteral \n"), AST, "invalid AST node: IntegerLiteral");
1453+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-EnumDecl "), AST, "invalid AST node: EnumDecl");
1454+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-VarDecl 0x1"), AST, "invalid AST node: VarDecl");
1455+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-VarDecl 0x1 'int'"), AST, "invalid AST node: VarDecl");
1456+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-VarDecl 0x1 <col:1>"), AST, "invalid AST node: VarDecl");
1457+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-FieldDecl 0x1 'int'"), AST, "invalid AST node: FieldDecl");
1458+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-FunctionDecl 0x1 <a.cpp:1:1, col:20> col:5 f 'int ()' prev"), AST, "invalid AST node: FunctionDecl");
1459+
// address-only declarations: getSpelling() is empty, addtoken() adds nothing and back() is null
1460+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-FunctionDecl 0x1"), AST, "invalid AST node: FunctionDecl");
1461+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-CXXConstructorDecl 0x1"), AST, "invalid AST node: CXXConstructorDecl");
1462+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-CXXMethodDecl 0x1"), AST, "invalid AST node: CXXMethodDecl");
1463+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-EnumConstantDecl 0x1"), AST, "invalid AST node: EnumConstantDecl");
1464+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-FunctionDecl 0x1 <a.cpp:1:1, col:20> col:5 f 'int ()'\n"
1465+
" `-ParmVarDecl 0x2 <col:1>\n"), AST, "invalid AST node: ParmVarDecl");
1466+
ASSERT_THROW_INTERNAL_EQUALS(parse("`-FunctionDecl 0x1 <a.cpp:1:1, col:20> col:5 f 'int ()'\n"
1467+
" `-ParmVarDecl 0x2 'int'\n"), AST, "invalid AST node: ParmVarDecl");
1468+
}
14451469
};
14461470

14471471
REGISTER_TEST(TestClangImport)

0 commit comments

Comments
 (0)