From 9221570027d759bda093ae035a7cc68d6923fa13 Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Wed, 2 Oct 2024 20:48:32 -0700 Subject: [PATCH] [Validator] Check size of PSV. (#6924) Check size of PSV part matches the PSVVersion. Updated DxilPipelineStateValidation::ReadOrWrite to read based on initInfo.PSVVersion. And return fail when size mismatch in RWMode::Read. Fixes #6817 --- docs/ReleaseNotes.md | 1 + .../DxilPipelineStateValidation.h | 13 +- lib/DxilContainer/DxilContainerAssembler.cpp | 34 +- .../DxilPipelineStateValidation.cpp | 37 ++ .../DxilContainerValidation.cpp | 315 +++++++++++- tools/clang/unittests/HLSL/ValidationTest.cpp | 466 +++++++++++++++++- utils/hct/hctdb.py | 5 + 7 files changed, 816 insertions(+), 55 deletions(-) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 81f393c6ff..cd7c7b874a 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -23,6 +23,7 @@ Place release notes for the upcoming release below this line and remove this lin - The incomplete WaveMatrix implementation has been removed. - DXIL Validator Hash is open sourced. +- DXIL container validation for PSV0 part allows any content ordering inside string and semantic index tables. ### Version 1.8.2407 diff --git a/include/dxc/DxilContainer/DxilPipelineStateValidation.h b/include/dxc/DxilContainer/DxilPipelineStateValidation.h index e6df509296..83d0dae6e9 100644 --- a/include/dxc/DxilContainer/DxilPipelineStateValidation.h +++ b/include/dxc/DxilContainer/DxilPipelineStateValidation.h @@ -226,7 +226,8 @@ struct PSVStringTable { PSVStringTable() : Table(nullptr), Size(0) {} PSVStringTable(const char *table, uint32_t size) : Table(table), Size(size) {} const char *Get(uint32_t offset) const { - assert(offset < Size && Table && Table[Size - 1] == '\0'); + if (!(offset < Size && Table && Table[Size - 1] == '\0')) + return nullptr; return Table + offset; } }; @@ -344,7 +345,8 @@ struct PSVSemanticIndexTable { PSVSemanticIndexTable(const uint32_t *table, uint32_t entries) : Table(table), Entries(entries) {} const uint32_t *Get(uint32_t offset) const { - assert(offset < Entries && Table); + if (!(offset < Entries && Table)) + return nullptr; return Table + offset; } }; @@ -638,7 +640,8 @@ class DxilPipelineStateValidation { _T *GetRecord(void *pRecords, uint32_t recordSize, uint32_t numRecords, uint32_t index) const { if (pRecords && index < numRecords && sizeof(_T) <= recordSize) { - assert((size_t)index * (size_t)recordSize <= UINT_MAX); + if (!((size_t)index * (size_t)recordSize <= UINT_MAX)) + return nullptr; return reinterpret_cast<_T *>(reinterpret_cast(pRecords) + (index * recordSize)); } @@ -1126,6 +1129,10 @@ void InitPSVSignatureElement(PSVSignatureElement0 &E, const DxilSignatureElement &SE, bool i1ToUnknownCompat); +// Setup PSVInitInfo with DxilModule. +// Note that the StringTable and PSVSemanticIndexTable are not done. +void SetupPSVInitInfo(PSVInitInfo &InitInfo, const DxilModule &DM); + // Setup shader properties for PSVRuntimeInfo* with DxilModule. void SetShaderProps(PSVRuntimeInfo0 *pInfo, const DxilModule &DM); void SetShaderProps(PSVRuntimeInfo1 *pInfo1, const DxilModule &DM); diff --git a/lib/DxilContainer/DxilContainerAssembler.cpp b/lib/DxilContainer/DxilContainerAssembler.cpp index d7d006bd4f..0b7f5dd467 100644 --- a/lib/DxilContainer/DxilContainerAssembler.cpp +++ b/lib/DxilContainer/DxilContainerAssembler.cpp @@ -738,30 +738,14 @@ class DxilPSVWriter : public DxilPartWriter { DxilPSVWriter(const DxilModule &mod, uint32_t PSVVersion = UINT_MAX) : m_Module(mod), m_PSVInitInfo(PSVVersion) { m_Module.GetValidatorVersion(m_ValMajor, m_ValMinor); - // Constraint PSVVersion based on validator version - uint32_t PSVVersionConstraint = hlsl::GetPSVVersion(m_ValMajor, m_ValMinor); - if (PSVVersion > PSVVersionConstraint) - m_PSVInitInfo.PSVVersion = PSVVersionConstraint; - - const ShaderModel *SM = m_Module.GetShaderModel(); - UINT uCBuffers = m_Module.GetCBuffers().size(); - UINT uSamplers = m_Module.GetSamplers().size(); - UINT uSRVs = m_Module.GetSRVs().size(); - UINT uUAVs = m_Module.GetUAVs().size(); - m_PSVInitInfo.ResourceCount = uCBuffers + uSamplers + uSRVs + uUAVs; + hlsl::SetupPSVInitInfo(m_PSVInitInfo, m_Module); + // TODO: for >= 6.2 version, create more efficient structure if (m_PSVInitInfo.PSVVersion > 0) { - m_PSVInitInfo.ShaderStage = (PSVShaderKind)SM->GetKind(); // Copy Dxil Signatures m_StringBuffer.push_back('\0'); // For empty semantic name (system value) - m_PSVInitInfo.SigInputElements = - m_Module.GetInputSignature().GetElements().size(); m_SigInputElements.resize(m_PSVInitInfo.SigInputElements); - m_PSVInitInfo.SigOutputElements = - m_Module.GetOutputSignature().GetElements().size(); m_SigOutputElements.resize(m_PSVInitInfo.SigOutputElements); - m_PSVInitInfo.SigPatchConstOrPrimElements = - m_Module.GetPatchConstOrPrimSignature().GetElements().size(); m_SigPatchConstOrPrimElements.resize( m_PSVInitInfo.SigPatchConstOrPrimElements); uint32_t i = 0; @@ -791,20 +775,6 @@ class DxilPSVWriter : public DxilPartWriter { m_PSVInitInfo.StringTable.Size = m_StringBuffer.size(); m_PSVInitInfo.SemanticIndexTable.Table = m_SemanticIndexBuffer.data(); m_PSVInitInfo.SemanticIndexTable.Entries = m_SemanticIndexBuffer.size(); - // Set up ViewID and signature dependency info - m_PSVInitInfo.UsesViewID = - m_Module.m_ShaderFlags.GetViewID() ? true : false; - m_PSVInitInfo.SigInputVectors = - m_Module.GetInputSignature().NumVectorsUsed(0); - for (unsigned streamIndex = 0; streamIndex < 4; streamIndex++) { - m_PSVInitInfo.SigOutputVectors[streamIndex] = - m_Module.GetOutputSignature().NumVectorsUsed(streamIndex); - } - m_PSVInitInfo.SigPatchConstOrPrimVectors = 0; - if (SM->IsHS() || SM->IsDS() || SM->IsMS()) { - m_PSVInitInfo.SigPatchConstOrPrimVectors = - m_Module.GetPatchConstOrPrimSignature().NumVectorsUsed(0); - } } if (!m_PSV.InitNew(m_PSVInitInfo, nullptr, &m_PSVBufferSize)) { DXASSERT(false, "PSV InitNew failed computing size!"); diff --git a/lib/DxilContainer/DxilPipelineStateValidation.cpp b/lib/DxilContainer/DxilPipelineStateValidation.cpp index 9f04c09943..66186549f2 100644 --- a/lib/DxilContainer/DxilPipelineStateValidation.cpp +++ b/lib/DxilContainer/DxilPipelineStateValidation.cpp @@ -110,6 +110,43 @@ void hlsl::InitPSVSignatureElement(PSVSignatureElement0 &E, E.DynamicMaskAndStream |= (SE.GetDynIdxCompMask()) & 0xF; } +void hlsl::SetupPSVInitInfo(PSVInitInfo &InitInfo, const DxilModule &DM) { + // Constraint PSVVersion based on validator version + unsigned ValMajor, ValMinor; + DM.GetValidatorVersion(ValMajor, ValMinor); + unsigned PSVVersionConstraint = hlsl::GetPSVVersion(ValMajor, ValMinor); + if (InitInfo.PSVVersion > PSVVersionConstraint) + InitInfo.PSVVersion = PSVVersionConstraint; + + const ShaderModel *SM = DM.GetShaderModel(); + uint32_t uCBuffers = DM.GetCBuffers().size(); + uint32_t uSamplers = DM.GetSamplers().size(); + uint32_t uSRVs = DM.GetSRVs().size(); + uint32_t uUAVs = DM.GetUAVs().size(); + InitInfo.ResourceCount = uCBuffers + uSamplers + uSRVs + uUAVs; + + if (InitInfo.PSVVersion > 0) { + InitInfo.ShaderStage = (PSVShaderKind)SM->GetKind(); + InitInfo.SigInputElements = DM.GetInputSignature().GetElements().size(); + InitInfo.SigPatchConstOrPrimElements = + DM.GetPatchConstOrPrimSignature().GetElements().size(); + InitInfo.SigOutputElements = DM.GetOutputSignature().GetElements().size(); + + // Set up ViewID and signature dependency info + InitInfo.UsesViewID = DM.m_ShaderFlags.GetViewID() ? true : false; + InitInfo.SigInputVectors = DM.GetInputSignature().NumVectorsUsed(0); + for (unsigned streamIndex = 0; streamIndex < 4; streamIndex++) { + InitInfo.SigOutputVectors[streamIndex] = + DM.GetOutputSignature().NumVectorsUsed(streamIndex); + } + InitInfo.SigPatchConstOrPrimVectors = 0; + if (SM->IsHS() || SM->IsDS() || SM->IsMS()) { + InitInfo.SigPatchConstOrPrimVectors = + DM.GetPatchConstOrPrimSignature().NumVectorsUsed(0); + } + } +} + void hlsl::SetShaderProps(PSVRuntimeInfo0 *pInfo, const DxilModule &DM) { const ShaderModel *SM = DM.GetShaderModel(); pInfo->MinimumExpectedWaveLaneCount = 0; diff --git a/lib/DxilValidation/DxilContainerValidation.cpp b/lib/DxilValidation/DxilContainerValidation.cpp index a5e68eb5e1..2276b0d3de 100644 --- a/lib/DxilValidation/DxilContainerValidation.cpp +++ b/lib/DxilValidation/DxilContainerValidation.cpp @@ -24,6 +24,7 @@ #include "dxc/DXIL/DxilUtil.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IR/Module.h" @@ -33,6 +34,7 @@ #include "DxilValidationUtils.h" #include +#include #include using namespace llvm; @@ -79,17 +81,95 @@ static void emitDxilDiag(LLVMContext &Ctx, const char *str) { hlsl::dxilutil::EmitErrorOnContext(Ctx, str); } +class StringTableVerifier { + std::unordered_map OffsetToUseCountMap; + const PSVStringTable &Table; + +public: + StringTableVerifier(const PSVStringTable &Table) : Table(Table) { + unsigned Start = 0; + for (unsigned i = 0; i < Table.Size; ++i) { + char ch = Table.Table[i]; + if (ch == '\0') { + OffsetToUseCountMap[Start] = 0; + Start = i + 1; + } + } + if (Table.Size >= 4) { + // Remove the '\0's at the end of the table added for padding. + for (unsigned i = Table.Size - 1; i > Table.Size - 4; --i) { + if (Table.Table[i] != '\0') + break; + OffsetToUseCountMap.erase(i); + } + } + } + bool MarkUse(unsigned Offset) { + auto it = OffsetToUseCountMap.find(Offset); + if (it != OffsetToUseCountMap.end()) + it->second++; + return Offset < Table.Size; + } + void Verify(ValidationContext &ValCtx) { + for (auto [Offset, UseCount] : OffsetToUseCountMap) { + if (UseCount != 0) + continue; + // DXC will always add a null-terminated string at the beginning of the + // StringTable. It is OK if it is not used. + if (Offset == 0 && Table.Table[0] == '\0') + continue; + + ValCtx.EmitFormatError(ValidationRule::ContainerUnusedItemInTable, + {"StringTable", Table.Get(Offset)}); + } + } +}; + +class SemanticIndexTableVerifier { + const PSVSemanticIndexTable &Table; + llvm::BitVector UseMask; + +public: + SemanticIndexTableVerifier(const PSVSemanticIndexTable &Table) + : Table(Table), UseMask(Table.Entries, false) {} + bool MarkUse(unsigned Offset, unsigned Size) { + if (Table.Table == nullptr) + return false; + if (Offset > Table.Entries) + return false; + if ((Offset + Size) > Table.Entries) + return false; + for (unsigned i = Offset; i < (Offset + Size); ++i) { + UseMask[i] = true; + } + return true; + } + void Verify(ValidationContext &ValCtx) { + for (unsigned i = 0; i < Table.Entries; i++) { + if (UseMask[i]) + continue; + + ValCtx.EmitFormatError(ValidationRule::ContainerUnusedItemInTable, + {"SemanticIndexTable", std::to_string(i)}); + } + } +}; + class PSVContentVerifier { DxilModule &DM; DxilPipelineStateValidation &PSV; ValidationContext &ValCtx; bool PSVContentValid = true; + StringTableVerifier StrTableVerifier; + SemanticIndexTableVerifier IndexTableVerifier; public: PSVContentVerifier(DxilPipelineStateValidation &PSV, DxilModule &DM, ValidationContext &ValCtx) - : DM(DM), PSV(PSV), ValCtx(ValCtx) {} - void Verify(); + : DM(DM), PSV(PSV), ValCtx(ValCtx), + StrTableVerifier(PSV.GetStringTable()), + IndexTableVerifier(PSV.GetSemanticIndexTable()) {} + void Verify(unsigned ValMajor, unsigned ValMinor, unsigned PSVVersion); private: void VerifySignatures(unsigned ValMajor, unsigned ValMinor); @@ -113,7 +193,8 @@ class PSVContentVerifier { PSVContentValid = false; } void EmitInvalidError(StringRef Name) { - ValCtx.EmitFormatError(ValidationRule::ContainerContentInvalid, {Name}); + ValCtx.EmitFormatError(ValidationRule::ContainerContentInvalid, + {"PSV0 part", Name}); PSVContentValid = false; } template static std::string GetDump(const Ty &T) { @@ -226,6 +307,17 @@ void PSVContentVerifier::VerifySignatureElement( const DxilSignatureElement &SE, PSVSignatureElement0 *PSVSE0, const PSVStringTable &StrTab, const PSVSemanticIndexTable &IndexTab, std::string Name, bool i1ToUnknownCompat) { + bool InvalidTableAccess = false; + if (!StrTableVerifier.MarkUse(PSVSE0->SemanticName)) { + EmitInvalidError("SemanticName"); + InvalidTableAccess = true; + } + if (!IndexTableVerifier.MarkUse(PSVSE0->SemanticIndexes, PSVSE0->Rows)) { + EmitInvalidError("SemanticIndex"); + InvalidTableAccess = true; + } + if (InvalidTableAccess) + return; // Find the signature element in the set. PSVSignatureElement0 ModulePSVSE0; InitPSVSignatureElement(ModulePSVSE0, SE, i1ToUnknownCompat); @@ -368,18 +460,9 @@ void PSVContentVerifier::VerifyEntryProperties(const ShaderModel *SM, } } -void PSVContentVerifier::Verify() { - unsigned ValMajor, ValMinor; - DM.GetValidatorVersion(ValMajor, ValMinor); - unsigned PSVVersion = hlsl::GetPSVVersion(ValMajor, ValMinor); - +void PSVContentVerifier::Verify(unsigned ValMajor, unsigned ValMinor, + unsigned PSVVersion) { PSVInitInfo PSVInfo(PSVVersion); - if (PSV.GetRuntimeInfoSize() != PSVInfo.RuntimeInfoSize()) { - EmitMismatchError("PSVRuntimeInfoSize", - std::to_string(PSV.GetRuntimeInfoSize()), - std::to_string(PSVInfo.RuntimeInfoSize())); - return; - } if (PSV.GetBindCount() > 0 && PSV.GetResourceBindInfoSize() != PSVInfo.ResourceBindInfoSize()) { @@ -421,11 +504,19 @@ void PSVContentVerifier::Verify() { } // PSV2 only added NumThreadsX/Y/Z which verified in VerifyEntryProperties. if (PSVVersion > 2) { - if (DM.GetEntryFunctionName() != PSV.GetEntryFunctionName()) - EmitMismatchError("EntryFunctionName", PSV.GetEntryFunctionName(), - DM.GetEntryFunctionName()); + PSVRuntimeInfo3 *PSV3 = PSV.GetPSVRuntimeInfo3(); + if (!StrTableVerifier.MarkUse(PSV3->EntryFunctionName)) { + EmitInvalidError("EntryFunctionName"); + } else { + if (DM.GetEntryFunctionName() != PSV.GetEntryFunctionName()) + EmitMismatchError("EntryFunctionName", PSV.GetEntryFunctionName(), + DM.GetEntryFunctionName()); + } } + StrTableVerifier.Verify(ValCtx); + IndexTableVerifier.Verify(ValCtx); + if (!PSVContentValid) ValCtx.EmitFormatError(ValidationRule::ContainerPartMatches, {"Pipeline State Validation"}); @@ -507,16 +598,204 @@ bool VerifySignatureMatches(llvm::Module *pModule, DXIL::SignatureKind SigKind, return !ValCtx.Failed; } +struct SimplePSV { + uint32_t PSVRuntimeInfoSize = 0; + uint32_t PSVNumResources = 0; + uint32_t PSVResourceBindInfoSize = 0; + uint32_t StringTableSize = 0; + const char *StringTable = nullptr; + uint32_t SemanticIndexTableEntries = 0; + const uint32_t *SemanticIndexTable = nullptr; + uint32_t PSVSignatureElementSize = 0; + const PSVRuntimeInfo1 *RuntimeInfo1 = nullptr; + bool IsValid = true; + SimplePSV(const void *pPSVData, uint32_t PSVSize) { + +#define INCREMENT_POS(Size) \ + Offset += Size; \ + if (Offset > PSVSize) { \ + IsValid = false; \ + return; \ + } + + uint32_t Offset = 0; + PSVRuntimeInfoSize = GetUint32AtOffset(pPSVData, 0); + INCREMENT_POS(4); + if (PSVRuntimeInfoSize >= sizeof(PSVRuntimeInfo1)) + RuntimeInfo1 = + (const PSVRuntimeInfo1 *)(GetPtrAtOffset(pPSVData, Offset)); + INCREMENT_POS(PSVRuntimeInfoSize); + + PSVNumResources = GetUint32AtOffset(pPSVData, Offset); + INCREMENT_POS(4); + if (PSVNumResources > 0) { + PSVResourceBindInfoSize = GetUint32AtOffset(pPSVData, Offset); + // Increase the offset for the resource bind info size. + INCREMENT_POS(4); + // Increase the offset for the resource bind info. + INCREMENT_POS(PSVNumResources * PSVResourceBindInfoSize); + } + if (RuntimeInfo1) { + StringTableSize = GetUint32AtOffset(pPSVData, Offset); + INCREMENT_POS(4); + // Make sure StringTableSize is aligned to 4 bytes. + if ((StringTableSize & 3) != 0) { + IsValid = false; + return; + } + if (StringTableSize) { + StringTable = GetPtrAtOffset(pPSVData, Offset); + INCREMENT_POS(StringTableSize); + } + SemanticIndexTableEntries = GetUint32AtOffset(pPSVData, Offset); + INCREMENT_POS(4); + if (SemanticIndexTableEntries) { + SemanticIndexTable = + (const uint32_t *)(GetPtrAtOffset(pPSVData, Offset)); + INCREMENT_POS(SemanticIndexTableEntries * 4); + } + if (RuntimeInfo1->SigInputElements || RuntimeInfo1->SigOutputElements || + RuntimeInfo1->SigPatchConstOrPrimElements) { + PSVSignatureElementSize = GetUint32AtOffset(pPSVData, Offset); + INCREMENT_POS(4); + uint32_t PSVNumSignatures = RuntimeInfo1->SigInputElements + + RuntimeInfo1->SigOutputElements + + RuntimeInfo1->SigPatchConstOrPrimElements; + INCREMENT_POS(PSVNumSignatures * PSVSignatureElementSize); + } + if (RuntimeInfo1->UsesViewID) { + for (unsigned i = 0; i < DXIL::kNumOutputStreams; i++) { + uint32_t SigOutputVectors = RuntimeInfo1->SigOutputVectors[i]; + if (SigOutputVectors == 0) + continue; + uint32_t MaskSizeInBytes = + sizeof(uint32_t) * + PSVComputeMaskDwordsFromVectors(SigOutputVectors); + INCREMENT_POS(MaskSizeInBytes); + } + if ((RuntimeInfo1->ShaderStage == (unsigned)DXIL::ShaderKind::Hull || + RuntimeInfo1->ShaderStage == (unsigned)DXIL::ShaderKind::Mesh) && + RuntimeInfo1->SigPatchConstOrPrimVectors) { + uint32_t MaskSizeInBytes = + sizeof(uint32_t) * PSVComputeMaskDwordsFromVectors( + RuntimeInfo1->SigPatchConstOrPrimVectors); + INCREMENT_POS(MaskSizeInBytes); + } + } + + for (unsigned i = 0; i < DXIL::kNumOutputStreams; i++) { + uint32_t SigOutputVectors = RuntimeInfo1->SigOutputVectors[i]; + if (SigOutputVectors == 0) + continue; + uint32_t TableSizeInBytes = + sizeof(uint32_t) * + PSVComputeInputOutputTableDwords(RuntimeInfo1->SigInputVectors, + SigOutputVectors); + INCREMENT_POS(TableSizeInBytes); + } + + if ((RuntimeInfo1->ShaderStage == (unsigned)DXIL::ShaderKind::Hull || + RuntimeInfo1->ShaderStage == (unsigned)DXIL::ShaderKind::Mesh) && + RuntimeInfo1->SigPatchConstOrPrimVectors && + RuntimeInfo1->SigInputVectors) { + uint32_t TableSizeInBytes = + sizeof(uint32_t) * PSVComputeInputOutputTableDwords( + RuntimeInfo1->SigInputVectors, + RuntimeInfo1->SigPatchConstOrPrimVectors); + INCREMENT_POS(TableSizeInBytes); + } + + if (RuntimeInfo1->ShaderStage == (unsigned)DXIL::ShaderKind::Domain && + RuntimeInfo1->SigOutputVectors[0] && + RuntimeInfo1->SigPatchConstOrPrimVectors) { + uint32_t TableSizeInBytes = + sizeof(uint32_t) * PSVComputeInputOutputTableDwords( + RuntimeInfo1->SigPatchConstOrPrimVectors, + RuntimeInfo1->SigOutputVectors[0]); + INCREMENT_POS(TableSizeInBytes); + } + } + IsValid = PSVSize == Offset; +#undef INCREMENT_POS + } + bool ValidatePSVInit(PSVInitInfo PSVInfo, ValidationContext &ValCtx) { + if (PSVRuntimeInfoSize != PSVInfo.RuntimeInfoSize()) { + ValCtx.EmitFormatError(ValidationRule::ContainerContentMatches, + {"PSVRuntimeInfoSize", "PSV0", + std::to_string(PSVRuntimeInfoSize), + std::to_string(PSVInfo.RuntimeInfoSize())}); + return false; + } + if (PSVNumResources && + PSVResourceBindInfoSize != PSVInfo.ResourceBindInfoSize()) { + ValCtx.EmitFormatError(ValidationRule::ContainerContentMatches, + {"PSVResourceBindInfoSize", "PSV0", + std::to_string(PSVResourceBindInfoSize), + std::to_string(PSVInfo.ResourceBindInfoSize())}); + return false; + } + if (RuntimeInfo1 && + (RuntimeInfo1->SigInputElements || RuntimeInfo1->SigOutputElements || + RuntimeInfo1->SigPatchConstOrPrimElements) && + PSVSignatureElementSize != PSVInfo.SignatureElementSize()) { + ValCtx.EmitFormatError(ValidationRule::ContainerContentMatches, + {"PSVSignatureElementSize", "PSV0", + std::to_string(PSVSignatureElementSize), + std::to_string(PSVInfo.SignatureElementSize())}); + return false; + } + return true; + } + +private: + const char *GetPtrAtOffset(const void *BasePtr, uint32_t Offset) const { + return (const char *)BasePtr + Offset; + } + uint32_t GetUint32AtOffset(const void *BasePtr, uint32_t Offset) const { + return *(const uint32_t *)GetPtrAtOffset(BasePtr, Offset); + } +}; + static void VerifyPSVMatches(ValidationContext &ValCtx, const void *pPSVData, uint32_t PSVSize) { + // SimplePSV.IsValid indicates whether the part is well-formed so that we may + // proceed with more detailed validation. + SimplePSV SimplePSV(pPSVData, PSVSize); + if (!SimplePSV.IsValid) { + ValCtx.EmitFormatError(ValidationRule::ContainerContentInvalid, + {"DxilContainer", "PSV0 part"}); + return; + } + // The PSVVersion determines the size of record structures that should be + // used when writing PSV0 data, and is based on the validator version in the + // module. + unsigned ValMajor, ValMinor; + ValCtx.DxilMod.GetValidatorVersion(ValMajor, ValMinor); + unsigned PSVVersion = hlsl::GetPSVVersion(ValMajor, ValMinor); + // PSVInfo is used to compute the expected record size of the PSV0 part of the + // container. It uses facts from the module. + PSVInitInfo PSVInfo(PSVVersion); + hlsl::SetupPSVInitInfo(PSVInfo, ValCtx.DxilMod); + // ValidatePSVInit checks that record sizes match expected for PSVVersion. + if (!SimplePSV.ValidatePSVInit(PSVInfo, ValCtx)) + return; + // Ensure that the string table data is null-terminated. + if (SimplePSV.StringTable && + SimplePSV.StringTable[SimplePSV.StringTableSize - 1] != '\0') { + ValCtx.EmitFormatError(ValidationRule::ContainerContentInvalid, + {"PSV part StringTable"}); + return; + } + DxilPipelineStateValidation PSV; if (!PSV.InitFromPSV0(pPSVData, PSVSize)) { ValCtx.EmitFormatError(ValidationRule::ContainerPartMatches, {"Pipeline State Validation"}); return; } + PSVContentVerifier Verifier(PSV, ValCtx.DxilMod, ValCtx); - Verifier.Verify(); + Verifier.Verify(ValMajor, ValMinor, PSVVersion); } static void VerifyFeatureInfoMatches(ValidationContext &ValCtx, diff --git a/tools/clang/unittests/HLSL/ValidationTest.cpp b/tools/clang/unittests/HLSL/ValidationTest.cpp index 339ff1fbb3..64188fecd8 100644 --- a/tools/clang/unittests/HLSL/ValidationTest.cpp +++ b/tools/clang/unittests/HLSL/ValidationTest.cpp @@ -321,6 +321,9 @@ class ValidationTest : public ::testing::Test { TEST_METHOD(PSVContentValidationCS) TEST_METHOD(PSVContentValidationMS) TEST_METHOD(PSVContentValidationAS) + TEST_METHOD(WrongPSVSize) + TEST_METHOD(WrongPSVSizeOnZeros) + TEST_METHOD(WrongPSVVersion) dxc::DxcDllSupport m_dllSupport; VersionSupportInfo m_ver; @@ -427,6 +430,18 @@ class ValidationTest : public ::testing::Test { pResultBlob); } + bool CompileFile(LPCWSTR fileName, LPCSTR pShaderModel, LPCWSTR *pArguments, + UINT32 argCount, IDxcBlob **pResultBlob) { + std::wstring fullPath = hlsl_test::GetPathToHlslDataFile(fileName); + CComPtr pLibrary; + CComPtr pSource; + VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary)); + VERIFY_SUCCEEDED( + pLibrary->CreateBlobFromFile(fullPath.c_str(), nullptr, &pSource)); + return CompileSource(pSource, pShaderModel, pArguments, argCount, nullptr, + 0, pResultBlob); + } + bool CompileSource(IDxcBlobEncoding *pSource, LPCSTR pShaderModel, IDxcBlob **pResultBlob) { return CompileSource(pSource, pShaderModel, nullptr, 0, nullptr, 0, @@ -4636,7 +4651,7 @@ TEST_F(ValidationTest, PSVStringTableReorder) { " ComponentType: 3", " DynamicIndexMask: 0", "') and DXIL module:('PSVSignatureElement:", - " SemanticName: ", + " SemanticName: A", " SemanticIndex: 0 ", " IsAllocated: 1", " StartRow: 0", @@ -4664,7 +4679,7 @@ TEST_F(ValidationTest, PSVStringTableReorder) { " ComponentType: 3", " DynamicIndexMask: 0", "') and DXIL module:('PSVSignatureElement:", - " SemanticName: ", + " SemanticName: B", " SemanticIndex: 0 ", " IsAllocated: 1", " StartRow: 0", @@ -4679,6 +4694,8 @@ TEST_F(ValidationTest, PSVStringTableReorder) { "')", "error: DXIL container mismatch for 'EntryFunctionName' between 'PSV0' " "part:('ain') and DXIL module:('main')", + "error: In 'StringTable', 'A' is not used", + "error: In 'StringTable', 'main' is not used", "error: Container part 'Pipeline State Validation' does not match " "expected for module.", "Validation failed."}, @@ -4697,6 +4714,25 @@ TEST_F(ValidationTest, PSVStringTableReorder) { VERIFY_IS_NOT_NULL(pUpdatedResult); VERIFY_SUCCEEDED(pUpdatedResult->GetStatus(&status)); VERIFY_SUCCEEDED(status); + + // Create unused name in String table. + PSVInfo->EntryFunctionName = UINT32_MAX; + + // Run validation again. + CComPtr pUpdatedTableResult2; + VERIFY_SUCCEEDED( + pValidator->Validate(pProgram, Flags, &pUpdatedTableResult2)); + // Make sure the validation was fail. + VERIFY_IS_NOT_NULL(pUpdatedTableResult2); + VERIFY_SUCCEEDED(pUpdatedTableResult2->GetStatus(&status)); + VERIFY_FAILED(status); + CheckOperationResultMsgs( + pUpdatedTableResult2, + { + "In 'PSV0 part', 'EntryFunctionName' is not well-formed", + "error: In 'StringTable', 'main' is not used", + }, + /*maySucceedAnyway*/ false, /*bRegex*/ false); } class SemanticIndexRotator { @@ -4715,6 +4751,10 @@ class SemanticIndexRotator { SignatureElements[i].SemanticIndexes = SignatureElements[i].SemanticIndexes - 1; } + void Clear(unsigned Index) { + for (unsigned i = 0; i < SignatureElements.size(); ++i) + SignatureElements[i].SemanticIndexes = Index; + } }; TEST_F(ValidationTest, PSVSemanticIndexTableReorder) { @@ -5043,6 +5083,31 @@ TEST_F(ValidationTest, PSVSemanticIndexTableReorder) { VERIFY_IS_NOT_NULL(pUpdatedResult); VERIFY_SUCCEEDED(pUpdatedResult->GetStatus(&status)); VERIFY_SUCCEEDED(status); + + // Clear SemanticIndexes. + InputRotator.Clear(UINT32_MAX); + OutputRotator.Clear(UINT32_MAX); + PatchConstOrPrimRotator.Clear(UINT32_MAX); + + // Run validation again. + CComPtr pUpdatedResult2; + VERIFY_SUCCEEDED(pValidator->Validate(pProgram, Flags, &pUpdatedResult2)); + // Make sure the validation was successful. + VERIFY_IS_NOT_NULL(pUpdatedResult2); + VERIFY_SUCCEEDED(pUpdatedResult2->GetStatus(&status)); + VERIFY_FAILED(status); + + CheckOperationResultMsgs( + pUpdatedResult2, + {"error: In 'PSV0 part', 'SemanticIndex' is not well-formed", + "error: In 'SemanticIndexTable', '0' is not used", + "error: In 'SemanticIndexTable', '2' is not used", + "error: In 'SemanticIndexTable', '3' is not used", + "error: In 'SemanticIndexTable', '4' is not used", + "error: Container part 'Pipeline State Validation' " + "does not match expected for module.", + "Validation failed."}, + /*maySucceedAnyway*/ false, /*bRegex*/ false); } struct SimplePSV { @@ -6034,3 +6099,400 @@ TEST_F(ValidationTest, PSVContentValidationAS) { "Validation failed."}, /*maySucceedAnyway*/ false, /*bRegex*/ false); } + +struct SimpleContainer { + hlsl::DxilContainerHeader *Header; + std::vector PartOffsets; + std::vector PartHeaders; + SimpleContainer(void *Ptr) { + Header = (hlsl::DxilContainerHeader *)Ptr; + hlsl::DxilPartIterator pPartIter(nullptr, 0); + pPartIter = hlsl::begin(Header); + for (unsigned i = 0; i < Header->PartCount; ++i) { + VERIFY_IS_TRUE(pPartIter != hlsl::end(Header)); + PartOffsets.push_back( + (uint32_t)((const char *)(*pPartIter) - (const char *)Header)); + PartHeaders.push_back((const DxilPartHeader *)(*pPartIter)); + ++pPartIter; + } + VERIFY_ARE_EQUAL(pPartIter, hlsl::end(Header)); + } +}; + +TEST_F(ValidationTest, WrongPSVSize) { + if (!m_ver.m_InternalValidator) + if (m_ver.SkipDxilVersion(1, 8)) + return; + + CComPtr pProgram; + CompileFile(L"..\\DXC\\dumpPSV_AS.hlsl", "as_6_8", &pProgram); + + CComPtr pValidator; + CComPtr pResult; + unsigned Flags = 0; + VERIFY_SUCCEEDED( + m_dllSupport.CreateInstance(CLSID_DxcValidator, &pValidator)); + VERIFY_SUCCEEDED(pValidator->Validate(pProgram, Flags, &pResult)); + // Make sure the validation was successful. + HRESULT status; + VERIFY_IS_NOT_NULL(pResult); + VERIFY_SUCCEEDED(pResult->GetStatus(&status)); + VERIFY_SUCCEEDED(status); + + hlsl::DxilContainerHeader *pHeader; + hlsl::DxilPartIterator pPartIter(nullptr, 0); + pHeader = (hlsl::DxilContainerHeader *)pProgram->GetBufferPointer(); + // Make sure the PSV part exists. + pPartIter = + std::find_if(hlsl::begin(pHeader), hlsl::end(pHeader), + hlsl::DxilPartIsType(hlsl::DFCC_PipelineStateValidation)); + VERIFY_ARE_NOT_EQUAL(hlsl::end(pHeader), pPartIter); + + // Create a new Blob which is 16 bytes larger than the original one. + std::vector pProgram2Data(pProgram->GetBufferSize() + 16, 0); + // Copy data from the original blob part by part. + // Copy all parts to program2. + SimpleContainer Container(pProgram->GetBufferPointer()); + uint32_t PartOffsetsSize = pHeader->PartCount * sizeof(uint32_t); + uint32_t Offset = sizeof(hlsl::DxilContainerHeader) + PartOffsetsSize; + std::vector PartOffsets; + const uint32_t ExtraSize = 16; + // copy all parts to program2. + for (unsigned i = 0; i < pHeader->PartCount; ++i) { + PartOffsets.emplace_back(Offset); + const DxilPartHeader *pPartHeader = Container.PartHeaders[i]; + + // Copy part header. + memcpy(pProgram2Data.data() + Offset, pPartHeader, sizeof(DxilPartHeader)); + Offset += sizeof(DxilPartHeader); + + // Copy part content. + uint32_t *PartPtr = + const_cast((const uint32_t *)GetDxilPartData(pPartHeader)); + memcpy(pProgram2Data.data() + Offset, PartPtr, pPartHeader->PartSize); + + Offset += pPartHeader->PartSize; + + if (pPartHeader->PartFourCC == hlsl::DFCC_PipelineStateValidation) { + // Update the size of PSV part. + DxilPartHeader *pPSVPartHeader = + (DxilPartHeader *)(pProgram2Data.data() + PartOffsets.back()); + pPSVPartHeader->PartSize += ExtraSize; + Offset += ExtraSize; + } + } + // Copy header. + pHeader->ContainerSizeInBytes += ExtraSize; + memcpy(pProgram2Data.data(), pHeader, sizeof(hlsl::DxilContainerHeader)); + // Copy partOffsets. + memcpy(pProgram2Data.data() + sizeof(hlsl::DxilContainerHeader), + PartOffsets.data(), PartOffsetsSize); + + // Create a new Blob from pProgram2Data. + CComPtr pProgram2; + CComPtr pLibrary; + VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary)); + VERIFY_SUCCEEDED(pLibrary->CreateBlobWithEncodingFromPinned( + pProgram2Data.data(), pProgram2Data.size(), CP_UTF8, &pProgram2)); + + // Run validation on updated container. + CComPtr pUpdatedResult; + VERIFY_SUCCEEDED(pValidator->Validate(pProgram2, Flags, &pUpdatedResult)); + // Make sure the validation was fail. + VERIFY_IS_NOT_NULL(pUpdatedResult); + VERIFY_SUCCEEDED(pUpdatedResult->GetStatus(&status)); + VERIFY_FAILED(status); + + CheckOperationResultMsgs( + pUpdatedResult, {"In 'DxilContainer', 'PSV0 part' is not well-formed"}, + /*maySucceedAnyway*/ false, /*bRegex*/ false); +} + +TEST_F(ValidationTest, WrongPSVSizeOnZeros) { + if (!m_ver.m_InternalValidator) + if (m_ver.SkipDxilVersion(1, 8)) + return; + + CComPtr pProgram; + CompileFile(L"..\\DXC\\dumpPSV_PS.hlsl", "ps_6_8", &pProgram); + + CComPtr pValidator; + CComPtr pResult; + unsigned Flags = 0; + VERIFY_SUCCEEDED( + m_dllSupport.CreateInstance(CLSID_DxcValidator, &pValidator)); + VERIFY_SUCCEEDED(pValidator->Validate(pProgram, Flags, &pResult)); + // Make sure the validation was successful. + HRESULT status; + VERIFY_IS_NOT_NULL(pResult); + VERIFY_SUCCEEDED(pResult->GetStatus(&status)); + VERIFY_SUCCEEDED(status); + + hlsl::DxilContainerHeader *pHeader; + hlsl::DxilPartIterator pPartIter(nullptr, 0); + pHeader = (hlsl::DxilContainerHeader *)pProgram->GetBufferPointer(); + // Make sure the PSV part exists. + pPartIter = + std::find_if(hlsl::begin(pHeader), hlsl::end(pHeader), + hlsl::DxilPartIsType(hlsl::DFCC_PipelineStateValidation)); + VERIFY_ARE_NOT_EQUAL(hlsl::end(pHeader), pPartIter); + + const DxilPartHeader *pPSVPart = (const DxilPartHeader *)(*pPartIter); + const uint32_t *PSVPtr = (const uint32_t *)GetDxilPartData(pPSVPart); + + uint32_t PSVRuntimeInfo_size = *(PSVPtr++); + VERIFY_ARE_EQUAL(sizeof(PSVRuntimeInfo3), PSVRuntimeInfo_size); + PSVRuntimeInfo3 *PSVInfo = + const_cast((const PSVRuntimeInfo3 *)PSVPtr); + VERIFY_ARE_EQUAL(2u, PSVInfo->SigInputElements); + PSVPtr += PSVRuntimeInfo_size / 4; + uint32_t *ResourceCountPtr = const_cast(PSVPtr++); + uint32_t ResourceCount = *ResourceCountPtr; + VERIFY_ARE_NOT_EQUAL(0u, ResourceCount); + uint32_t ResourceBindingsSize = *(PSVPtr++); + PSVPtr += (ResourceCount * ResourceBindingsSize) / 4; + uint32_t *StringTableSizePtr = const_cast(PSVPtr++); + uint32_t StringTableSize = *StringTableSizePtr; + // Skip string table. + PSVPtr += StringTableSize / 4; + uint32_t *SemanticIndexTableEntriesPtr = const_cast(PSVPtr++); + uint32_t SemanticIndexTableEntries = *SemanticIndexTableEntriesPtr; + + *SemanticIndexTableEntriesPtr = 0; + + // Run validation on updated container. + CComPtr pUpdatedResult1; + VERIFY_SUCCEEDED(pValidator->Validate(pProgram, Flags, &pUpdatedResult1)); + // Make sure the validation was fail. + VERIFY_IS_NOT_NULL(pUpdatedResult1); + VERIFY_SUCCEEDED(pUpdatedResult1->GetStatus(&status)); + VERIFY_FAILED(status); + + CheckOperationResultMsgs( + pUpdatedResult1, {"In 'DxilContainer', 'PSV0 part' is not well-formed"}, + /*maySucceedAnyway*/ false, /*bRegex*/ false); + + *SemanticIndexTableEntriesPtr = SemanticIndexTableEntries; + *StringTableSizePtr = 0; + + // Run validation on updated container. + CComPtr pUpdatedResult2; + VERIFY_SUCCEEDED(pValidator->Validate(pProgram, Flags, &pUpdatedResult2)); + // Make sure the validation was fail. + VERIFY_IS_NOT_NULL(pUpdatedResult2); + VERIFY_SUCCEEDED(pUpdatedResult2->GetStatus(&status)); + VERIFY_FAILED(status); + + CheckOperationResultMsgs( + pUpdatedResult2, {"In 'DxilContainer', 'PSV0 part' is not well-formed"}, + /*maySucceedAnyway*/ false, /*bRegex*/ false); + + *StringTableSizePtr = StringTableSize; + *ResourceCountPtr = 0; + + // Run validation on updated container. + CComPtr pUpdatedResult3; + VERIFY_SUCCEEDED(pValidator->Validate(pProgram, Flags, &pUpdatedResult3)); + // Make sure the validation was fail. + VERIFY_IS_NOT_NULL(pUpdatedResult3); + VERIFY_SUCCEEDED(pUpdatedResult3->GetStatus(&status)); + VERIFY_FAILED(status); + + CheckOperationResultMsgs( + pUpdatedResult3, {"In 'DxilContainer', 'PSV0 part' is not well-formed"}, + /*maySucceedAnyway*/ false, /*bRegex*/ false); + *ResourceCountPtr = ResourceCount; +} + +TEST_F(ValidationTest, WrongPSVVersion) { + if (!m_ver.m_InternalValidator) + if (m_ver.SkipDxilVersion(1, 8)) + return; + + CComPtr pProgram60; + std::vector args; + args.emplace_back(L"-validator-version"); + args.emplace_back(L"1.0"); + CompileFile(L"..\\DXC\\dumpPSV_CS.hlsl", "cs_6_0", args.data(), args.size(), + &pProgram60); + + CComPtr pValidator; + CComPtr pResult; + unsigned Flags = DxcValidatorFlags_InPlaceEdit; + VERIFY_SUCCEEDED( + m_dllSupport.CreateInstance(CLSID_DxcValidator, &pValidator)); + VERIFY_SUCCEEDED(pValidator->Validate(pProgram60, Flags, &pResult)); + // Make sure the validation was successful. + HRESULT status; + VERIFY_IS_NOT_NULL(pResult); + VERIFY_SUCCEEDED(pResult->GetStatus(&status)); + VERIFY_SUCCEEDED(status); + + hlsl::DxilContainerHeader *pHeader60; + hlsl::DxilPartIterator pPartIter(nullptr, 0); + pHeader60 = (hlsl::DxilContainerHeader *)pProgram60->GetBufferPointer(); + // Make sure the PSV part exists. + pPartIter = + std::find_if(hlsl::begin(pHeader60), hlsl::end(pHeader60), + hlsl::DxilPartIsType(hlsl::DFCC_PipelineStateValidation)); + VERIFY_ARE_NOT_EQUAL(hlsl::end(pHeader60), pPartIter); + + CComPtr pProgram68; + + CompileFile(L"..\\DXC\\dumpPSV_CS.hlsl", "cs_6_8", &pProgram68); + CComPtr pResult2; + VERIFY_SUCCEEDED(pValidator->Validate(pProgram68, Flags, &pResult2)); + // Make sure the validation was successful. + VERIFY_IS_NOT_NULL(pResult); + VERIFY_SUCCEEDED(pResult->GetStatus(&status)); + VERIFY_SUCCEEDED(status); + + hlsl::DxilContainerHeader *pHeader68; + pHeader68 = (hlsl::DxilContainerHeader *)pProgram68->GetBufferPointer(); + // Make sure the PSV part exists. + pPartIter = + std::find_if(hlsl::begin(pHeader68), hlsl::end(pHeader68), + hlsl::DxilPartIsType(hlsl::DFCC_PipelineStateValidation)); + VERIFY_ARE_NOT_EQUAL(hlsl::end(pHeader68), pPartIter); + + // Switch the PSV part between 6.0 to 6.8. + SimpleContainer Container60(pProgram60->GetBufferPointer()); + SimpleContainer Container68(pProgram68->GetBufferPointer()); + uint32_t Container60WithPSV68Size = sizeof(hlsl::DxilContainerHeader) + + pHeader60->PartCount * sizeof(uint32_t); + uint32_t Container68WithPSV60Size = sizeof(hlsl::DxilContainerHeader) + + pHeader60->PartCount * sizeof(uint32_t); + unsigned Container68PartSkipped = 0; + for (unsigned i = 0; i < pHeader60->PartCount; ++i) { + const DxilPartHeader *pPartHeader60 = Container60.PartHeaders[i]; + const DxilPartHeader *pPartHeader68 = + Container68.PartHeaders[i + Container68PartSkipped]; + if (pPartHeader68->PartFourCC == hlsl::DFCC_ShaderHash) { + Container68PartSkipped++; + pPartHeader68 = Container68.PartHeaders[i + Container68PartSkipped]; + } + + VERIFY_ARE_EQUAL(pPartHeader60->PartFourCC, pPartHeader68->PartFourCC); + Container60WithPSV68Size += sizeof(DxilPartHeader); + Container68WithPSV60Size += sizeof(DxilPartHeader); + if (pPartHeader60->PartFourCC == hlsl::DFCC_PipelineStateValidation) { + Container60WithPSV68Size += pPartHeader68->PartSize; + Container68WithPSV60Size += pPartHeader60->PartSize; + } else { + Container60WithPSV68Size += pPartHeader60->PartSize; + Container68WithPSV60Size += pPartHeader68->PartSize; + } + } + + // Create mixed container. + std::vector pProgram60WithPSV68Data(Container60WithPSV68Size, 0); + std::vector pProgram68WithPSV60Data(Container68WithPSV60Size, 0); + + uint32_t PartOffsetsSize = pHeader60->PartCount * sizeof(uint32_t); + uint32_t Offset60 = sizeof(hlsl::DxilContainerHeader) + PartOffsetsSize; + std::vector PartOffsets60; + uint32_t Offset68 = sizeof(hlsl::DxilContainerHeader) + PartOffsetsSize; + std::vector PartOffsets68; + Container68PartSkipped = 0; + for (unsigned i = 0; i < pHeader60->PartCount; ++i) { + PartOffsets60.emplace_back(Offset60); + PartOffsets68.emplace_back(Offset68); + + const DxilPartHeader *pPartHeader60 = Container60.PartHeaders[i]; + const DxilPartHeader *pPartHeader68 = + Container68.PartHeaders[i + Container68PartSkipped]; + if (pPartHeader68->PartFourCC == hlsl::DFCC_ShaderHash) { + Container68PartSkipped++; + pPartHeader68 = Container68.PartHeaders[i + Container68PartSkipped]; + } + + if (pPartHeader60->PartFourCC == hlsl::DFCC_PipelineStateValidation) { + // Copy PSV part from 6.8 to 6.0. + memcpy(pProgram60WithPSV68Data.data() + Offset60, pPartHeader68, + sizeof(DxilPartHeader)); + Offset60 += sizeof(DxilPartHeader); + memcpy(pProgram60WithPSV68Data.data() + Offset60, + GetDxilPartData(pPartHeader68), pPartHeader68->PartSize); + Offset60 += pPartHeader68->PartSize; + // Copy PSV part from 6.0 to 6.8. + memcpy(pProgram68WithPSV60Data.data() + Offset68, pPartHeader60, + sizeof(DxilPartHeader)); + Offset68 += sizeof(DxilPartHeader); + memcpy(pProgram68WithPSV60Data.data() + Offset68, + GetDxilPartData(pPartHeader60), pPartHeader60->PartSize); + + Offset68 += pPartHeader60->PartSize; + } else { + // Copy PSV part from 6.0 to 6.0. + memcpy(pProgram60WithPSV68Data.data() + Offset60, pPartHeader60, + sizeof(DxilPartHeader)); + Offset60 += sizeof(DxilPartHeader); + memcpy(pProgram60WithPSV68Data.data() + Offset60, + GetDxilPartData(pPartHeader60), pPartHeader60->PartSize); + Offset60 += pPartHeader60->PartSize; + // Copy PSV part from 6.8 to 6.8. + memcpy(pProgram68WithPSV60Data.data() + Offset68, pPartHeader68, + sizeof(DxilPartHeader)); + Offset68 += sizeof(DxilPartHeader); + memcpy(pProgram68WithPSV60Data.data() + Offset68, + GetDxilPartData(pPartHeader68), pPartHeader68->PartSize); + Offset68 += pPartHeader68->PartSize; + } + } + + // Copy header. + VERIFY_ARE_EQUAL(Container60WithPSV68Size, Offset60); + pHeader60->ContainerSizeInBytes = Container60WithPSV68Size; + memcpy(pProgram60WithPSV68Data.data(), pHeader60, + sizeof(hlsl::DxilContainerHeader)); + VERIFY_ARE_EQUAL(Container68WithPSV60Size, Offset68); + pHeader68->ContainerSizeInBytes = Container68WithPSV60Size; + pHeader68->PartCount -= Container68PartSkipped; + memcpy(pProgram68WithPSV60Data.data(), pHeader68, + sizeof(hlsl::DxilContainerHeader)); + // Copy partOffsets. + memcpy(pProgram60WithPSV68Data.data() + sizeof(hlsl::DxilContainerHeader), + PartOffsets60.data(), PartOffsetsSize); + memcpy(pProgram68WithPSV60Data.data() + sizeof(hlsl::DxilContainerHeader), + PartOffsets68.data(), PartOffsetsSize); + + // Create a new Blob. + CComPtr pProgram60WithPSV68; + CComPtr pLibrary; + VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary)); + VERIFY_SUCCEEDED(pLibrary->CreateBlobWithEncodingFromPinned( + pProgram60WithPSV68Data.data(), pProgram60WithPSV68Data.size(), CP_UTF8, + &pProgram60WithPSV68)); + + // Run validation on new containers. + CComPtr p60WithPSV68Result; + VERIFY_SUCCEEDED( + pValidator->Validate(pProgram60WithPSV68, Flags, &p60WithPSV68Result)); + // Make sure the validation was fail. + VERIFY_IS_NOT_NULL(p60WithPSV68Result); + VERIFY_SUCCEEDED(p60WithPSV68Result->GetStatus(&status)); + VERIFY_FAILED(status); + CheckOperationResultMsgs( + p60WithPSV68Result, + {"DXIL container mismatch for 'PSVRuntimeInfoSize' between 'PSV0' " + "part:('52') and DXIL module:('24')"}, + /*maySucceedAnyway*/ false, /*bRegex*/ false); + + // Create a new Blob. + CComPtr pProgram68WithPSV60; + VERIFY_SUCCEEDED(pLibrary->CreateBlobWithEncodingFromPinned( + pProgram68WithPSV60Data.data(), pProgram68WithPSV60Data.size(), CP_UTF8, + &pProgram68WithPSV60)); + CComPtr p68WithPSV60Result; + VERIFY_SUCCEEDED( + pValidator->Validate(pProgram68WithPSV60, Flags, &p68WithPSV60Result)); + // Make sure the validation was fail. + VERIFY_IS_NOT_NULL(p68WithPSV60Result); + VERIFY_SUCCEEDED(p68WithPSV60Result->GetStatus(&status)); + VERIFY_FAILED(status); + CheckOperationResultMsgs( + p68WithPSV60Result, + {"DXIL container mismatch for 'PSVRuntimeInfoSize' between 'PSV0' " + "part:('24') and DXIL module:('52')"}, + /*maySucceedAnyway*/ false, /*bRegex*/ false); +} diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index 69d1e4293f..19220d6d1a 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -6933,6 +6933,11 @@ def build_valrules(self): "DXIL Container Content is well-formed", "In '%0', '%1' is not well-formed", ) + self.add_valrule_msg( + "Container.UnusedItemInTable", + "Items in Table must be used", + "In '%0', '%1' is not used", + ) self.add_valrule("Meta.Required", "Required metadata missing.") self.add_valrule_msg( "Meta.ComputeWithNode",