Skip to content

Commit 5b99b44

Browse files
authored
Fix uninitialized use of TIntermediate::resource (KhronosGroup#2424)
TIntermediate was constructed without initializing any of the `resources` fields, and `TProgram::linkStage()` was not calling `TIntermediate::setLimits()` after constructing new `TIntermediate`s for non-first stages. Fields of `resources` were then read in `TIntermediate::finalCheck()` triggering undefined behavior. This CL makes three changes: (1) `TIntermediate::setLimits()` is now called for non-first stages by copying the `firstIntermediate`'s limits. This ensures that the `resources` fields is initialized, fixing the bug. (2) `TIntermediate::resources` is now wrapped in a `MustBeAssigned<>` helper struct, asserting in non-release builds that this field is always initialized before reading. (3) `TIntermediate::resources` is now zero-initialized, so that if the `TIntermediate::resources` field is not set in a release build (and so the `assert()` will be disabled) behavior is still deterministic. Fixes KhronosGroup#2423
1 parent f4f1d8a commit 5b99b44

File tree

3 files changed

+23
-4
lines changed

3 files changed

+23
-4
lines changed

glslang/MachineIndependent/ShaderLang.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2016,7 +2016,7 @@ bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
20162016
intermediate[stage] = new TIntermediate(stage,
20172017
firstIntermediate->getVersion(),
20182018
firstIntermediate->getProfile());
2019-
2019+
intermediate[stage]->setLimits(firstIntermediate->getLimits());
20202020

20212021
// The new TIntermediate must use the same origin as the original TIntermediates.
20222022
// Otherwise linking will fail due to different coordinate systems.

glslang/MachineIndependent/linkValidate.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -736,10 +736,10 @@ void TIntermediate::finalCheck(TInfoSink& infoSink, bool keepUncalled)
736736

737737
// "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the
738738
// implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents."
739-
if (xfbBuffers[b].stride > (unsigned int)(4 * resources.maxTransformFeedbackInterleavedComponents)) {
739+
if (xfbBuffers[b].stride > (unsigned int)(4 * resources->maxTransformFeedbackInterleavedComponents)) {
740740
error(infoSink, "xfb_stride is too large:");
741741
infoSink.info.prefix(EPrefixError);
742-
infoSink.info << " xfb_buffer " << (unsigned int)b << ", components (1/4 stride) needed are " << xfbBuffers[b].stride/4 << ", gl_MaxTransformFeedbackInterleavedComponents is " << resources.maxTransformFeedbackInterleavedComponents << "\n";
742+
infoSink.info << " xfb_buffer " << (unsigned int)b << ", components (1/4 stride) needed are " << xfbBuffers[b].stride/4 << ", gl_MaxTransformFeedbackInterleavedComponents is " << resources->maxTransformFeedbackInterleavedComponents << "\n";
743743
}
744744
}
745745

glslang/MachineIndependent/localintermediate.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,23 @@ class TNumericFeatures {
259259
unsigned int features;
260260
};
261261

262+
// MustBeAssigned wraps a T, asserting that it has been assigned with
263+
// operator =() before attempting to read with operator T() or operator ->().
264+
// Used to catch cases where fields are read before they have been assigned.
265+
template<typename T>
266+
class MustBeAssigned
267+
{
268+
public:
269+
MustBeAssigned() = default;
270+
MustBeAssigned(const T& v) : value(v) {}
271+
operator const T&() const { assert(isSet); return value; }
272+
const T* operator ->() const { assert(isSet); return &value; }
273+
MustBeAssigned& operator = (const T& v) { value = v; isSet = true; return *this; }
274+
private:
275+
T value;
276+
bool isSet = false;
277+
};
278+
262279
//
263280
// Set of helper functions to help parse and build the tree.
264281
//
@@ -270,6 +287,7 @@ class TIntermediate {
270287
profile(p), version(v),
271288
#endif
272289
treeRoot(0),
290+
resources(TBuiltInResource{}),
273291
numEntryPoints(0), numErrors(0), numPushConstants(0), recursive(false),
274292
invertY(false),
275293
useStorageBuffer(false),
@@ -406,6 +424,7 @@ class TIntermediate {
406424
int getNumErrors() const { return numErrors; }
407425
void addPushConstantCount() { ++numPushConstants; }
408426
void setLimits(const TBuiltInResource& r) { resources = r; }
427+
const TBuiltInResource& getLimits() const { return resources; }
409428

410429
bool postProcess(TIntermNode*, EShLanguage);
411430
void removeTree();
@@ -955,7 +974,7 @@ class TIntermediate {
955974
SpvVersion spvVersion;
956975
TIntermNode* treeRoot;
957976
std::set<std::string> requestedExtensions; // cumulation of all enabled or required extensions; not connected to what subset of the shader used them
958-
TBuiltInResource resources;
977+
MustBeAssigned<TBuiltInResource> resources;
959978
int numEntryPoints;
960979
int numErrors;
961980
int numPushConstants;

0 commit comments

Comments
 (0)