KhronosGroup / KhronosGroup/glslang
HLSL: NULL deref in EatNumThreads when numthreads arg is non-constant (cascading -C)
- Dominant language
- C++
- Stars
- 3.6k
- Forks
- 989
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 31
Description
## Summary
`[numthreads(non-const, ...)]` under cascading errors (`-C`) causes a NULL dereference in `HlslParseContext::handleEntryPointAttributes` (`EatNumThreads`).
`getAsConstantUnion()` returns NULL for a non-constant argument, but the code still dereferences it.
glslang CLI without `-C` only reports a compile error. **shaderc always enables `EShMsgCascadingErrors`**, so the same input crashes through shaderc.
Related but different: #4093, #4094 (other HLSL NULL paths); #4324 fixed over-long `numthreads` OOB, not this NULL deref.
## PoC
```hlsl
[numthreads(hhhh1, 1)]
void main() {}
```
`hhhh1` is not a compile-time constant → `getAsConstantUnion()` returns NULL → SEGV.
## Repro
```bash
git clone --depth 1 https://github.com/KhronosGroup/glslang.git
cd glslang
cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_C_FLAGS='-fsanitize=address -fno-omit-frame-pointer -g -O1' \
-DCMAKE_CXX_FLAGS='-fsanitize=address -fno-omit-frame-pointer -g -O1' \
-DCMAKE_EXE_LINKER_FLAGS='-fsanitize=address' \
-DENABLE_OPT=OFF \
-DENABLE_HLSL=ON
cmake --build build -j"$(nproc)"
cd ..
cat > numthreads_tiny.hlsl <<'EOF'
[numthreads(hhhh1, 1)]
void main() {}
EOF
ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:symbolize=1 \
glslang/build/StandAlone/glslang -D -e main -S comp -V -C ./numthreads_tiny.hlsl
```
Verified on tip `2d0f196` (2026-09-18), Ubuntu + clang ASAN.
## Expected (unpatched)
```
ERROR: AddressSanitizer: SEGV ...
#1 ... HlslParseContext::handleEntryPointAttributes ... hlslParseHelper.cpp:1775
SUMMARY: AddressSanitizer: SEGV ... ConstantUnion.h:915
```
Without `-C`: compile errors only, no ASAN (exit 2).
## Bug
File: `glslang/HLSL/hlslParseHelper.cpp` (`EatNumThreads`)
```cpp
for (int lid = 0; lid < int(sequence.size()); ++lid)
intermediate.setLocalSize(lid,
sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
// ^^^^^^^^^^^^^^^^^^ NULL when arg is non-constant
```
Neighbors like `EatInstance` already use `TAttributeArgs::getInt`, which returns `false` on NULL instead of crashing.
## Suggested fix
```diff
--- a/glslang/HLSL/hlslParseHelper.cpp
+++ b/glslang/HLSL/hlslParseHelper.cpp
@@ -1771,8 +1771,16 @@
if (sequence.size() > 3) {
error(loc, "expected at most three arguments", "numthreads", "");
break;
}
- for (int lid = 0; lid < int(sequence.size()); ++lid)
- intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst());
+ for (int lid = 0; lid < int(sequence.size()); ++lid) {
+ int size = 1;
+ if (!it->getInt(size, lid)) {
+ error(loc, "number of threads must be a constant integer", "numthreads", "");
+ break;
+ }
+ intermediate.setLocalSize(lid, size);
+ }
break;
}
```
## After patch
Same command prints compile errors only (no ASAN SEGV):
```
ERROR: ./numthreads_tiny.hlsl:1: 'hhhh1' : unknown variable
ERROR: ./numthreads_tiny.hlsl:2: 'numthreads' : number of threads must be a constant integer
ERROR: 2 compilation errors. No code generated.
```
Contributor guide
Research direction
Start in glslang/HLSL/hlslParseHelper.cpp at HlslParseContext::handleEntryPointAttributes and the EatNumThreads path, then reproduce with the provided non-constant numthreads input under the ASAN glslang command. Compare the handling with the nearby EatInstance path. Done means cascading errors report compilation failures without an ASAN NULL dereference.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 84/100