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