Validate that SPIR-V binary is encoded as little endian for WebGPU (#2523)
Fixes #2522
diff --git a/source/val/validate.cpp b/source/val/validate.cpp
index b145b19..52009fc 100644
--- a/source/val/validate.cpp
+++ b/source/val/validate.cpp
@@ -281,6 +281,12 @@
<< "Invalid SPIR-V magic number.";
}
+ if (spvIsWebGPUEnv(context.target_env) && endian != SPV_ENDIANNESS_LITTLE) {
+ return DiagnosticStream(position, context.consumer, "",
+ SPV_ERROR_INVALID_BINARY)
+ << "WebGPU requires SPIR-V to be little endian.";
+ }
+
spv_header_t header;
if (spvBinaryHeaderGet(binary.get(), endian, &header)) {
return DiagnosticStream(position, context.consumer, "",
diff --git a/test/val/val_webgpu_test.cpp b/test/val/val_webgpu_test.cpp
index 48ea21d..c55f927 100644
--- a/test/val/val_webgpu_test.cpp
+++ b/test/val/val_webgpu_test.cpp
@@ -276,6 +276,63 @@
"\"SPV_KHR_8bit_storage\"\n"));
}
+spv_binary GenerateTrivialBinary(bool need_little_endian) {
+ // Smallest possible valid WebGPU SPIR-V binary in little endian. Contains all
+ // the required boilerplate and a trivial entry point function.
+ static const uint8_t binary_bytes[] = {
+ // clang-format off
+ 0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0xE1, 0x14, 0x00, 0x00,
+ 0x0A, 0x00, 0x08, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x4B, 0x48, 0x52, 0x5F,
+ 0x76, 0x75, 0x6C, 0x6B, 0x61, 0x6E, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72,
+ 0x79, 0x5F, 0x6D, 0x6F, 0x64, 0x65, 0x6C, 0x00, 0x0E, 0x00, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x73, 0x68, 0x61, 0x64,
+ 0x65, 0x72, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00
+ // clang-format on
+ };
+ static const size_t word_count = sizeof(binary_bytes) / sizeof(uint32_t);
+ std::unique_ptr<spv_binary_t> result(new spv_binary_t);
+ if (!result) return nullptr;
+
+ result->wordCount = word_count;
+ result->code = new uint32_t[word_count];
+ if (!result->code) return nullptr;
+
+ if (need_little_endian) {
+ memcpy(result->code, binary_bytes, sizeof(binary_bytes));
+ } else {
+ uint8_t* code_bytes = reinterpret_cast<uint8_t*>(result->code);
+ for (size_t word = 0; word < word_count; ++word) {
+ code_bytes[4 * word] = binary_bytes[4 * word + 3];
+ code_bytes[4 * word + 1] = binary_bytes[4 * word + 2];
+ code_bytes[4 * word + 2] = binary_bytes[4 * word + 1];
+ code_bytes[4 * word + 3] = binary_bytes[4 * word];
+ }
+ }
+
+ return result.release();
+}
+
+TEST_F(ValidateWebGPU, LittleEndianGood) {
+ DestroyBinary();
+ binary_ = GenerateTrivialBinary(true);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
+}
+
+TEST_F(ValidateWebGPU, BigEndianBad) {
+ DestroyBinary();
+ binary_ = GenerateTrivialBinary(false);
+ EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("WebGPU requires SPIR-V to be little endian."));
+}
+
} // namespace
} // namespace val
} // namespace spvtools