Fix uninitialized use of TIntermediate::resource (#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 #2423
This commit is contained in:
parent
f4f1d8a352
commit
5b99b448b3
3 changed files with 23 additions and 4 deletions
|
|
@ -259,6 +259,23 @@ private:
|
|||
unsigned int features;
|
||||
};
|
||||
|
||||
// MustBeAssigned wraps a T, asserting that it has been assigned with
|
||||
// operator =() before attempting to read with operator T() or operator ->().
|
||||
// Used to catch cases where fields are read before they have been assigned.
|
||||
template<typename T>
|
||||
class MustBeAssigned
|
||||
{
|
||||
public:
|
||||
MustBeAssigned() = default;
|
||||
MustBeAssigned(const T& v) : value(v) {}
|
||||
operator const T&() const { assert(isSet); return value; }
|
||||
const T* operator ->() const { assert(isSet); return &value; }
|
||||
MustBeAssigned& operator = (const T& v) { value = v; isSet = true; return *this; }
|
||||
private:
|
||||
T value;
|
||||
bool isSet = false;
|
||||
};
|
||||
|
||||
//
|
||||
// Set of helper functions to help parse and build the tree.
|
||||
//
|
||||
|
|
@ -270,6 +287,7 @@ public:
|
|||
profile(p), version(v),
|
||||
#endif
|
||||
treeRoot(0),
|
||||
resources(TBuiltInResource{}),
|
||||
numEntryPoints(0), numErrors(0), numPushConstants(0), recursive(false),
|
||||
invertY(false),
|
||||
useStorageBuffer(false),
|
||||
|
|
@ -406,6 +424,7 @@ public:
|
|||
int getNumErrors() const { return numErrors; }
|
||||
void addPushConstantCount() { ++numPushConstants; }
|
||||
void setLimits(const TBuiltInResource& r) { resources = r; }
|
||||
const TBuiltInResource& getLimits() const { return resources; }
|
||||
|
||||
bool postProcess(TIntermNode*, EShLanguage);
|
||||
void removeTree();
|
||||
|
|
@ -955,7 +974,7 @@ protected:
|
|||
SpvVersion spvVersion;
|
||||
TIntermNode* treeRoot;
|
||||
std::set<std::string> requestedExtensions; // cumulation of all enabled or required extensions; not connected to what subset of the shader used them
|
||||
TBuiltInResource resources;
|
||||
MustBeAssigned<TBuiltInResource> resources;
|
||||
int numEntryPoints;
|
||||
int numErrors;
|
||||
int numPushConstants;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue