| Issue |
184020
|
| Summary |
[clangd] NULL pointer dereference in `VisitCXXThrowExpr` when using `textDocument/typeDefinition` on a bare `throw;` _expression_
|
| Labels |
new issue
|
| Assignees |
|
| Reporter |
hongtaihu
|
# Summary
clangd crashes with a NULL pointer dereference when a `textDocument/typeDefinition` (or `textDocument/hover`) LSP request is issued with the cursor on a bare `throw;` rethrow _expression_ (i.e., a `throw` statement with no operand).
The crash is in `XRefs.cpp:VisitCXXThrowExpr`, which calls `S->getSubExpr()->getType()` without checking for `nullptr`. For a rethrow _expression_ (`throw;`), `CXXThrowExpr::getSubExpr()` is documented to return `nullptr` (it uses `cast_or_null`, and the class itself checks `if (!getSubExpr())` in `getEndLoc()`).
Discovered via fuzzing with a fuzzer
[clangd_bug_repro.gz](https://github.com/user-attachments/files/25664404/clangd_bug_repro.gz)
on clangd trunk `aff5afc48df63615053b2432da198a4932435c3f`.
---
# Affected Version
- **clangd**: 23.0.0git (trunk commit `aff5afc48df63615053b2432da198a4932435c3f`)
- **Likely affected**: all versions containing `VisitCXXThrowExpr` in `XRefs.cpp`
---
# Steps to Reproduce
## Option A — Minimal self-contained Python reproducer
Requires an ASAN-instrumented clangd build. Run:
```bash
python3 repro.py
# or explicitly: python3 repro.py /path/to/clangd_asan
```
The script uses the inline 6-line source from Option B below and sends a `textDocument/typeDefinition` request at `position: {line: 4, character: 8}` (0-indexed), which is the bare `throw;` rethrow.
## Option B — Manual LSP interaction
**Source file** (`minimal.cpp`):
```cpp
void f() {
try {
throw 42;
} catch (int) {
throw; // <-- send typeDefinition/hover here
}
}
```
**LSP messages** (in order):
1. `initialize`
2. `initialized`
3. `textDocument/didOpen` — open `minimal.cpp`
4. *(wait ~8 s for AST build)*
5. `textDocument/typeDefinition` with `position: {line: 4, character: 8}` — cursor on `throw;`
*(0-indexed; "line 4" in the snippet above, which is the bare `throw;`)*
---
# ASAN Stack Trace
```
==<PID>==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008
==<PID>==The signal is caused by a READ memory access.
==<PID>==Hint: address points to the zero page.
#0 syscall sysdeps/unix/sysv/linux/x86_64/syscall.S:38
#1 SignalHandler llvm/lib/Support/Unix/Signals.inc:429
#2 <libc signal frame>
#3 clang::Expr::getType() const clang/include/clang/AST/Expr.h:144
#4 VisitCXXThrowExpr clangd/XRefs.cpp:2090 <-- CRASH
#5 Visit clang/AST/StmtNodes.inc:670
#6 typeForNode clangd/XRefs.cpp:2133
#7 operator() (findType lambda) clangd/XRefs.cpp:2209
#8 operator() (findType lambda) clangd/XRefs.cpp:2217
#9 callback_fn llvm/include/llvm/ADT/STLFunctionalExtras.h:46
#10 SelectionTree::createEach clangd/Selection.cpp:1082
#11 findType clangd/XRefs.cpp:2215
#12 operator() (ClangdServer lambda) clangd/ClangdServer.cpp:990
...
[AST worker thread]
```
**ASAN classification**: `SourceAv` / `NOT_EXPLOITABLE` — read access violation at address `0x8` (null pointer + field offset).
---
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs