Commit b1de5a7e by Olli Etuaho Committed by Commit Bot

Prevent stack overflow due to recursive swizzle of an l-value

Long chains of recursive swizzling could previously cause a stack overflow in checkCanBeLValue. Fold recursive swizzling when it is parsed to prevent this. BUG=angleproject:2439 TEST=angle_unittests Change-Id: I83b4c27442185709f6762d5ec23b93244010da05 Reviewed-on: https://chromium-review.googlesource.com/983593Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
parent beafe1a8
...@@ -795,6 +795,7 @@ TIntermSwizzle::TIntermSwizzle(const TIntermSwizzle &node) : TIntermExpression(n ...@@ -795,6 +795,7 @@ TIntermSwizzle::TIntermSwizzle(const TIntermSwizzle &node) : TIntermExpression(n
ASSERT(operandCopy != nullptr); ASSERT(operandCopy != nullptr);
mOperand = operandCopy; mOperand = operandCopy;
mSwizzleOffsets = node.mSwizzleOffsets; mSwizzleOffsets = node.mSwizzleOffsets;
mHasFoldedDuplicateOffsets = node.mHasFoldedDuplicateOffsets;
} }
TIntermBinary::TIntermBinary(const TIntermBinary &node) TIntermBinary::TIntermBinary(const TIntermBinary &node)
...@@ -1041,7 +1042,8 @@ void TIntermUnary::promote() ...@@ -1041,7 +1042,8 @@ void TIntermUnary::promote()
TIntermSwizzle::TIntermSwizzle(TIntermTyped *operand, const TVector<int> &swizzleOffsets) TIntermSwizzle::TIntermSwizzle(TIntermTyped *operand, const TVector<int> &swizzleOffsets)
: TIntermExpression(TType(EbtFloat, EbpUndefined)), : TIntermExpression(TType(EbtFloat, EbpUndefined)),
mOperand(operand), mOperand(operand),
mSwizzleOffsets(swizzleOffsets) mSwizzleOffsets(swizzleOffsets),
mHasFoldedDuplicateOffsets(false)
{ {
ASSERT(mSwizzleOffsets.size() <= 4); ASSERT(mSwizzleOffsets.size() <= 4);
promote(); promote();
...@@ -1167,6 +1169,10 @@ void TIntermSwizzle::promote() ...@@ -1167,6 +1169,10 @@ void TIntermSwizzle::promote()
bool TIntermSwizzle::hasDuplicateOffsets() const bool TIntermSwizzle::hasDuplicateOffsets() const
{ {
if (mHasFoldedDuplicateOffsets)
{
return true;
}
int offsetCount[4] = {0u, 0u, 0u, 0u}; int offsetCount[4] = {0u, 0u, 0u, 0u};
for (const auto offset : mSwizzleOffsets) for (const auto offset : mSwizzleOffsets)
{ {
...@@ -1179,6 +1185,11 @@ bool TIntermSwizzle::hasDuplicateOffsets() const ...@@ -1179,6 +1185,11 @@ bool TIntermSwizzle::hasDuplicateOffsets() const
return false; return false;
} }
void TIntermSwizzle::setHasFoldedDuplicateOffsets(bool hasFoldedDuplicateOffsets)
{
mHasFoldedDuplicateOffsets = hasFoldedDuplicateOffsets;
}
bool TIntermSwizzle::offsetsMatch(int offset) const bool TIntermSwizzle::offsetsMatch(int offset) const
{ {
return mSwizzleOffsets.size() == 1 && mSwizzleOffsets[0] == offset; return mSwizzleOffsets.size() == 1 && mSwizzleOffsets[0] == offset;
...@@ -1472,6 +1483,24 @@ const TConstantUnion *TIntermConstantUnion::FoldIndexing(const TType &type, ...@@ -1472,6 +1483,24 @@ const TConstantUnion *TIntermConstantUnion::FoldIndexing(const TType &type,
TIntermTyped *TIntermSwizzle::fold(TDiagnostics * /* diagnostics */) TIntermTyped *TIntermSwizzle::fold(TDiagnostics * /* diagnostics */)
{ {
TIntermSwizzle *operandSwizzle = mOperand->getAsSwizzleNode();
if (operandSwizzle)
{
// We need to fold the two swizzles into one, so that repeated swizzling can't cause stack
// overflow in ParseContext::checkCanBeLValue().
bool hadDuplicateOffsets = operandSwizzle->hasDuplicateOffsets();
TVector<int> foldedOffsets;
for (int offset : mSwizzleOffsets)
{
// Offset should already be validated.
ASSERT(static_cast<size_t>(offset) < operandSwizzle->mSwizzleOffsets.size());
foldedOffsets.push_back(operandSwizzle->mSwizzleOffsets[offset]);
}
operandSwizzle->mSwizzleOffsets = foldedOffsets;
operandSwizzle->setType(getType());
operandSwizzle->setHasFoldedDuplicateOffsets(hadDuplicateOffsets);
return operandSwizzle;
}
TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion(); TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion();
if (operandConstant == nullptr) if (operandConstant == nullptr)
{ {
......
...@@ -431,6 +431,7 @@ class TIntermSwizzle : public TIntermExpression ...@@ -431,6 +431,7 @@ class TIntermSwizzle : public TIntermExpression
void writeOffsetsAsXYZW(TInfoSinkBase *out) const; void writeOffsetsAsXYZW(TInfoSinkBase *out) const;
bool hasDuplicateOffsets() const; bool hasDuplicateOffsets() const;
void setHasFoldedDuplicateOffsets(bool hasFoldedDuplicateOffsets);
bool offsetsMatch(int offset) const; bool offsetsMatch(int offset) const;
TIntermTyped *fold(TDiagnostics *diagnostics) override; TIntermTyped *fold(TDiagnostics *diagnostics) override;
...@@ -438,6 +439,7 @@ class TIntermSwizzle : public TIntermExpression ...@@ -438,6 +439,7 @@ class TIntermSwizzle : public TIntermExpression
protected: protected:
TIntermTyped *mOperand; TIntermTyped *mOperand;
TVector<int> mSwizzleOffsets; TVector<int> mSwizzleOffsets;
bool mHasFoldedDuplicateOffsets;
private: private:
void promote(); void promote();
......
...@@ -5838,3 +5838,49 @@ TEST_F(VertexShaderValidationTest, GlFragCoord) ...@@ -5838,3 +5838,49 @@ TEST_F(VertexShaderValidationTest, GlFragCoord)
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog; FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
} }
} }
// Test that a long sequence of repeated swizzling on an l-value does not cause a stack overflow.
TEST_F(VertexShaderValidationTest, LValueRepeatedSwizzle)
{
std::stringstream shaderString;
shaderString << R"(#version 300 es
precision mediump float;
uniform vec2 u;
void main()
{
vec2 f;
f)";
for (int i = 0; i < 1000; ++i)
{
shaderString << ".yx.yx";
}
shaderString << R"( = vec2(0.0);
})";
if (!compile(shaderString.str()))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that swizzling that contains duplicate components can't form an l-value, even if it is
// swizzled again so that the final result does not contain duplicate components.
TEST_F(VertexShaderValidationTest, LValueSwizzleDuplicateComponents)
{
const std::string &shaderString = R"(#version 300 es
precision mediump float;
void main()
{
vec2 f;
(f.xxyy).xz = vec2(0.0);
})";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment