llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.63k stars 11.83k forks source link

[clang] API: isFlexibleArrayMember will crash when the type source location associated with the Expr is invalid and macros are skipped #63074

Open 2over12 opened 1 year ago

2over12 commented 1 year ago

Minimal reproducing example:

#include <clang/AST/ASTContext.h>
#include <clang/AST/Decl.h>
#include <clang/AST/DeclarationName.h>
#include <clang/AST/Expr.h>
#include <clang/AST/OperationKinds.h>
#include <clang/AST/Type.h>
#include <clang/Basic/LangOptions.h>
#include <clang/Basic/SourceLocation.h>
#include <clang/Basic/Specifiers.h>
#include <clang/Frontend/ASTUnit.h>
#include <clang/Sema/Sema.h>
#include <clang/Tooling/Tooling.h>
#include <llvm/ADT/APInt.h>

clang::IdentifierInfo *CreateIdentifier(std::string name, clang::ASTContext& ctx ) {
  std::string str{""};
  for (auto chr : name) {
    str.push_back(std::isalnum(chr) ? chr : '_');
  }
  return &ctx.Idents.get(str);
}

int main(int argc, char *argv[]) {
    auto ast = clang::tooling::buildASTFromCode("");

    auto rdecl = clang::RecordDecl::Create(ast->getASTContext(), clang::TagTypeKind::TTK_Struct, ast->getASTContext().getTranslationUnitDecl(), clang::SourceLocation(), clang::SourceLocation(), CreateIdentifier("test", ast->getASTContext()));
    auto& sema = ast->getSema();
    auto chr_ty = ast->getASTContext().getUnsignedWCharType();
    auto int_sz = clang::IntegerLiteral::Create( ast->getASTContext(), llvm::APInt(64, 1, false), ast->getASTContext().getIntTypeForBitwidth(64, 0),  clang::SourceLocation());
    auto arr_ty = ast->getASTContext().getConstantArrayType(chr_ty, llvm::APInt(64, 1, false), int_sz, clang::ArrayType::ArraySizeModifier::Normal, 0);
    auto fld = sema.CheckFieldDecl(clang::DeclarationName(CreateIdentifier("test_field", ast->getASTContext())), arr_ty, ast->getASTContext().getTrivialTypeSourceInfo(arr_ty), rdecl, clang::SourceLocation(), false, nullptr, clang::ICIS_NoInit, 
        clang::SourceLocation(), clang::AccessSpecifier::AS_none,nullptr);
    rdecl->completeDefinition();

    auto record_ty = ast->getASTContext().getRecordType(rdecl);
    clang::CXXScopeSpec ss;
    auto dap{clang::DeclAccessPair::make(fld, fld->getAccess())};
    auto gv = clang::VarDecl::Create(ast->getASTContext(), ast->getASTContext().getTranslationUnitDecl(), clang::SourceLocation(), clang::SourceLocation(), CreateIdentifier("test_gv", ast->getASTContext()),record_ty,ast->getASTContext().getTrivialTypeSourceInfo(record_ty), clang::SC_None);

    auto ref = sema.BuildDeclRefExpr(gv, gv->getType(), clang::ExprValueKind::VK_LValue, clang::SourceLocation(), nullptr);

    auto fld_ref = sema.BuildFieldReferenceExpr(ref, false, clang::SourceLocation(),ss, fld, dap,clang::DeclarationNameInfo());

    assert(fld_ref.isUsable());

    assert(fld_ref.get()->isFlexibleArrayMemberLike(ast->getASTContext(), clang::LangOptions::StrictFlexArraysLevelKind::Default, true));
}

isFlexibleArrayMemberLike uses the source type info to attempt to determine if the size is the result of a macro. This behavior can lead to a nullptr being passed to dyn_cast if the source range associated with the type info is an empty location.

Unfortunately, CheckArrayAccess uses ignoreTemplate meaning in cases where the API is used for code generation (ie. when source locs will not exist) a crash will occur.

An example patch that treats type info without a location as a non-macro size is available here

llvmbot commented 1 year ago

@llvm/issue-subscribers-clang-frontend

moodyhunter commented 12 months ago

I'm also experiencing this bug when using clangd with the following file content:

typedef whatever_it_is_it_must_be_undefined features_array_t[10];

typedef struct
{
    features_array_t features;
} state_t;

void my_func()
{
    state_t *state;
    state->features[1];
}

cross reference: https://github.com/clangd/clangd/issues/1806

shafik commented 11 months ago

CC @AaronBallman