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
18using namespace llvm;
19using namespace llvm::object;
20
21template <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
26TEST(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
37TEST(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
44TEST(DXCFile, EmptyFile) {
45 EXPECT_THAT_EXPECTED(
46 DXContainer::create(MemoryBufferRef(StringRef("", 0), "")),
47 FailedWithMessage("Reading structure out of file bounds"));
48}
49
50TEST(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(ValOrErr: DXContainer::create(Object: getMemoryBuffer<32>(Data: 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
64TEST(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
75TEST(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
87TEST(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
100TEST(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
113TEST(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
129TEST(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(ValOrErr: DXContainer::create(Object: getMemoryBuffer<116>(Data: 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
177static Expected<DXContainer>
178generateDXContainer(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(EC: YIn.error(), S: GenerateDiag.getMessage());
191
192 raw_svector_ostream OS(BinaryData);
193 std::string ErrorMsg;
194 if (!yaml::yaml2dxcontainer(
195 Doc&: Obj, Out&: OS, EH: [&ErrorMsg](const Twine &Msg) { ErrorMsg = Msg.str(); }))
196 return createStringError(EC: YIn.error(), S: ErrorMsg);
197
198 MemoryBufferRef BinaryDataRef = MemoryBufferRef(OS.str(), "");
199
200 return DXContainer::create(Object: BinaryDataRef);
201}
202
203TEST(DXCFile, PSVResourceIterators) {
204 const char *Yaml = R"(
205--- !dxcontainer
206Header:
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
213Parts:
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
380TEST(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// ...
515TEST(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(ValOrErr: DXContainer::create(Object: getMemoryBuffer<180>(Data: 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
600TEST(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.
622TEST(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
644TEST(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

source code of llvm/unittests/Object/DXContainerTest.cpp