[clangd][ASAN] textDocument/typeDefinition can trigger stack-overflow in TypePrinter/BuiltinType::getName
- 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
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