KhronosGroup / KhronosGroup/glslang

HLSL: NULL deref in EatNumThreads when numthreads arg is non-constant (cascading -C)

Open Beginner friendly
#4,433 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.