xref: /llvm-project/llvm/unittests/Object/DXContainerTest.cpp (revision 9f87522b121a08b97aff433e8b53dec1acf7c9d0)
1 //===- DXContainerTest.cpp - Tests for DXContainerFile --------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "llvm/Object/DXContainer.h"
10 #include "llvm/ADT/StringRef.h"
11 #include "llvm/BinaryFormat/Magic.h"
12 #include "llvm/ObjectYAML/DXContainerYAML.h"
13 #include "llvm/ObjectYAML/yaml2obj.h"
14 #include "llvm/Support/MemoryBufferRef.h"
15 #include "llvm/Testing/Support/Error.h"
16 #include "gtest/gtest.h"
17 
18 using namespace llvm;
19 using namespace llvm::object;
20 
21 template <std::size_t X> MemoryBufferRef getMemoryBuffer(uint8_t Data[X]) {
22   StringRef Obj(reinterpret_cast<char *>(&Data[0]), X);
23   return MemoryBufferRef(Obj, "");
24 }
25 
26 TEST(DXCFile, IdentifyMagic) {
27   {
28     StringRef Buffer("DXBC");
29     EXPECT_EQ(identify_magic(Buffer), file_magic::dxcontainer_object);
30   }
31   {
32     StringRef Buffer("DXBCBlahBlahBlah");
33     EXPECT_EQ(identify_magic(Buffer), file_magic::dxcontainer_object);
34   }
35 }
36 
37 TEST(DXCFile, ParseHeaderErrors) {
38   uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43};
39   EXPECT_THAT_EXPECTED(
40       DXContainer::create(getMemoryBuffer<4>(Buffer)),
41       FailedWithMessage("Reading structure out of file bounds"));
42 }
43 
44 TEST(DXCFile, EmptyFile) {
45   EXPECT_THAT_EXPECTED(
46       DXContainer::create(MemoryBufferRef(StringRef("", 0), "")),
47       FailedWithMessage("Reading structure out of file bounds"));
48 }
49 
50 TEST(DXCFile, ParseHeader) {
51   uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
52                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53                       0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
54                       0x70, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
55   DXContainer C =
56       llvm::cantFail(DXContainer::create(getMemoryBuffer<32>(Buffer)));
57   EXPECT_TRUE(memcmp(C.getHeader().Magic, "DXBC", 4) == 0);
58   EXPECT_TRUE(memcmp(C.getHeader().FileHash.Digest,
59                      "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0);
60   EXPECT_EQ(C.getHeader().Version.Major, 1u);
61   EXPECT_EQ(C.getHeader().Version.Minor, 0u);
62 }
63 
64 TEST(DXCFile, ParsePartMissingOffsets) {
65   uint8_t Buffer[] = {
66       0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
68       0x00, 0x00, 0x70, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
69   };
70   EXPECT_THAT_EXPECTED(
71       DXContainer::create(getMemoryBuffer<32>(Buffer)),
72       FailedWithMessage("Reading structure out of file bounds"));
73 }
74 
75 TEST(DXCFile, ParsePartInvalidOffsets) {
76   // This test covers a case where the part offset is beyond the buffer size.
77   uint8_t Buffer[] = {
78       0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
80       0x70, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
81   };
82   EXPECT_THAT_EXPECTED(
83       DXContainer::create(getMemoryBuffer<36>(Buffer)),
84       FailedWithMessage("Part offset points beyond boundary of the file"));
85 }
86 
87 TEST(DXCFile, ParsePartTooSmallBuffer) {
88   // This test covers a case where there is insufficent space to read a full
89   // part name, but the offset for the part is inside the buffer.
90   uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
91                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
92                       0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
93                       0x26, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
94                       0x24, 0x00, 0x00, 0x00, 0x46, 0x4B};
95   EXPECT_THAT_EXPECTED(
96       DXContainer::create(getMemoryBuffer<38>(Buffer)),
97       FailedWithMessage("File not large enough to read part name"));
98 }
99 
100 TEST(DXCFile, ParsePartNoSize) {
101   // This test covers a case where the part's header is readable, but the size
102   // the part extends beyond the boundaries of the file.
103   uint8_t Buffer[] = {0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00,
104                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105                       0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x0D, 0x00,
106                       0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
107                       0x46, 0x4B, 0x45, 0x30, 0x00, 0x00};
108   EXPECT_THAT_EXPECTED(
109       DXContainer::create(getMemoryBuffer<42>(Buffer)),
110       FailedWithMessage("Reading part size out of file bounds"));
111 }
112 
113 TEST(DXCFile, ParseOverlappingParts) {
114   // This test covers a case where a part's offset is inside the size range
115   // covered by the previous part.
116   uint8_t Buffer[] = {
117       0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
119       0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
120       0x2C, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x30, 0x08, 0x00, 0x00, 0x00,
121       0x46, 0x4B, 0x45, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122   };
123   EXPECT_THAT_EXPECTED(
124       DXContainer::create(getMemoryBuffer<60>(Buffer)),
125       FailedWithMessage(
126           "Part offset for part 1 begins before the previous part ends"));
127 }
128 
129 TEST(DXCFile, ParseEmptyParts) {
130   uint8_t Buffer[] = {
131       0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
133       0x70, 0x0D, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00,
134       0x44, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
135       0x5C, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00,
136       0x46, 0x4B, 0x45, 0x30, 0x00, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x31,
137       0x00, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x32, 0x00, 0x00, 0x00, 0x00,
138       0x46, 0x4B, 0x45, 0x33, 0x00, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x34,
139       0x00, 0x00, 0x00, 0x00, 0x46, 0x4B, 0x45, 0x35, 0x00, 0x00, 0x00, 0x00,
140       0x46, 0x4B, 0x45, 0x36, 0x00, 0x00, 0x00, 0x00,
141   };
142   DXContainer C =
143       llvm::cantFail(DXContainer::create(getMemoryBuffer<116>(Buffer)));
144   EXPECT_EQ(C.getHeader().PartCount, 7u);
145 
146   // All the part sizes are 0, which makes a nice test of the range based for
147   int ElementsVisited = 0;
148   for (auto Part : C) {
149     EXPECT_EQ(Part.Part.Size, 0u);
150     EXPECT_EQ(Part.Data.size(), 0u);
151     ++ElementsVisited;
152   }
153   EXPECT_EQ(ElementsVisited, 7);
154 
155   {
156     // These are all intended to be fake part names so that the parser doesn't
157     // try to parse the part data.
158     auto It = C.begin();
159     EXPECT_TRUE(memcmp(It->Part.Name, "FKE0", 4) == 0);
160     ++It;
161     EXPECT_TRUE(memcmp(It->Part.Name, "FKE1", 4) == 0);
162     ++It;
163     EXPECT_TRUE(memcmp(It->Part.Name, "FKE2", 4) == 0);
164     ++It;
165     EXPECT_TRUE(memcmp(It->Part.Name, "FKE3", 4) == 0);
166     ++It;
167     EXPECT_TRUE(memcmp(It->Part.Name, "FKE4", 4) == 0);
168     ++It;
169     EXPECT_TRUE(memcmp(It->Part.Name, "FKE5", 4) == 0);
170     ++It;
171     EXPECT_TRUE(memcmp(It->Part.Name, "FKE6", 4) == 0);
172     ++It; // Don't increment past the end
173     EXPECT_TRUE(memcmp(It->Part.Name, "FKE6", 4) == 0);
174   }
175 }
176 
177 static Expected<DXContainer>
178 generateDXContainer(StringRef Yaml, SmallVectorImpl<char> &BinaryData) {
179   DXContainerYAML::Object Obj;
180   SMDiagnostic GenerateDiag;
181   yaml::Input YIn(
182       Yaml, /*Ctxt=*/nullptr,
183       [](const SMDiagnostic &Diag, void *DiagContext) {
184         *static_cast<SMDiagnostic *>(DiagContext) = Diag;
185       },
186       &GenerateDiag);
187 
188   YIn >> Obj;
189   if (YIn.error())
190     return createStringError(YIn.error(), GenerateDiag.getMessage());
191 
192   raw_svector_ostream OS(BinaryData);
193   std::string ErrorMsg;
194   if (!yaml::yaml2dxcontainer(
195           Obj, OS, [&ErrorMsg](const Twine &Msg) { ErrorMsg = Msg.str(); }))
196     return createStringError(YIn.error(), ErrorMsg);
197 
198   MemoryBufferRef BinaryDataRef = MemoryBufferRef(OS.str(), "");
199 
200   return DXContainer::create(BinaryDataRef);
201 }
202 
203 TEST(DXCFile, PSVResourceIterators) {
204   const char *Yaml = R"(
205 --- !dxcontainer
206 Header:
207   Hash:            [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
208                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
209   Version:
210     Major:           1
211     Minor:           0
212   PartCount:       2
213 Parts:
214   - Name:            PSV0
215     Size:            144
216     PSVInfo:
217       Version:         0
218       ShaderStage:     14
219       PayloadSizeInBytes: 4092
220       MinimumWaveLaneCount: 0
221       MaximumWaveLaneCount: 4294967295
222       ResourceStride:  16
223       Resources:
224         - Type:            1
225           Space:           1
226           LowerBound:      1
227           UpperBound:      1
228         - Type:            2
229           Space:           2
230           LowerBound:      2
231           UpperBound:      2
232         - Type:            3
233           Space:           3
234           LowerBound:      3
235           UpperBound:      3
236   - Name:            DXIL
237     Size:            24
238     Program:
239       MajorVersion:    6
240       MinorVersion:    0
241       ShaderKind:      14
242       Size:            6
243       DXILMajorVersion: 0
244       DXILMinorVersion: 1
245       DXILSize:        0
246 ...
247 )";
248 
249   SmallVector<char, 256> BinaryData;
250   auto C = generateDXContainer(Yaml, BinaryData);
251 
252   ASSERT_THAT_EXPECTED(C, Succeeded());
253 
254   const auto &PSVInfo = C->getPSVInfo();
255   ASSERT_TRUE(PSVInfo.has_value());
256 
257   EXPECT_EQ(PSVInfo->getResourceCount(), 3u);
258 
259   auto It = PSVInfo->getResources().begin();
260 
261   EXPECT_TRUE(It == PSVInfo->getResources().begin());
262 
263   dxbc::PSV::v2::ResourceBindInfo Binding;
264 
265   Binding = *It;
266   EXPECT_EQ(Binding.Type, 1u);
267   EXPECT_EQ(Binding.Flags, 0u);
268 
269   ++It;
270   Binding = *It;
271 
272   EXPECT_EQ(Binding.Type, 2u);
273   EXPECT_EQ(Binding.Flags, 0u);
274 
275   --It;
276   Binding = *It;
277 
278   EXPECT_TRUE(It == PSVInfo->getResources().begin());
279 
280   EXPECT_EQ(Binding.Type, 1u);
281   EXPECT_EQ(Binding.Flags, 0u);
282 
283   --It;
284   Binding = *It;
285 
286   EXPECT_EQ(Binding.Type, 1u);
287   EXPECT_EQ(Binding.Flags, 0u);
288 
289   ++It;
290   Binding = *It;
291 
292   EXPECT_EQ(Binding.Type, 2u);
293   EXPECT_EQ(Binding.Flags, 0u);
294 
295   ++It;
296   Binding = *It;
297 
298   EXPECT_EQ(Binding.Type, 3u);
299   EXPECT_EQ(Binding.Flags, 0u);
300 
301   EXPECT_FALSE(It == PSVInfo->getResources().end());
302 
303   ++It;
304   Binding = *It;
305 
306   EXPECT_TRUE(It == PSVInfo->getResources().end());
307   EXPECT_FALSE(It != PSVInfo->getResources().end());
308 
309   EXPECT_EQ(Binding.Type, 0u);
310   EXPECT_EQ(Binding.Flags, 0u);
311 
312   {
313     auto Old = It++;
314     Binding = *Old;
315 
316     EXPECT_TRUE(Old == PSVInfo->getResources().end());
317     EXPECT_FALSE(Old != PSVInfo->getResources().end());
318 
319     EXPECT_EQ(Binding.Type, 0u);
320     EXPECT_EQ(Binding.Flags, 0u);
321   }
322 
323   Binding = *It;
324 
325   EXPECT_TRUE(It == PSVInfo->getResources().end());
326 
327   EXPECT_EQ(Binding.Type, 0u);
328   EXPECT_EQ(Binding.Flags, 0u);
329 
330   {
331     auto Old = It--;
332     Binding = *Old;
333     EXPECT_TRUE(Old == PSVInfo->getResources().end());
334 
335     EXPECT_EQ(Binding.Type, 0u);
336     EXPECT_EQ(Binding.Flags, 0u);
337   }
338 
339   Binding = *It;
340 
341   EXPECT_EQ(Binding.Type, 3u);
342   EXPECT_EQ(Binding.Flags, 0u);
343 }
344 
345 // The malicious file bits in these tests are mutations of the binary produced
346 // by the following YAML:
347 //
348 // --- !dxcontainer
349 // Header:
350 //   Hash:            [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
351 //                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
352 //   Version:
353 //     Major:           1
354 //     Minor:           0
355 //   PartCount:       3
356 // Parts:
357 //   - Name:            DXIL
358 //     Size:            24
359 //     Program:
360 //       MajorVersion:    6
361 //       MinorVersion:    0
362 //       ShaderKind:      14
363 //       Size:            6
364 //       DXILMajorVersion: 0
365 //       DXILMinorVersion: 1
366 //       DXILSize:        0
367 //   - Name:            PSV0
368 //     Size:            36
369 //     PSVInfo:
370 //       Version:         0
371 //       ShaderStage:     5
372 //       MinimumWaveLaneCount: 0
373 //       MaximumWaveLaneCount: 0
374 //       ResourceStride:       16
375 //       Resources: []
376 //   - Name: BLEH
377 //     Size: 16
378 // ...
379 
380 TEST(DXCFile, MaliciousFiles) {
381 
382   // In this file blob, the file size is specified as 96 bytes (0x60), and the
383   // PSV0 data is specified as 24 bytes (0x18) which extends beyond the size of
384   // the file.
385   {
386     uint8_t Buffer[] = {
387         0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
388         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
389         0x60, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
390         0x48, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C, 0x18, 0x00, 0x00, 0x00,
391         0x60, 0x00, 0x0E, 0x00, 0x06, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C,
392         0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
393         0x50, 0x53, 0x56, 0x30, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
394         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
395     };
396     EXPECT_THAT_EXPECTED(
397         DXContainer::create(getMemoryBuffer<96>(Buffer)),
398         FailedWithMessage(
399             "Pipeline state data extends beyond the bounds of the part"));
400   }
401 
402   // PSV extends beyond part, but in file range. In this blob the file size is
403   // 144 bytes (0x90), and the PSV part is 36 bytes (0x24), and the PSV data is
404   // 40 bytes (0x40).
405   {
406     uint8_t Buffer[] = {
407         0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
408         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
409         0x90, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
410         0x4C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C,
411         0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x0E, 0x00, 0x06, 0x00, 0x00, 0x00,
412         0x44, 0x58, 0x49, 0x4C, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
413         0x00, 0x00, 0x00, 0x00, 0x50, 0x53, 0x56, 0x30, 0x24, 0x00, 0x00, 0x00,
414         0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
415         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
416         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
417         0x42, 0x4C, 0x45, 0x48, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
418         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
419     };
420     EXPECT_THAT_EXPECTED(
421         DXContainer::create(getMemoryBuffer<144>(Buffer)),
422         FailedWithMessage(
423             "Pipeline state data extends beyond the bounds of the part"));
424   }
425 
426   // In this file blob, the file is 116 bytes (0x74). The file structure is
427   // valid except that it specifies 1 16 byte resource binding which would
428   // extend beyond the range of the part and file.
429   {
430     uint8_t Buffer[] = {
431         0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
432         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
433         0x74, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
434         0x48, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C, 0x18, 0x00, 0x00, 0x00,
435         0x60, 0x00, 0x0E, 0x00, 0x06, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C,
436         0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
437         0x50, 0x53, 0x56, 0x30, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
438         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
439         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
440         0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
441     };
442     EXPECT_THAT_EXPECTED(
443         DXContainer::create(getMemoryBuffer<116>(Buffer)),
444         FailedWithMessage(
445             "Resource binding data extends beyond the bounds of the part"));
446   }
447 
448   // In this file blob, the file is 116 bytes (0x74). The file structure is
449   // valid except that it specifies 1 16 byte resource binding which would
450   // extend beyond the range of the part and into the `BLEH` part.
451   {
452     uint8_t Buffer[] = {
453         0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
454         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
455         0x90, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00,
456         0x4C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C,
457         0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x0E, 0x00, 0x06, 0x00, 0x00, 0x00,
458         0x44, 0x58, 0x49, 0x4C, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
459         0x00, 0x00, 0x00, 0x00, 0x50, 0x53, 0x56, 0x30, 0x24, 0x00, 0x00, 0x00,
460         0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
461         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
462         0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
463         0x42, 0x4C, 0x45, 0x48, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
464         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
465     };
466     EXPECT_THAT_EXPECTED(
467         DXContainer::create(getMemoryBuffer<144>(Buffer)),
468         FailedWithMessage(
469             "Resource binding data extends beyond the bounds of the part"));
470   }
471 }
472 
473 // This test verifies that the resource iterator follows the stride even if the
474 // stride doesn't match an expected or known value. In this test, the resource
475 // data is structured validly, with 32 bytes per resource. This test is based on
476 // editing the binary output constructed from this yaml.
477 //
478 // --- !dxcontainer
479 // Header:
480 //   Hash:            [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
481 //                      0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
482 //   Version:
483 //     Major:           1
484 //     Minor:           0
485 //   PartCount:       2
486 // Parts:
487 //   - Name:            DXIL
488 //     Size:            24
489 //     Program:
490 //       MajorVersion:    6
491 //       MinorVersion:    0
492 //       ShaderKind:      14
493 //       Size:            6
494 //       DXILMajorVersion: 0
495 //       DXILMinorVersion: 1
496 //       DXILSize:        0
497 //   - Name:            PSV0
498 //     Size:            100
499 //     PSVInfo:
500 //       Version:         0
501 //       ShaderStage:     5
502 //       MinimumWaveLaneCount: 0
503 //       MaximumWaveLaneCount: 0
504 //       ResourceStride:       16
505 //       Resources:
506 //         - Type:            1
507 //           Space:           2
508 //           LowerBound:      3
509 //           UpperBound:      4
510 //         - Type:            5
511 //           Space:           6
512 //           LowerBound:      7
513 //           UpperBound:      8
514 // ...
515 TEST(DXCFile, PSVResourceIteratorsStride) {
516   uint8_t Buffer[] = {
517         0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
518         0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
519         0x28, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C, 0x18, 0x00, 0x00, 0x00,
520         0x60, 0x00, 0x0E, 0x00, 0x06, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4C, 0x00, 0x01, 0x00, 0x00,
521         0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x53, 0x56, 0x30, 0x64, 0x00, 0x00, 0x00,
522         0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
523         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
524         0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
525         0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
526         0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
527         0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
528         0x00, 0x00, 0x00, 0x00,
529     };
530     DXContainer C =
531       llvm::cantFail(DXContainer::create(getMemoryBuffer<180>(Buffer)));
532 
533   const auto &PSVInfo = C.getPSVInfo();
534   ASSERT_TRUE(PSVInfo.has_value());
535 
536   ASSERT_EQ(PSVInfo->getResourceCount(), 2u);
537 
538   auto It = PSVInfo->getResources().begin();
539 
540   EXPECT_TRUE(It == PSVInfo->getResources().begin());
541 
542   dxbc::PSV::v2::ResourceBindInfo Binding;
543 
544   Binding = *It;
545   EXPECT_EQ(Binding.Type, 1u);
546   EXPECT_EQ(Binding.Space, 2u);
547   EXPECT_EQ(Binding.LowerBound, 3u);
548   EXPECT_EQ(Binding.UpperBound, 4u);
549 
550   ++It;
551   Binding = *It;
552 
553   EXPECT_EQ(Binding.Type, 5u);
554   EXPECT_EQ(Binding.Space, 6u);
555   EXPECT_EQ(Binding.LowerBound, 7u);
556   EXPECT_EQ(Binding.UpperBound, 8u);
557 
558   --It;
559   Binding = *It;
560 
561   EXPECT_TRUE(It == PSVInfo->getResources().begin());
562 
563   EXPECT_EQ(Binding.Type, 1u);
564   EXPECT_EQ(Binding.Space, 2u);
565   EXPECT_EQ(Binding.LowerBound, 3u);
566   EXPECT_EQ(Binding.UpperBound, 4u);
567 
568   --It;
569   Binding = *It;
570 
571   EXPECT_EQ(Binding.Type, 1u);
572   EXPECT_EQ(Binding.Space, 2u);
573   EXPECT_EQ(Binding.LowerBound, 3u);
574   EXPECT_EQ(Binding.UpperBound, 4u);
575 
576   ++It;
577   Binding = *It;
578 
579   EXPECT_EQ(Binding.Type, 5u);
580   EXPECT_EQ(Binding.Space, 6u);
581   EXPECT_EQ(Binding.LowerBound, 7u);
582   EXPECT_EQ(Binding.UpperBound, 8u);;
583 
584 
585   EXPECT_FALSE(It == PSVInfo->getResources().end());
586 
587   ++It;
588   Binding = *It;
589 
590   EXPECT_TRUE(It == PSVInfo->getResources().end());
591   EXPECT_FALSE(It != PSVInfo->getResources().end());
592 
593   EXPECT_EQ(Binding.Type, 0u);
594   EXPECT_EQ(Binding.Flags, 0u);
595 }
596 
597 // This test binary is created using mutations of the yaml in the SigElements
598 // test found under test/ObjectYAML/DXContainer/SigElements.yaml.
599 
600 TEST(DXCFile, MisalignedStringTable) {
601   uint8_t Buffer[] = {
602       0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
603       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
604       0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
605       0x48, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4c, 0x18, 0x00, 0x00, 0x00,
606       0x60, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4c,
607       0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
608       0x50, 0x53, 0x56, 0x30, 0x68, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
609       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
610       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
611       0x05, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x08, 0x10, 0x20, 0x40,
612       0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
613       0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
614       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
615       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
616   EXPECT_THAT_EXPECTED(DXContainer::create(getMemoryBuffer<168>(Buffer)),
617                        FailedWithMessage("String table misaligned"));
618 }
619 
620 // This test binary is created using mutations of the yaml in the SigElements
621 // test found under test/ObjectYAML/DXContainer/SigElements.yaml.
622 TEST(DXCFile, SigElementsExtendBeyondPart) {
623   uint8_t Buffer[] = {
624       0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
625       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
626       0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
627       0x48, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4c, 0x18, 0x00, 0x00, 0x00,
628       0x60, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4c,
629       0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
630       0x50, 0x53, 0x56, 0x30, 0x54, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
631       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
632       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
633       0x05, 0x80, 0x00, 0x00, 0x02, 0x00, 0x00, 0x40, 0x08, 0x10, 0x20, 0x40,
634       0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x49, 0x4e, 0x00,
635       0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
636       0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
637       0x02, 0x00, 0x42, 0x00, 0x02, 0x00, 0x03, 0x00};
638   EXPECT_THAT_EXPECTED(
639       DXContainer::create(getMemoryBuffer<164>(Buffer)),
640       FailedWithMessage(
641           "Signature elements extend beyond the size of the part"));
642 }
643 
644 TEST(DXCFile, MalformedSignature) {
645   /*
646   The tests here exercise the DXContainer Signature section parser. These tests
647   are based on modifying the binary described by the following yaml:
648 
649   --- !dxcontainer
650   Header:
651     Hash:            [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
652                       0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
653     Version:
654       Major:           1
655       Minor:           0
656     FileSize:        128
657     PartCount:       1
658     PartOffsets:     [ 64 ]
659   Parts:
660     - Name:            ISG1
661       Size:            52
662       Signature:
663         Parameters:
664           - Stream:          0
665             Name:            AAA
666             Index:           0
667             SystemValue:     Undefined
668             CompType:        Float32
669             Register:        0
670             Mask:            7
671             ExclusiveMask:   2
672             MinPrecision:    Default
673   ...
674 
675   The unmodified hex sequence is:
676 
677   uint8_t Buffer[] = {
678     0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
679   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80,
680   0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
681   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
682   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
683   0x49, 0x53, 0x47, 0x31, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08,
684   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00,
685   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
686   0x00, 0x07, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x00,
687   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
688   };
689 
690   */
691 
692   {
693 
694     // This binary says the signature has 10 parameters, but the part data is
695     // only big enough for 1.
696     uint8_t Buffer[] = {
697         0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
698         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
699         0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
700         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
701         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
702         0x00, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, 0x31, 0x34, 0x00, 0x00, 0x00,
703         0x0A, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
704         0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
705         0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00,
706         0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00,
707         0x00, 0x00, 0x00, 0x00};
708     EXPECT_THAT_EXPECTED(
709         DXContainer::create(getMemoryBuffer<164>(Buffer)),
710         FailedWithMessage(
711             "Signature parameters extend beyond the part boundary"));
712   }
713 
714   {
715 
716     // This binary only has one parameter, but the start offset is beyond the
717     // size of the part.
718     uint8_t Buffer[] = {
719         0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
720         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
721         0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
722         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
723         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
724         0x00, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, 0x31, 0x34, 0x00, 0x00, 0x00,
725         0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
726         0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
727         0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00,
728         0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00,
729         0x00, 0x00, 0x00, 0x00};
730     EXPECT_THAT_EXPECTED(
731         DXContainer::create(getMemoryBuffer<164>(Buffer)),
732         FailedWithMessage(
733             "Signature parameters extend beyond the part boundary"));
734   }
735 
736   {
737 
738     // This parameter has a name offset of 3, which is before the start of the
739     // string table.
740     uint8_t Buffer[] = {
741         0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
742         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
743         0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
744         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
745         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
746         0x00, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, 0x31, 0x34, 0x00, 0x00, 0x00,
747         0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
748         0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
749         0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00,
750         0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00,
751         0x00, 0x00, 0x00, 0x00};
752     EXPECT_THAT_EXPECTED(
753         DXContainer::create(getMemoryBuffer<164>(Buffer)),
754         FailedWithMessage("Invalid parameter name offset: name starts before "
755                           "the first name offset"));
756   }
757 
758   {
759     // This parameter has a name offset of 255, which is after the end of the
760     // part.
761     uint8_t Buffer[] = {
762         0x44, 0x58, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
763         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
764         0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
765         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
766         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
767         0x00, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, 0x31, 0x34, 0x00, 0x00, 0x00,
768         0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
769         0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
770         0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00,
771         0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00,
772         0x00, 0x00, 0x00, 0x00};
773     EXPECT_THAT_EXPECTED(
774         DXContainer::create(getMemoryBuffer<164>(Buffer)),
775         FailedWithMessage("Invalid parameter name offset: name starts after "
776                           "the end of the part data"));
777   }
778 }
779