llvm / llvm/llvm-project

[clangd][ASAN] textDocument/typeDefinition can trigger stack-overflow in TypePrinter/BuiltinType::getName

Open
#196,794 1 comment 0 reactions 0 assignees View on GitHub
clangd compiler-rt:asan
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

Current ASAN-enabled clangd can be made to crash while handling a semantic query on a mutated descendant of `libcxxabi/test/catch_multi_level_pointer.pass.cpp`.

Environment:
- clangd 23.0.0git
- commit: aff5afc48df63615053b2432da198a4932435c3f
- Ubuntu 24.04 x86_64
- ASAN-enabled clangd

This reproduces on the current ASAN build, but I cannot reproduce it on the current non-ASAN/assert clangd build.

The smallest request sequence I am using is:

1. `initialize`
2. `initialized`
3. `textDocument/didOpen`
4. `textDocument/typeDefinition`
5. `shutdown`
6. `exit`

A reduced reproducer script is below.
source code
```cxx
template
bool test_conversion(To) { return true; }
bool test_conversion(...) { return false; }
template
struct CreatePointer {
Pointer operator()() const {
}
};
template
struct CreatePointer {
Tp* operator()() const {
}
};

template
void catch_pointer_test() {
}
template
struct TestTypes {
typedef Tp* Type;
typedef Tp const* CType;
typedef Tp volatile* VType;
typedef Tp const volatile* CVType;
};
template
struct TestTypes {
typedef Member (Class::*Type);
typedef const Member (Class::*CType);
typedef volatile Member (Class::*VType);
typedef const volatile Member (Class::*CVType);
};
template
struct generate_tests_imp {
typedef TestTypes ThrowTypes;
typedef TestTypes CatchTypes;
void operator()() {
typedef typename ThrowTypes::Type Type;
run_catch_tests();
}
template
void run_catch_tests() {
typedef typename CatchTypes::Type Type;
catch_pointer_test();
generate_tests_imp()();
}
};
template
struct generate_tests_imp {

};
template
struct generate_tests : generate_tests_imp {};
int main()
{
generate_tests()();
}
```
## What happens

Under ASAN, clangd crashes in an AST worker thread with:

- `AddressSanitizer: stack-overflow`

The stack is dominated by repeated recursion in type printing:

- `clang::BuiltinType::getName`
- `TypePrinter::printBuiltinBefore`
- `TypePrinter::printBefore`
- `TypePrinter::printPointerBefore`

Under gdb, the worker thread (`Worker:main.cxx`) eventually stops with `SIGSEGV`, but the recursive stack is the same `TypePrinter` chain.
The crash is also not tightly tied to one precise cursor token: many nearby and even clearly out-of-range positions reproduce the same family once `typeDefinition` is issued.

Contributor guide

Open the contributing guide

Research direction

Start with the reduced reproducer based on libcxxabi/test/catch_multi_level_pointer.pass.cpp and issue the listed initialize, didOpen, and textDocument/typeDefinition sequence against an ASAN-enabled clangd. Trace the repeated recursion through clang::BuiltinType::getName and TypePrinter::printBuiltinBefore, printBefore, and printPointerBefore. Done means the sequence no longer produces an AddressSanitizer stack-overflow.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.