KhronosGroup / KhronosGroup/glslang
glslangValidator: out of bounds bug when function parameter is a struct containing an array sized by specialized constant
- Dominant language
- C++
- Stars
- 3.6k
- Forks
- 989
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 31
Description
Case:
A struct containing an array sized by a specialization constant being passed into a function.
Problem:
When compiling the GLSL shader below to Spir-V using `glslangValidator` , a local copy of the array is created before passing it to the function. However, it uses the size of the specialization constant's default value to copy the values. This either causes an out of bounds index or doesn't copy all values if the specialization constant is defined to be anything else. When the function is inlined, this does not happen.
Expected result:
- Best case (using const qualifier for the function parameter): the same result as the inlined version (see glsl code below), OR:
- An error would be triggered, similar to assigning the struct to initialize a local variable: "'initializer' : can't use with types containing arrays sized with a specialization constant"
```
glslangValidator.exe test.glsl -e main -o test.spv -G -v -S frag --auto-map-locations
```
The original `test.glsl` fragment shader:
```
#version 310 es
precision highp float;
layout (constant_id = 0) const int VALUE_COUNT = 16;
out vec4 fragOut;
struct ValueStruct
{
float values[VALUE_COUNT];
};
layout (std140, binding = 3) uniform unifBuffer
{
ValueStruct container;
};
// adding const to ascertain it's read-only doesn't help, could otherwise be a good hint
float getTotal(const ValueStruct container)
{
float total = 0.0;
for (int j = 0; j < VALUE_COUNT; ++j) {
total += container.values[j];
}
return total;
}
void main()
{
float total = 0.0;
// inlining the method works
// #define INLINE
#ifdef INLINE
// inlined correctly replicates the double loop
for (int j = 0; j < VALUE_COUNT; ++j) {
total += container.values[j];
}
#else
// when using a function call container.values[0 - 16] is copied to a local version (so using the default VALUE_COUNT value), failing when the spec constant is defined to be lower
total += getTotal(container);
#endif
fragOut = vec4(total);
}
```
When compiling the Spir-V code back to glsl:
```
#version 310 es
precision mediump float;
precision highp int;
#ifndef SPIRV_CROSS_CONSTANT_ID_0
#define SPIRV_CROSS_CONSTANT_ID_0 16
#endif
const int VALUE_COUNT = SPIRV_CROSS_CONSTANT_ID_0;
struct ValueStruct
{
highp float values[VALUE_COUNT];
};
layout(binding = 3, std140) uniform unifBuffer
{
ValueStruct container;
} _46;
layout(location = 0) out highp vec4 fragOut;
highp float getTotal(ValueStruct container)
{
highp float total = 0.0;
for (mediump int j = 0; j < VALUE_COUNT; j++)
{
total += container.values[j];
}
return total;
}
void main()
{
highp float total = 0.0;
ValueStruct param;
// Incorrect!
param.values[0] = _46.container.values[0];
param.values[1] = _46.container.values[1];
param.values[2] = _46.container.values[2];
param.values[3] = _46.container.values[3];
param.values[4] = _46.container.values[4];
param.values[5] = _46.container.values[5];
param.values[6] = _46.container.values[6];
param.values[7] = _46.container.values[7];
param.values[8] = _46.container.values[8];
param.values[9] = _46.container.values[9];
param.values[10] = _46.container.values[10];
param.values[11] = _46.container.values[11];
param.values[12] = _46.container.values[12];
param.values[13] = _46.container.values[13];
param.values[14] = _46.container.values[14];
param.values[15] = _46.container.values[15];
total += getTotal(param);
fragOut = vec4(total);
}
```
Contributor guide
Research direction
Start by compiling the supplied test.glsl with glslangValidator, including a specialization value different from the default, and inspect the generated SPIR-V or reconstructed GLSL. Trace function-parameter handling for structs containing arrays sized by specialization constants. Done means the function call matches the inlined behavior or emits the documented diagnostic instead of copying the default-sized array.
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
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100