[DX] Fix PSV resource serialization
authorChris Bieneman <chris.bieneman@me.com>
Tue, 18 Jul 2023 00:33:16 +0000 (19:33 -0500)
committerChris Bieneman <chris.bieneman@me.com>
Wed, 19 Jul 2023 23:01:01 +0000 (18:01 -0500)
When writing this initially I missed including the resource stride.
This change adds the resources stride to the serialized value.

I've also extended the testing and error reporting around parsing PSV
information. This adds tests to verify that the reader produces
meaningful error messages for malformed DXContainer files, and a test
that verifies the resource stride is respected in the reader even if
the stride isn't an expected or known value (as would happen if the
format changes in the future).

This is part of #59479.

Reviewed By: bogner, bob80905

Differential Revision: https://reviews.llvm.org/D155143

31 files changed:
llvm/include/llvm/Object/DXContainer.h
llvm/include/llvm/ObjectYAML/DXContainerYAML.h
llvm/lib/MC/DXContainerPSVInfo.cpp
llvm/lib/Object/DXContainer.cpp
llvm/lib/ObjectYAML/DXContainerYAML.cpp
llvm/test/ObjectYAML/DXContainer/PSVv0-amplification.yaml
llvm/test/ObjectYAML/DXContainer/PSVv0-compute.yaml
llvm/test/ObjectYAML/DXContainer/PSVv0-domain.yaml
llvm/test/ObjectYAML/DXContainer/PSVv0-geometry.yaml
llvm/test/ObjectYAML/DXContainer/PSVv0-hull.yaml
llvm/test/ObjectYAML/DXContainer/PSVv0-mesh.yaml
llvm/test/ObjectYAML/DXContainer/PSVv0-pixel.yaml
llvm/test/ObjectYAML/DXContainer/PSVv0-vertex.yaml
llvm/test/ObjectYAML/DXContainer/PSVv1-amplification.yaml
llvm/test/ObjectYAML/DXContainer/PSVv1-compute.yaml
llvm/test/ObjectYAML/DXContainer/PSVv1-domain.yaml
llvm/test/ObjectYAML/DXContainer/PSVv1-geometry.yaml
llvm/test/ObjectYAML/DXContainer/PSVv1-hull.yaml
llvm/test/ObjectYAML/DXContainer/PSVv1-mesh.yaml
llvm/test/ObjectYAML/DXContainer/PSVv1-pixel.yaml
llvm/test/ObjectYAML/DXContainer/PSVv1-vertex.yaml
llvm/test/ObjectYAML/DXContainer/PSVv2-amplification.yaml
llvm/test/ObjectYAML/DXContainer/PSVv2-compute.yaml
llvm/test/ObjectYAML/DXContainer/PSVv2-domain.yaml
llvm/test/ObjectYAML/DXContainer/PSVv2-geometry.yaml
llvm/test/ObjectYAML/DXContainer/PSVv2-hull.yaml
llvm/test/ObjectYAML/DXContainer/PSVv2-mesh.yaml
llvm/test/ObjectYAML/DXContainer/PSVv2-pixel.yaml
llvm/test/ObjectYAML/DXContainer/PSVv2-vertex.yaml
llvm/tools/obj2yaml/dxcontainer2yaml.cpp
llvm/unittests/Object/DXContainerTest.cpp

index 55371d1..ece2dfd 100644 (file)
@@ -35,16 +35,19 @@ class PSVRuntimeInfo {
   // swaps it as appropriate.
   struct ResourceArray {
     StringRef Data;
-    size_t Stride; // size of each element in the list.
+    uint32_t Stride; // size of each element in the list.
 
     ResourceArray() = default;
     ResourceArray(StringRef D, size_t S) : Data(D), Stride(S) {}
 
     using value_type = dxbc::PSV::v2::ResourceBindInfo;
+    static constexpr uint32_t MaxStride() {
+      return static_cast<uint32_t>(sizeof(value_type));
+    }
 
     struct iterator {
       StringRef Data;
-      size_t Stride; // size of each element in the list.
+      uint32_t Stride; // size of each element in the list.
       const char *Current;
 
       iterator(const ResourceArray &A, const char *C)
@@ -58,7 +61,8 @@ class PSVRuntimeInfo {
         value_type Val = {{0, 0, 0, 0}, 0, 0};
         if (Current >= Data.end())
           return Val;
-        memcpy(static_cast<void *>(&Val), Current, Stride);
+        memcpy(static_cast<void *>(&Val), Current,
+               std::min(Stride, MaxStride()));
         if (sys::IsBigEndianHost)
           Val.swapBytes();
         return Val;
@@ -123,6 +127,8 @@ public:
                : (Size >= sizeof(dxbc::PSV::v1::RuntimeInfo) ? 1 : 0);
   }
 
+  uint32_t getResourceStride() const { return Resources.Stride; }
+
   const InfoStruct &getInfo() const { return BasicInfo; }
 };
 
index 299c8bf..5dff8b3 100644 (file)
@@ -80,6 +80,7 @@ struct PSVInfo {
   uint32_t Version;
 
   dxbc::PSV::v2::RuntimeInfo Info;
+  uint32_t ResourceStride;
   std::vector<ResourceBindInfo> Resources;
 
   void mapInfoForVersion(yaml::IO &IO);
index a3444d0..ea12495 100644 (file)
@@ -16,16 +16,20 @@ using namespace llvm::dxbc::PSV;
 
 void PSVRuntimeInfo::write(raw_ostream &OS, uint32_t Version) const {
   uint32_t InfoSize;
+  uint32_t BindingSize;
   switch (Version) {
   case 0:
     InfoSize = sizeof(dxbc::PSV::v0::RuntimeInfo);
+    BindingSize = sizeof(dxbc::PSV::v0::ResourceBindInfo);
     break;
   case 1:
     InfoSize = sizeof(dxbc::PSV::v1::RuntimeInfo);
+    BindingSize = sizeof(dxbc::PSV::v0::ResourceBindInfo);
     break;
   case 2:
   default:
     InfoSize = sizeof(dxbc::PSV::v2::RuntimeInfo);
+    BindingSize = sizeof(dxbc::PSV::v2::ResourceBindInfo);
   }
   uint32_t InfoSizeSwapped = InfoSize;
   if (sys::IsBigEndianHost)
@@ -40,8 +44,7 @@ void PSVRuntimeInfo::write(raw_ostream &OS, uint32_t Version) const {
     sys::swapByteOrder(ResourceCount);
   OS.write(reinterpret_cast<const char *>(&ResourceCount), sizeof(uint32_t));
 
-  size_t BindingSize = (Version < 2) ? sizeof(v0::ResourceBindInfo)
-                                     : sizeof(v2::ResourceBindInfo);
+  OS.write(reinterpret_cast<const char *>(&BindingSize), sizeof(uint32_t));
   for (const auto &Res : Resources)
     OS.write(reinterpret_cast<const char *>(&Res), BindingSize);
 }
index a0f0698..48932af 100644 (file)
@@ -200,6 +200,10 @@ Error DirectX::PSVRuntimeInfo::parse(uint16_t ShaderKind) {
 
   StringRef PSVInfoData = Data.substr(sizeof(uint32_t), Size);
 
+  if (PSVInfoData.size() < Size)
+    return parseFailed(
+        "Pipeline state data extends beyond the bounds of the part");
+
   using namespace dxbc::PSV;
 
   const uint32_t PSVVersion = getVersion();
@@ -234,12 +238,20 @@ Error DirectX::PSVRuntimeInfo::parse(uint16_t ShaderKind) {
     return Err;
   Current += sizeof(uint32_t);
 
-  Resources.Stride = (PSVVersion < 2) ? sizeof(v0::ResourceBindInfo)
-                                      : sizeof(v2::ResourceBindInfo);
-  size_t BindingDataSize = Resources.Stride * ResourceCount;
-  Resources.Data = Data.substr(Current - Data.begin(), BindingDataSize);
+  if (ResourceCount > 0) {
+    if (Error Err = readInteger(Data, Current, Resources.Stride))
+      return Err;
+    Current += sizeof(uint32_t);
+
+    size_t BindingDataSize = Resources.Stride * ResourceCount;
+    Resources.Data = Data.substr(Current - Data.begin(), BindingDataSize);
 
-  Current += BindingDataSize;
+    if (Resources.Data.size() < BindingDataSize)
+      return parseFailed(
+          "Resource binding data extends beyond the bounds of the part");
+
+    Current += BindingDataSize;
+  }
 
   return Error::success();
 }
index 741b0cb..ed9f399 100644 (file)
@@ -127,6 +127,7 @@ void MappingTraits<DXContainerYAML::PSVInfo>::mapping(
   IO.mapRequired("ShaderStage", PSV.Info.ShaderStage);
   PSV.mapInfoForVersion(IO);
 
+  IO.mapRequired("ResourceStride", PSV.ResourceStride);
   IO.mapRequired("Resources", PSV.Resources);
 
   // Restore the YAML context.
index 4b9999f..d15bfad 100644 (file)
@@ -17,6 +17,7 @@ Parts:
       PayloadSizeInBytes: 4092
       MinimumWaveLaneCount: 0
       MaximumWaveLaneCount: 4294967295
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -45,6 +46,7 @@ Parts:
 # CHECK-NEXT: PayloadSizeInBytes: 4092
 # CHECK-NEXT: MinimumWaveLaneCount: 0
 # CHECK-NEXT: MaximumWaveLaneCount: 4294967295
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index 84111d0..7e9f2fb 100644 (file)
@@ -16,6 +16,7 @@ Parts:
       ShaderStage:     5
       MinimumWaveLaneCount: 0
       MaximumWaveLaneCount: 4294967295
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -43,6 +44,7 @@ Parts:
 # CHECK-NEXT: ShaderStage:     5
 # CHECK-NEXT: MinimumWaveLaneCount: 0
 # CHECK-NEXT: MaximumWaveLaneCount: 4294967295
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index c5ca43a..db2aee9 100644 (file)
@@ -19,6 +19,7 @@ Parts:
       TessellatorDomain: 2056
       MinimumWaveLaneCount: 0
       MaximumWaveLaneCount: 4294967295
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -49,6 +50,7 @@ Parts:
 # CHECK-NEXT: TessellatorDomain: 2056
 # CHECK-NEXT: MinimumWaveLaneCount: 0
 # CHECK-NEXT: MaximumWaveLaneCount: 4294967295
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index 96e8b27..5509ac6 100644 (file)
@@ -20,6 +20,7 @@ Parts:
       OutputPositionPresent: 1
       MinimumWaveLaneCount: 0
       MaximumWaveLaneCount: 4294967295
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -51,6 +52,7 @@ Parts:
 # CHECK-NEXT: OutputPositionPresent: 1
 # CHECK-NEXT: MinimumWaveLaneCount: 0
 # CHECK-NEXT: MaximumWaveLaneCount: 4294967295
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index 8113874..cd60f2b 100644 (file)
@@ -20,6 +20,7 @@ Parts:
       TessellatorOutputPrimitive: 8192
       MinimumWaveLaneCount: 0
       MaximumWaveLaneCount: 4294967295
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -51,6 +52,7 @@ Parts:
 # CHECK-NEXT: TessellatorOutputPrimitive: 8192
 # CHECK-NEXT: MinimumWaveLaneCount: 0
 # CHECK-NEXT: MaximumWaveLaneCount: 4294967295
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index 4d8c6d4..07fb656 100644 (file)
@@ -21,6 +21,7 @@ Parts:
       MaxOutputPrimitives: 4092
       MinimumWaveLaneCount: 0
       MaximumWaveLaneCount: 4294967295
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -53,6 +54,7 @@ Parts:
 # CHECK-NEXT: MaxOutputPrimitives: 4092
 # CHECK-NEXT: MinimumWaveLaneCount: 0
 # CHECK-NEXT: MaximumWaveLaneCount: 4294967295
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index c05471c..c7f956e 100644 (file)
@@ -18,6 +18,7 @@ Parts:
       SampleFrequency: 96
       MinimumWaveLaneCount: 0
       MaximumWaveLaneCount: 4294967295
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -47,6 +48,7 @@ Parts:
 # CHECK-NEXT: SampleFrequency: 96
 # CHECK-NEXT: MinimumWaveLaneCount: 0
 # CHECK-NEXT: MaximumWaveLaneCount: 4294967295
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index fe176ad..6df9169 100644 (file)
@@ -17,6 +17,7 @@ Parts:
       OutputPositionPresent: 1
       MinimumWaveLaneCount: 0
       MaximumWaveLaneCount: 4294967295
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -45,6 +46,7 @@ Parts:
 # CHECK-NEXT: OutputPositionPresent: 1
 # CHECK-NEXT: MinimumWaveLaneCount: 0
 # CHECK-NEXT: MaximumWaveLaneCount: 4294967295
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index 310df02..94c215c 100644 (file)
@@ -23,6 +23,7 @@ Parts:
       SigPatchConstOrPrimElements: 32
       SigInputVectors: 64
       SigOutputVectors: [ 8, 16, 32, 64 ]
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -57,6 +58,7 @@ Parts:
 # CHECK-NEXT: SigPatchConstOrPrimElements: 32
 # CHECK-NEXT: SigInputVectors: 64
 # CHECK-NEXT: SigOutputVectors: [ 8, 16, 32, 64 ]
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index d8233a7..715b28b 100644 (file)
@@ -22,6 +22,7 @@ Parts:
       SigPatchConstOrPrimElements: 32
       SigInputVectors: 64
       SigOutputVectors: [ 8, 16, 32, 64 ]
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -55,6 +56,7 @@ Parts:
 # CHECK-NEXT: SigPatchConstOrPrimElements: 32
 # CHECK-NEXT: SigInputVectors: 64
 # CHECK-NEXT: SigOutputVectors: [ 8, 16, 32, 64 ]
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index 87826ed..472b5e0 100644 (file)
@@ -26,6 +26,7 @@ Parts:
       SigPatchConstOrPrimElements: 32
       SigInputVectors: 64
       SigOutputVectors: [ 8, 16, 32, 64 ]
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -63,6 +64,7 @@ Parts:
 # CHECK-NEXT: SigPatchConstOrPrimElements: 32
 # CHECK-NEXT: SigInputVectors: 64
 # CHECK-NEXT: SigOutputVectors: [ 8, 16, 32, 64 ]
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index a081cde..0c0a4ed 100644 (file)
@@ -27,6 +27,7 @@ Parts:
       SigPatchConstOrPrimElements: 32
       SigInputVectors: 64
       SigOutputVectors: [ 8, 16, 32, 64 ]
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -65,6 +66,7 @@ Parts:
 # CHECK-NEXT: SigPatchConstOrPrimElements: 32
 # CHECK-NEXT: SigInputVectors: 64
 # CHECK-NEXT: SigOutputVectors: [ 8, 16, 32, 64 ]
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index 4c5f60d..330d24f 100644 (file)
@@ -27,6 +27,7 @@ Parts:
       SigPatchConstOrPrimElements: 32
       SigInputVectors: 64
       SigOutputVectors: [ 8, 16, 32, 64 ]
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -65,6 +66,7 @@ Parts:
 # CHECK-NEXT: SigPatchConstOrPrimElements: 32
 # CHECK-NEXT: SigInputVectors: 64
 # CHECK-NEXT: SigOutputVectors: [ 8, 16, 32, 64 ]
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index c969620..51bd639 100644 (file)
@@ -29,6 +29,7 @@ Parts:
       SigPatchConstOrPrimElements: 32
       SigInputVectors: 64
       SigOutputVectors: [ 8, 16, 32, 64 ]
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -69,6 +70,7 @@ Parts:
 # CHECK-NEXT: SigPatchConstOrPrimElements: 32
 # CHECK-NEXT: SigInputVectors: 64
 # CHECK-NEXT: SigOutputVectors: [ 8, 16, 32, 64 ]
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index 279fd09..2e49034 100644 (file)
@@ -24,6 +24,7 @@ Parts:
       SigPatchConstOrPrimElements: 32
       SigInputVectors: 64
       SigOutputVectors: [ 8, 16, 32, 64 ]
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -59,6 +60,7 @@ Parts:
 # CHECK-NEXT: SigPatchConstOrPrimElements: 32
 # CHECK-NEXT: SigInputVectors: 64
 # CHECK-NEXT: SigOutputVectors: [ 8, 16, 32, 64 ]
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index aa2a17a..ad8f082 100644 (file)
@@ -23,6 +23,7 @@ Parts:
       SigPatchConstOrPrimElements: 32
       SigInputVectors: 64
       SigOutputVectors: [ 8, 16, 32, 64 ]
+      ResourceStride:       16
       Resources:
         - Type:            1
           Space:           2
@@ -57,6 +58,7 @@ Parts:
 # CHECK-NEXT: SigPatchConstOrPrimElements: 32
 # CHECK-NEXT: SigInputVectors: 64
 # CHECK-NEXT: SigOutputVectors: [ 8, 16, 32, 64 ]
+# CHECK-NEXT: ResourceStride: 16
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index f2b431e..d4fee91 100644 (file)
@@ -26,6 +26,7 @@ Parts:
       NumThreadsX:     512
       NumThreadsY:     1024
       NumThreadsZ:     2048
+      ResourceStride:       24
       Resources:
         - Type:            1
           Space:           2
@@ -67,6 +68,7 @@ Parts:
 # CHECK-NEXT: NumThreadsX:     512
 # CHECK-NEXT: NumThreadsY:     1024
 # CHECK-NEXT: NumThreadsZ:     2048
+# CHECK-NEXT: ResourceStride: 24
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index fd477b3..5ee136b 100644 (file)
@@ -25,6 +25,7 @@ Parts:
       NumThreadsX:     512
       NumThreadsY:     1024
       NumThreadsZ:     2048
+      ResourceStride:       24
       Resources:
         - Type:            1
           Space:           2
@@ -65,6 +66,7 @@ Parts:
 # CHECK-NEXT: NumThreadsX:     512
 # CHECK-NEXT: NumThreadsY:     1024
 # CHECK-NEXT: NumThreadsZ:     2048
+# CHECK-NEXT: ResourceStride: 24
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index f339a42..4ed9f04 100644 (file)
@@ -29,6 +29,7 @@ Parts:
       NumThreadsX:     512
       NumThreadsY:     1024
       NumThreadsZ:     2048
+      ResourceStride:       24
       Resources:
         - Type:            1
           Space:           2
@@ -73,6 +74,7 @@ Parts:
 # CHECK-NEXT: NumThreadsX:     512
 # CHECK-NEXT: NumThreadsY:     1024
 # CHECK-NEXT: NumThreadsZ:     2048
+# CHECK-NEXT: ResourceStride: 24
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index 09150dd..66def9b 100644 (file)
@@ -30,6 +30,7 @@ Parts:
       NumThreadsX:     512
       NumThreadsY:     1024
       NumThreadsZ:     2048
+      ResourceStride:       24
       Resources:
         - Type:            1
           Space:           2
@@ -75,6 +76,7 @@ Parts:
 # CHECK-NEXT: NumThreadsX:     512
 # CHECK-NEXT: NumThreadsY:     1024
 # CHECK-NEXT: NumThreadsZ:     2048
+# CHECK-NEXT: ResourceStride: 24
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index 9a07346..0a35992 100644 (file)
@@ -30,6 +30,7 @@ Parts:
       NumThreadsX:     512
       NumThreadsY:     1024
       NumThreadsZ:     2048
+      ResourceStride:       24
       Resources:
         - Type:            1
           Space:           2
@@ -75,6 +76,7 @@ Parts:
 # CHECK-NEXT: NumThreadsX:     512
 # CHECK-NEXT: NumThreadsY:     1024
 # CHECK-NEXT: NumThreadsZ:     2048
+# CHECK-NEXT: ResourceStride: 24
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index d6e8be4..ec81b60 100644 (file)
@@ -32,6 +32,7 @@ Parts:
       NumThreadsX:     512
       NumThreadsY:     1024
       NumThreadsZ:     2048
+      ResourceStride:       24
       Resources:
         - Type:            1
           Space:           2
@@ -79,6 +80,7 @@ Parts:
 # CHECK-NEXT: NumThreadsX:     512
 # CHECK-NEXT: NumThreadsY:     1024
 # CHECK-NEXT: NumThreadsZ:     2048
+# CHECK-NEXT: ResourceStride: 24
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index 841c1e9..60e53f8 100644 (file)
@@ -27,6 +27,7 @@ Parts:
       NumThreadsX:     512
       NumThreadsY:     1024
       NumThreadsZ:     2048
+      ResourceStride:       24
       Resources:
         - Type:            1
           Space:           2
@@ -69,6 +70,7 @@ Parts:
 # CHECK-NEXT: NumThreadsX:     512
 # CHECK-NEXT: NumThreadsY:     1024
 # CHECK-NEXT: NumThreadsZ:     2048
+# CHECK-NEXT: ResourceStride: 24
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index 786cbc7..15b227c 100644 (file)
@@ -26,6 +26,7 @@ Parts:
       NumThreadsX:     512
       NumThreadsY:     1024
       NumThreadsZ:     2048
+      ResourceStride:       24
       Resources:
         - Type:            1
           Space:           2
@@ -67,6 +68,7 @@ Parts:
 # CHECK-NEXT: NumThreadsX:     512
 # CHECK-NEXT: NumThreadsY:     1024
 # CHECK-NEXT: NumThreadsZ:     2048
+# CHECK-NEXT: ResourceStride: 24
 # CHECK-NEXT: Resources:
 # CHECK-NEXT: - Type:            1
 # CHECK-NEXT: Space:           2
index f33f565..dd3bea6 100644 (file)
@@ -89,6 +89,7 @@ dumpDXContainer(MemoryBufferRef Source) {
       else if (const auto *P =
                    std::get_if<dxbc::PSV::v2::RuntimeInfo>(&PSVInfo->getInfo()))
         NewPart.Info = DXContainerYAML::PSVInfo(P);
+      NewPart.Info->ResourceStride = PSVInfo->getResourceStride();
       for (auto Res : PSVInfo->getResources())
         NewPart.Info->Resources.push_back(Res);
       break;
index 4d7daaf..151fab0 100644 (file)
@@ -219,6 +219,7 @@ Parts:
       PayloadSizeInBytes: 4092
       MinimumWaveLaneCount: 0
       MaximumWaveLaneCount: 4294967295
+      ResourceStride:  16
       Resources:
         - Type:            1
           Space:           1
@@ -334,9 +335,261 @@ Parts:
     EXPECT_EQ(Binding.Type, 0u);
     EXPECT_EQ(Binding.Flags, 0u);
   }
-  
+
   Binding = *It;
 
   EXPECT_EQ(Binding.Type, 3u);
   EXPECT_EQ(Binding.Flags, 0u);
 }
+
+// The malicious file bits in these tests are mutations of the binary produced
+// by the following YAML:
+//
+// --- !dxcontainer
+// Header:
+//   Hash:            [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+//                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
+//   Version:
+//     Major:           1
+//     Minor:           0
+//   PartCount:       3
+// Parts:
+//   - Name:            DXIL
+//     Size:            24
+//     Program:
+//       MajorVersion:    6
+//       MinorVersion:    0
+//       ShaderKind:      14
+//       Size:            6
+//       DXILMajorVersion: 0
+//       DXILMinorVersion: 1
+//       DXILSize:        0
+//   - Name:            PSV0
+//     Size:            36
+//     PSVInfo:
+//       Version:         0
+//       ShaderStage:     5
+//       MinimumWaveLaneCount: 0
+//       MaximumWaveLaneCount: 0
+//       ResourceStride:       16
+//       Resources: []
+//   - Name: BLEH
+//     Size: 16
+// ...
+
+TEST(DXCFile, MaliciousFiles) {
+
+  // In this file blob, the file size is specified as 96 bytes (0x60), and the
+  // PSV0 data is specified as 24 bytes (0x18) which extends beyond the size of
+  // the file.
+  {
+    uint8_t Buffer[] = {
+        0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+        0x48, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C, 0x18, 0x00, 0x00, 0x00,
+        0x60, 0x00, 0x0E, 0x00, 0x06, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C,
+        0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x50, 0x53, 0x56, 0x30, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    };
+    EXPECT_THAT_EXPECTED(
+        DXContainer::create(getMemoryBuffer<96>(Buffer)),
+        FailedWithMessage(
+            "Pipeline state data extends beyond the bounds of the part"));
+  }
+
+  // PSV extends beyond part, but in file range. In this blob the file size is
+  // 144 bytes (0x90), and the PSV part is 36 bytes (0x24), and the PSV data is
+  // 40 bytes (0x40).
+  {
+    uint8_t Buffer[] = {
+        0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x90, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
+        0x4C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C,
+        0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x0E, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0x44, 0x58, 0x49, 0x4C, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x50, 0x53, 0x56, 0x30, 0x24, 0x00, 0x00, 0x00,
+        0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x42, 0x4C, 0x45, 0x48, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    };
+    EXPECT_THAT_EXPECTED(
+        DXContainer::create(getMemoryBuffer<144>(Buffer)),
+        FailedWithMessage(
+            "Pipeline state data extends beyond the bounds of the part"));
+  }
+
+  // In this file blob, the file is 116 bytes (0x74). The file structure is
+  // valid except that it specifies 1 16 byte resource binding which would
+  // extend beyond the range of the part and file.
+  {
+    uint8_t Buffer[] = {
+        0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x74, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+        0x48, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C, 0x18, 0x00, 0x00, 0x00,
+        0x60, 0x00, 0x0E, 0x00, 0x06, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C,
+        0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x50, 0x53, 0x56, 0x30, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+    };
+    EXPECT_THAT_EXPECTED(
+        DXContainer::create(getMemoryBuffer<116>(Buffer)),
+        FailedWithMessage(
+            "Resource binding data extends beyond the bounds of the part"));
+  }
+
+  // In this file blob, the file is 116 bytes (0x74). The file structure is
+  // valid except that it specifies 1 16 byte resource binding which would
+  // extend beyond the range of the part and into the `BLEH` part.
+  {
+    uint8_t Buffer[] = {
+        0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x90, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
+        0x4C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C,
+        0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x0E, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0x44, 0x58, 0x49, 0x4C, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x50, 0x53, 0x56, 0x30, 0x24, 0x00, 0x00, 0x00,
+        0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x42, 0x4C, 0x45, 0x48, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    };
+    EXPECT_THAT_EXPECTED(
+        DXContainer::create(getMemoryBuffer<144>(Buffer)),
+        FailedWithMessage(
+            "Resource binding data extends beyond the bounds of the part"));
+  }
+}
+
+// This test verifies that the resource iterator follows the stride even if the
+// stride doesn't match an expected or known value. In this test, the resource
+// data is structured validly, with 32 bytes per resource. This test is based on
+// editing the binary output constructed from this yaml.
+//
+// --- !dxcontainer
+// Header:
+//   Hash:            [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
+//                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
+//   Version:
+//     Major:           1
+//     Minor:           0
+//   PartCount:       2
+// Parts:
+//   - Name:            DXIL
+//     Size:            24
+//     Program:
+//       MajorVersion:    6
+//       MinorVersion:    0
+//       ShaderKind:      14
+//       Size:            6
+//       DXILMajorVersion: 0
+//       DXILMinorVersion: 1
+//       DXILSize:        0
+//   - Name:            PSV0
+//     Size:            100
+//     PSVInfo:
+//       Version:         0
+//       ShaderStage:     5
+//       MinimumWaveLaneCount: 0
+//       MaximumWaveLaneCount: 0
+//       ResourceStride:       16
+//       Resources:
+//         - Type:            1
+//           Space:           2
+//           LowerBound:      3
+//           UpperBound:      4
+//         - Type:            5
+//           Space:           6
+//           LowerBound:      7
+//           UpperBound:      8
+// ...
+TEST(DXCFile, PSVResourceIteratorsStride) {
+  uint8_t Buffer[] = {
+        0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 
+        0x28, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C, 0x18, 0x00, 0x00, 0x00,
+        0x60, 0x00, 0x0E, 0x00, 0x06, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C, 0x00, 0x01, 0x00, 0x00,
+        0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x53, 0x56, 0x30, 0x64, 0x00, 0x00, 0x00,
+        0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+    }; 
+    DXContainer C =
+      llvm::cantFail(DXContainer::create(getMemoryBuffer<180>(Buffer)));
+
+  const auto &PSVInfo = C.getPSVInfo();
+  ASSERT_TRUE(PSVInfo.has_value());
+
+  ASSERT_EQ(PSVInfo->getResourceCount(), 2u);
+
+  auto It = PSVInfo->getResources().begin();
+
+  EXPECT_TRUE(It == PSVInfo->getResources().begin());
+
+  dxbc::PSV::v2::ResourceBindInfo Binding;
+
+  Binding = *It;
+  EXPECT_EQ(Binding.Type, 1u);
+  EXPECT_EQ(Binding.Space, 2u);
+  EXPECT_EQ(Binding.LowerBound, 3u);
+  EXPECT_EQ(Binding.UpperBound, 4u);
+
+  ++It;
+  Binding = *It;
+
+  EXPECT_EQ(Binding.Type, 5u);
+  EXPECT_EQ(Binding.Space, 6u);
+  EXPECT_EQ(Binding.LowerBound, 7u);
+  EXPECT_EQ(Binding.UpperBound, 8u);
+
+  --It;
+  Binding = *It;
+
+  EXPECT_TRUE(It == PSVInfo->getResources().begin());
+
+  EXPECT_EQ(Binding.Type, 1u);
+  EXPECT_EQ(Binding.Space, 2u);
+  EXPECT_EQ(Binding.LowerBound, 3u);
+  EXPECT_EQ(Binding.UpperBound, 4u);
+
+  --It;
+  Binding = *It;
+
+  EXPECT_EQ(Binding.Type, 1u);
+  EXPECT_EQ(Binding.Space, 2u);
+  EXPECT_EQ(Binding.LowerBound, 3u);
+  EXPECT_EQ(Binding.UpperBound, 4u);
+
+  ++It;
+  Binding = *It;
+
+  EXPECT_EQ(Binding.Type, 5u);
+  EXPECT_EQ(Binding.Space, 6u);
+  EXPECT_EQ(Binding.LowerBound, 7u);
+  EXPECT_EQ(Binding.UpperBound, 8u);;
+
+
+  EXPECT_FALSE(It == PSVInfo->getResources().end());
+
+  ++It;
+  Binding = *It;
+
+  EXPECT_TRUE(It == PSVInfo->getResources().end());
+  EXPECT_FALSE(It != PSVInfo->getResources().end());
+
+  EXPECT_EQ(Binding.Type, 0u);
+  EXPECT_EQ(Binding.Flags, 0u);
+}