Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions lib/clangimport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<AstNodePtr> children;

Expand Down Expand Up @@ -1052,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<Scope *>(nameToken->scope());
scope->enumeratorList.emplace_back(nameToken->scope());
Enumerator *e = &scope->enumeratorList.back();
Expand Down Expand Up @@ -1388,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");
Expand All @@ -1409,11 +1419,14 @@ 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<Scope *>(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()) {
Expand Down Expand Up @@ -1562,6 +1575,9 @@ Token * clangimport::AstNode::createTokensVarDecl(TokenList &tokenList)
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

This check covers VarDecl/FieldDecl only. A ParmVarDecl with the same shape is tokenized in the parameter loop of createTokensFunctionDecl (child->getSpelling() → child->addtoken(...) → mData->varDecl(...)). There the address 0x2 becomes the name, a number token gets a varId, and cppcheck aborts:

TranslationUnitDecl 0x0 <<invalid sloc>> <invalid sloc>
`-FunctionDecl 0x1 <a.cpp:1:1, col:20> col:5 f 'int ()'
  `-ParmVarDecl 0x2 <col:1>
cppcheck: lib/token.cpp:195: void Token::update_property_info(): Assertion `!mImpl->mVarId || mTokType == eVariable' failed.

ParmVarDecl 0x2 'int' aborts the same way. That loop needs the same check: throw when the parameter has no token between the address and the type. Add a test case to invalidExtTokens for it.

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();
Expand Down
22 changes: 21 additions & 1 deletion test/testclangimport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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[]) {
Expand Down Expand Up @@ -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() {
Expand All @@ -1442,6 +1443,25 @@ 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 <a.cpp:1:1, col:20> col:5 f 'int ()'\n"
" `-CompoundStmt 0x2 <col:9, col:20>\n"
" `-ReturnStmt 0x3 <col:11, col:18>\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("`-VarDecl 0x1 'int'"), AST, "invalid AST node: VarDecl");
ASSERT_THROW_INTERNAL_EQUALS(parse("`-VarDecl 0x1 <col:1>"), 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 <a.cpp:1:1, col:20> 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");
}
};

REGISTER_TEST(TestClangImport)
Loading