Validate SPIR-V instruction word array accesses
Previously the InsnIterator::wordPointer() method allowed unchecked
access to SPIR-V instruction words. This change eliminates this method
and instead provides a `Span` helper class to represent a contiguous
range of instruction words, where every access is checked against the
bounds of the instruction size as well as the word count of the span.
Bug: b/191426269
Change-Id: Idaef6432e8a0c489f565428edf4d5a347c6c518c
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/64689
Reviewed-by: Alexis Hétu <sugoi@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index ead8189..8d99850 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -574,7 +574,7 @@
if(opcode == spv::OpAccessChain || opcode == spv::OpInBoundsAccessChain)
{
Decorations dd{};
- ApplyDecorationsForAccessChain(&dd, &descriptorDecorations[resultId], pointerId, insn.wordCount() - 4, insn.wordPointer(4));
+ ApplyDecorationsForAccessChain(&dd, &descriptorDecorations[resultId], pointerId, Span(insn, 4, insn.wordCount() - 4));
// Note: offset is the one thing that does *not* propagate, as the access chain accounts for it.
dd.HasOffset = false;
decorations[resultId].Apply(dd);
@@ -1201,14 +1201,14 @@
VisitInterfaceInner(def.word(1), d, f);
}
-void SpirvShader::ApplyDecorationsForAccessChain(Decorations *d, DescriptorDecorations *dd, Object::ID baseId, uint32_t numIndexes, uint32_t const *indexIds) const
+void SpirvShader::ApplyDecorationsForAccessChain(Decorations *d, DescriptorDecorations *dd, Object::ID baseId, const Span &indexIds) const
{
ApplyDecorationsForId(d, baseId);
auto &baseObject = getObject(baseId);
ApplyDecorationsForId(d, baseObject.typeId());
auto typeId = getType(baseObject).element;
- for(auto i = 0u; i < numIndexes; i++)
+ for(uint32_t i = 0; i < indexIds.size(); i++)
{
ApplyDecorationsForId(d, typeId);
auto &type = getType(typeId);
@@ -1242,7 +1242,7 @@
}
}
-SIMD::Pointer SpirvShader::WalkExplicitLayoutAccessChain(Object::ID baseId, uint32_t numIndexes, uint32_t const *indexIds, EmitState const *state) const
+SIMD::Pointer SpirvShader::WalkExplicitLayoutAccessChain(Object::ID baseId, const Span &indexIds, const EmitState *state) const
{
// Produce a offset into external memory in sizeof(float) units
@@ -1251,6 +1251,7 @@
Decorations d = GetDecorationsForId(baseObject.typeId());
Int arrayIndex = 0;
+ uint32_t start = 0;
if(baseObject.kind == Object::Kind::DescriptorSet)
{
auto type = getType(typeId).definition.opcode();
@@ -1268,8 +1269,7 @@
arrayIndex = Extract(state->getIntermediate(indexIds[0]).Int(0), 0);
}
- numIndexes--;
- indexIds++;
+ start = 1;
typeId = getType(typeId).element;
}
}
@@ -1278,7 +1278,7 @@
int constantOffset = 0;
- for(auto i = 0u; i < numIndexes; i++)
+ for(uint32_t i = start; i < indexIds.size(); i++)
{
auto &type = getType(typeId);
ApplyDecorationsForId(&d, typeId);
@@ -1353,7 +1353,7 @@
return ptr;
}
-SIMD::Pointer SpirvShader::WalkAccessChain(Object::ID baseId, uint32_t numIndexes, uint32_t const *indexIds, EmitState const *state) const
+SIMD::Pointer SpirvShader::WalkAccessChain(Object::ID baseId, const Span &indexIds, EmitState const *state) const
{
// TODO: avoid doing per-lane work in some cases if we can?
auto routine = state->routine;
@@ -1364,7 +1364,7 @@
int constantOffset = 0;
- for(auto i = 0u; i < numIndexes; i++)
+ for(uint32_t i = 0; i < indexIds.size(); i++)
{
auto &type = getType(typeId);
switch(type.opcode())
@@ -1437,11 +1437,11 @@
return ptr;
}
-uint32_t SpirvShader::WalkLiteralAccessChain(Type::ID typeId, uint32_t numIndexes, uint32_t const *indexes) const
+uint32_t SpirvShader::WalkLiteralAccessChain(Type::ID typeId, const Span &indexes) const
{
uint32_t componentOffset = 0;
- for(auto i = 0u; i < numIndexes; i++)
+ for(uint32_t i = 0; i < indexes.size(); i++)
{
auto &type = getType(typeId);
switch(type.opcode())
@@ -1824,7 +1824,7 @@
{
auto text = spvtools::spvInstructionBinaryToText(
vk::SPIRV_VERSION,
- insn.wordPointer(0),
+ insn.data(),
insn.wordCount(),
insns.data(),
insns.size(),
@@ -2202,8 +2202,6 @@
Type::ID typeId = insn.word(1);
Object::ID resultId = insn.word(2);
Object::ID baseId = insn.word(3);
- uint32_t numIndexes = insn.wordCount() - 4;
- const uint32_t *indexes = insn.wordPointer(4);
auto &type = getType(typeId);
ASSERT(type.componentCount == 1);
ASSERT(getObject(resultId).kind == Object::Kind::Pointer);
@@ -2212,12 +2210,12 @@
type.storageClass == spv::StorageClassUniform ||
type.storageClass == spv::StorageClassStorageBuffer)
{
- auto ptr = WalkExplicitLayoutAccessChain(baseId, numIndexes, indexes, state);
+ auto ptr = WalkExplicitLayoutAccessChain(baseId, Span(insn, 4, insn.wordCount() - 4), state);
state->createPointer(resultId, ptr);
}
else
{
- auto ptr = WalkAccessChain(baseId, numIndexes, indexes, state);
+ auto ptr = WalkAccessChain(baseId, Span(insn, 4, insn.wordCount() - 4), state);
state->createPointer(resultId, ptr);
}
@@ -2253,7 +2251,7 @@
auto &dst = state->createIntermediate(insn.resultId(), type.componentCount);
auto &newPartObject = getObject(insn.word(3));
auto &newPartObjectTy = getType(newPartObject);
- auto firstNewComponent = WalkLiteralAccessChain(resultTypeId, insn.wordCount() - 5, insn.wordPointer(5));
+ auto firstNewComponent = WalkLiteralAccessChain(resultTypeId, Span(insn, 5, insn.wordCount() - 5));
Operand srcObjectAccess(this, state, insn.word(4));
Operand newPartObjectAccess(this, state, insn.word(3));
@@ -2283,7 +2281,7 @@
auto &dst = state->createIntermediate(insn.resultId(), type.componentCount);
auto &compositeObject = getObject(insn.word(3));
Type::ID compositeTypeId = compositeObject.definition.word(1);
- auto firstComponent = WalkLiteralAccessChain(compositeTypeId, insn.wordCount() - 4, insn.wordPointer(4));
+ auto firstComponent = WalkLiteralAccessChain(compositeTypeId, Span(insn, 4, insn.wordCount() - 4));
Operand compositeObjectAccess(this, state, insn.word(3));
for(auto i = 0u; i < type.componentCount; i++)
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 65ebf69..561032b 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -166,7 +166,7 @@
class Type;
class Object;
- // Pseudo-iterator over SPIRV instructions, designed to support range-based-for.
+ // Pseudo-iterator over SPIR-V instructions, designed to support range-based-for.
class InsnIterator
{
public:
@@ -195,14 +195,14 @@
return iter[n];
}
- uint32_t const *wordPointer(uint32_t n) const
+ const uint32_t *data() const
{
- return &iter[n];
+ return &iter[0];
}
const char *string(uint32_t n) const
{
- return reinterpret_cast<const char *>(wordPointer(n));
+ return reinterpret_cast<const char *>(&iter[n]);
}
// Returns the number of whole-words that a string literal starting at
@@ -214,8 +214,7 @@
uint32_t c = wordCount();
for(uint32_t i = n; n < c; i++)
{
- auto *u32 = wordPointer(i);
- auto *u8 = reinterpret_cast<const uint8_t *>(u32);
+ const char *s = string(i);
// SPIR-V spec 2.2.1. Instructions:
// A string is interpreted as a nul-terminated stream of
// characters. The character set is Unicode in the UTF-8
@@ -225,7 +224,7 @@
// The final word contains the string's nul-termination
// character (0), and all contents past the end of the string in
// the final word are padded with 0.
- if(u8[3] == 0)
+ if(s[3] == 0)
{
return 1 + i - n;
}
@@ -303,6 +302,32 @@
return InsnIterator{ insns.cend() };
}
+ // A range of contiguous instruction words.
+ struct Span
+ {
+ Span(const InsnIterator &insn, uint32_t offset, uint32_t size)
+ : insn(insn)
+ , offset(offset)
+ , wordCount(size)
+ {}
+
+ uint32_t operator[](uint32_t index) const
+ {
+ ASSERT(index < wordCount);
+ return insn.word(offset + index);
+ }
+
+ uint32_t size() const
+ {
+ return wordCount;
+ }
+
+ private:
+ const InsnIterator &insn;
+ const uint32_t offset;
+ const uint32_t wordCount;
+ };
+
class Type
{
public:
@@ -933,7 +958,7 @@
Decorations GetDecorationsForId(TypeOrObjectID id) const;
void ApplyDecorationsForId(Decorations *d, TypeOrObjectID id) const;
void ApplyDecorationsForIdMember(Decorations *d, Type::ID id, uint32_t member) const;
- void ApplyDecorationsForAccessChain(Decorations *d, DescriptorDecorations *dd, Object::ID baseId, uint32_t numIndexes, uint32_t const *indexIds) const;
+ void ApplyDecorationsForAccessChain(Decorations *d, DescriptorDecorations *dd, Object::ID baseId, const Span &indexIds) const;
// Creates an Object for the instruction's result in 'defs'.
void DefineResult(const InsnIterator &insn);
@@ -1253,11 +1278,11 @@
OutOfBoundsBehavior getOutOfBoundsBehavior(Object::ID pointerId, EmitState const *state) const;
- SIMD::Pointer WalkExplicitLayoutAccessChain(Object::ID id, uint32_t numIndexes, uint32_t const *indexIds, EmitState const *state) const;
- SIMD::Pointer WalkAccessChain(Object::ID id, uint32_t numIndexes, uint32_t const *indexIds, EmitState const *state) const;
+ SIMD::Pointer WalkExplicitLayoutAccessChain(Object::ID id, const Span &indexIds, const EmitState *state) const;
+ SIMD::Pointer WalkAccessChain(Object::ID id, const Span &indexIds, const EmitState *state) const;
// Returns the *component* offset in the literal for the given access chain.
- uint32_t WalkLiteralAccessChain(Type::ID id, uint32_t numIndexes, uint32_t const *indexes) const;
+ uint32_t WalkLiteralAccessChain(Type::ID id, const Span &indexes) const;
// Lookup the active lane mask for the edge from -> to.
// If from is unreachable, then a mask of all zeros is returned.
diff --git a/src/Pipeline/SpirvShaderDebugger.cpp b/src/Pipeline/SpirvShaderDebugger.cpp
index 0f25a32..b0559af 100644
--- a/src/Pipeline/SpirvShaderDebugger.cpp
+++ b/src/Pipeline/SpirvShaderDebugger.cpp
@@ -2571,13 +2571,13 @@
{
auto instruction = spvtools::spvInstructionBinaryToText(
vk::SPIRV_VERSION,
- insn.wordPointer(0),
+ insn.data(),
insn.wordCount(),
insns.data(),
insns.size(),
SPV_BINARY_TO_TEXT_OPTION_NO_HEADER) +
"\n";
- dbg->spirvLineMappings[insn.wordPointer(0)] = currentLine;
+ dbg->spirvLineMappings[insn.data()] = currentLine;
currentLine += std::count(instruction.begin(), instruction.end(), '\n');
source += instruction;
}
@@ -2670,7 +2670,7 @@
{
auto instruction = spvtools::spvInstructionBinaryToText(
vk::SPIRV_VERSION,
- insn.wordPointer(0),
+ insn.data(),
insn.wordCount(),
insns.data(),
insns.size(),
@@ -2683,7 +2683,7 @@
{
auto instruction = spvtools::spvInstructionBinaryToText(
vk::SPIRV_VERSION,
- insn.wordPointer(0),
+ insn.data(),
insn.wordCount(),
insns.data(),
insns.size(),
@@ -2711,7 +2711,7 @@
// We're emitting debugger logic for SPIR-V.
if(IsStatement(insn.opcode()))
{
- auto line = dbg->spirvLineMappings.at(insn.wordPointer(0));
+ auto line = dbg->spirvLineMappings.at(insn.data());
dbg->setLocation(state, dbg->spirvFile, line);
}
}
@@ -2735,7 +2735,7 @@
break;
default:
{
- auto resIt = dbg->results.find(insn.wordPointer(0));
+ auto resIt = dbg->results.find(insn.data());
if(resIt != dbg->results.end())
{
dbg->shadow.create(this, state, resIt->second);
@@ -2759,7 +2759,7 @@
auto dbg = impl.debugger;
if(!dbg) { return; }
- dbg->results.emplace(insn.wordPointer(0), resultId);
+ dbg->results.emplace(insn.data(), resultId);
}
SpirvShader::EmitResult SpirvShader::EmitLine(InsnIterator insn, EmitState *state) const
@@ -2779,7 +2779,7 @@
{
auto instruction = spvtools::spvInstructionBinaryToText(
vk::SPIRV_VERSION,
- insn.wordPointer(0),
+ insn.data(),
insn.wordCount(),
insns.data(),
insns.size(),
diff --git a/src/Pipeline/SpirvShaderSpec.cpp b/src/Pipeline/SpirvShaderSpec.cpp
index 7da7396..fc4806a 100644
--- a/src/Pipeline/SpirvShaderSpec.cpp
+++ b/src/Pipeline/SpirvShaderSpec.cpp
@@ -85,7 +85,7 @@
{
auto &result = CreateConstant(insn);
auto const &compositeObject = getObject(insn.word(4));
- auto firstComponent = WalkLiteralAccessChain(compositeObject.typeId(), insn.wordCount() - 5, insn.wordPointer(5));
+ auto firstComponent = WalkLiteralAccessChain(compositeObject.typeId(), Span(insn, 5, insn.wordCount() - 5));
for(auto i = 0u; i < getType(result).componentCount; i++)
{
@@ -99,7 +99,7 @@
auto &result = CreateConstant(insn);
auto const &newPart = getObject(insn.word(4));
auto const &oldObject = getObject(insn.word(5));
- auto firstNewComponent = WalkLiteralAccessChain(result.typeId(), insn.wordCount() - 6, insn.wordPointer(6));
+ auto firstNewComponent = WalkLiteralAccessChain(result.typeId(), Span(insn, 6, insn.wordCount() - 6));
// old components before
for(auto i = 0u; i < firstNewComponent; i++)