From 7d49e0121ed58ce9cba6bd281e7a47e1652ea031 Mon Sep 17 00:00:00 2001 From: nussaiba shaikh Date: Sun, 18 Oct 2026 23:27:32 +0530 Subject: [PATCH 1/3] clang import: reject AST nodes without the ext tokens the handlers read --- lib/clangimport.cpp | 12 ++++++++++-- test/testclangimport.cpp | 14 +++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/clangimport.cpp b/lib/clangimport.cpp index 495f01cdff4..85a6e0745cb 100644 --- a/lib/clangimport.cpp +++ b/lib/clangimport.cpp @@ -336,7 +336,10 @@ namespace clangimport { public: AstNode(std::string nodeType, const std::string &ext, Data *data) : nodeType(std::move(nodeType)), mExtTokens(splitString(ext)), mData(data) - {} + { + if (mExtTokens.empty()) + throw InternalError(nullptr, "invalid AST node: " + this->nodeType, InternalError::AST); + } std::string nodeType; std::vector children; @@ -1413,7 +1416,10 @@ void clangimport::AstNode::createTokensFunctionDecl(TokenList &tokenList) auto *nestedIn = const_cast(nameToken->scope()); if (prev) { - const std::string addr = *(std::find(mExtTokens.cbegin(), mExtTokens.cend(), "prev") + 1); + const auto it = std::find(mExtTokens.cbegin(), mExtTokens.cend(), "prev"); + if (it + 1 == mExtTokens.cend()) + throw InternalError(nullptr, "invalid AST node: " + nodeType, InternalError::AST); + const std::string addr = *(it + 1); mData->ref(addr, nameToken); } if (!nameToken->function()) { @@ -1556,6 +1562,8 @@ void clangimport::AstNode::createTokensForCXXRecord(TokenList &tokenList) Token * clangimport::AstNode::createTokensVarDecl(TokenList &tokenList) { + if (mExtTokens.size() < 2) + throw InternalError(nullptr, "invalid AST node: " + nodeType, InternalError::AST); const std::string addr = mExtTokens.front(); if (contains(mExtTokens, "static")) addtoken(tokenList, "static"); diff --git a/test/testclangimport.cpp b/test/testclangimport.cpp index bbbd75822f5..ea158299988 100644 --- a/test/testclangimport.cpp +++ b/test/testclangimport.cpp @@ -144,6 +144,7 @@ class TestClangImport : public TestFixture { TEST_CASE(crash2); TEST_CASE(crash3); TEST_CASE(nullNodeInvalidLevel); + TEST_CASE(invalidExtTokens); } std::string parse(const char clang[]) { @@ -1421,7 +1422,7 @@ class TestClangImport : public TestFixture { // getSpelling() indexed mExtTokens[typeIndex - 1] without a lower-bound // check, so a node whose line carries no ext tokens (typeIndex <= 0) read // out of bounds. - (void)parse("`-RecordDecl "); // don't crash + ASSERT_THROW_INTERNAL_EQUALS(parse("`-RecordDecl "), AST, "invalid AST node: RecordDecl"); } void nullNodeInvalidLevel() { @@ -1442,6 +1443,17 @@ class TestClangImport : public TestFixture { " `-CXXRecordDecl 0x2\n"); // don't crash (void)parse("`-CXXRecordDecl 0x1"); // don't crash } + + void invalidExtTokens() { + // nodes without the ext tokens the handlers read must not index out of bounds + ASSERT_THROW_INTERNAL_EQUALS(parse("`-FunctionDecl 0x1 col:5 f 'int ()'\n" + " `-CompoundStmt 0x2 \n" + " `-ReturnStmt 0x3 \n" + " `-IntegerLiteral \n"), AST, "invalid AST node: IntegerLiteral"); + ASSERT_THROW_INTERNAL_EQUALS(parse("`-EnumDecl "), AST, "invalid AST node: EnumDecl"); + ASSERT_THROW_INTERNAL_EQUALS(parse("`-VarDecl 0x1"), AST, "invalid AST node: VarDecl"); + ASSERT_THROW_INTERNAL_EQUALS(parse("`-FunctionDecl 0x1 col:5 f 'int ()' prev"), AST, "invalid AST node: FunctionDecl"); + } }; REGISTER_TEST(TestClangImport) From 3b3df10956942eb3b35632ddd2dacf11a02d2cc5 Mon Sep 17 00:00:00 2001 From: nussaiba shaikh Date: Mon, 19 Oct 2026 13:20:12 +0530 Subject: [PATCH 2/3] clang import: reject address-only function and enumerator declarations --- lib/clangimport.cpp | 11 +++++++++-- test/testclangimport.cpp | 5 +++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/clangimport.cpp b/lib/clangimport.cpp index 85a6e0745cb..f3e9d19a56d 100644 --- a/lib/clangimport.cpp +++ b/lib/clangimport.cpp @@ -1055,7 +1055,10 @@ Token *clangimport::AstNode::createTokens(TokenList &tokenList) return nullptr; } if (nodeType == EnumConstantDecl) { - Token *nameToken = addtoken(tokenList, getSpelling()); + const std::string name = getSpelling(); + if (name.empty()) + throw InternalError(nullptr, "invalid AST node: " + nodeType, InternalError::AST); + Token *nameToken = addtoken(tokenList, name); auto *scope = const_cast(nameToken->scope()); scope->enumeratorList.emplace_back(nameToken->scope()); Enumerator *e = &scope->enumeratorList.back(); @@ -1391,6 +1394,10 @@ Token * clangimport::AstNode::createTokensCall(TokenList &tokenList) void clangimport::AstNode::createTokensFunctionDecl(TokenList &tokenList) { + // an address-only node has no name: addtoken("") adds nothing and would return a null back() + const std::string name = getSpelling(); + if (name.empty()) + throw InternalError(nullptr, "invalid AST node: " + nodeType, InternalError::AST); const bool prev = contains(mExtTokens, "prev"); const bool hasBody = !children.empty() && children.back()->nodeType == CompoundStmt; const bool isStatic = contains(mExtTokens, "static"); @@ -1412,7 +1419,7 @@ void clangimport::AstNode::createTokensFunctionDecl(TokenList &tokenList) if (mExtTokens.size() > 4 && mExtTokens[1] == "parent") addFullScopeNameTokens(tokenList, mData->getScope(mExtTokens[2])); - Token *nameToken = addtoken(tokenList, getSpelling() + getTemplateParameters()); + Token *nameToken = addtoken(tokenList, name + getTemplateParameters()); auto *nestedIn = const_cast(nameToken->scope()); if (prev) { diff --git a/test/testclangimport.cpp b/test/testclangimport.cpp index ea158299988..4aa505bd0a7 100644 --- a/test/testclangimport.cpp +++ b/test/testclangimport.cpp @@ -1453,6 +1453,11 @@ class TestClangImport : public TestFixture { ASSERT_THROW_INTERNAL_EQUALS(parse("`-EnumDecl "), AST, "invalid AST node: EnumDecl"); ASSERT_THROW_INTERNAL_EQUALS(parse("`-VarDecl 0x1"), AST, "invalid AST node: VarDecl"); ASSERT_THROW_INTERNAL_EQUALS(parse("`-FunctionDecl 0x1 col:5 f 'int ()' prev"), AST, "invalid AST node: FunctionDecl"); + // address-only declarations: getSpelling() is empty, addtoken() adds nothing and back() is null + ASSERT_THROW_INTERNAL_EQUALS(parse("`-FunctionDecl 0x1"), AST, "invalid AST node: FunctionDecl"); + ASSERT_THROW_INTERNAL_EQUALS(parse("`-CXXConstructorDecl 0x1"), AST, "invalid AST node: CXXConstructorDecl"); + ASSERT_THROW_INTERNAL_EQUALS(parse("`-CXXMethodDecl 0x1"), AST, "invalid AST node: CXXMethodDecl"); + ASSERT_THROW_INTERNAL_EQUALS(parse("`-EnumConstantDecl 0x1"), AST, "invalid AST node: EnumConstantDecl"); } }; From c0a3af7c93c00e8e6dc3556acc132353263deec4 Mon Sep 17 00:00:00 2001 From: nussaiba shaikh Date: Tue, 20 Oct 2026 22:39:08 +0530 Subject: [PATCH 3/3] clang import: reject VarDecl/FieldDecl without a name token --- lib/clangimport.cpp | 5 +++-- test/testclangimport.cpp | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/clangimport.cpp b/lib/clangimport.cpp index f3e9d19a56d..534948afdea 100644 --- a/lib/clangimport.cpp +++ b/lib/clangimport.cpp @@ -1569,14 +1569,15 @@ void clangimport::AstNode::createTokensForCXXRecord(TokenList &tokenList) Token * clangimport::AstNode::createTokensVarDecl(TokenList &tokenList) { - if (mExtTokens.size() < 2) - throw InternalError(nullptr, "invalid AST node: " + nodeType, InternalError::AST); const std::string addr = mExtTokens.front(); if (contains(mExtTokens, "static")) addtoken(tokenList, "static"); int typeIndex = mExtTokens.size() - 1; while (typeIndex > 1 && std::isalpha(mExtTokens[typeIndex][0])) typeIndex--; + // there must be a name token between the address and the type + if (typeIndex < 2) + throw InternalError(nullptr, "invalid AST node: " + nodeType, InternalError::AST); const std::string type = mExtTokens[typeIndex]; const std::string name = mExtTokens[typeIndex - 1]; const Token *startToken = tokenList.back(); diff --git a/test/testclangimport.cpp b/test/testclangimport.cpp index 4aa505bd0a7..ef87c0d6347 100644 --- a/test/testclangimport.cpp +++ b/test/testclangimport.cpp @@ -1452,6 +1452,9 @@ class TestClangImport : public TestFixture { " `-IntegerLiteral \n"), AST, "invalid AST node: IntegerLiteral"); ASSERT_THROW_INTERNAL_EQUALS(parse("`-EnumDecl "), AST, "invalid AST node: EnumDecl"); ASSERT_THROW_INTERNAL_EQUALS(parse("`-VarDecl 0x1"), AST, "invalid AST node: VarDecl"); + ASSERT_THROW_INTERNAL_EQUALS(parse("`-VarDecl 0x1 'int'"), AST, "invalid AST node: VarDecl"); + ASSERT_THROW_INTERNAL_EQUALS(parse("`-VarDecl 0x1 "), AST, "invalid AST node: VarDecl"); + ASSERT_THROW_INTERNAL_EQUALS(parse("`-FieldDecl 0x1 'int'"), AST, "invalid AST node: FieldDecl"); ASSERT_THROW_INTERNAL_EQUALS(parse("`-FunctionDecl 0x1 col:5 f 'int ()' prev"), AST, "invalid AST node: FunctionDecl"); // address-only declarations: getSpelling() is empty, addtoken() adds nothing and back() is null ASSERT_THROW_INTERNAL_EQUALS(parse("`-FunctionDecl 0x1"), AST, "invalid AST node: FunctionDecl");