Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Preview Hashing sentinel value to dxil validator #7053

Merged
merged 15 commits into from
Jan 25, 2025
Merged
15 changes: 13 additions & 2 deletions include/dxc/DXIL/DxilShaderModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,23 @@ class ShaderModel {
public:
using Kind = DXIL::ShaderKind;

// Major/Minor version of highest shader model
// Major/Minor version of highest released shader model
tex3d marked this conversation as resolved.
Show resolved Hide resolved
// clang-format off
// Python lines need to be not formatted.
/* <py::lines('VALRULE-TEXT')>hctdb_instrhelp.get_highest_shader_model()</py>*/
/* <py::lines('VALRULE-TEXT')>hctdb_instrhelp.get_highest_recognized_shader_model()</py>*/
// clang-format on
// VALRULE-TEXT:BEGIN
static const unsigned kHighestMajor = 6;
static const unsigned kHighestMinor = 9;
// VALRULE-TEXT:END

/* <py::lines('VALRULE-TEXT')>hctdb_instrhelp.get_highest_released_shader_model()</py>*/
// clang-format on
// VALRULE-TEXT:BEGIN
static const unsigned kHighestReleasedMajor = 6;
static const unsigned kHighestReleasedMinor = 8;
// VALRULE-TEXT:END

static const unsigned kOfflineMinor = 0xF;

bool IsPS() const { return m_Kind == Kind::Pixel; }
Expand Down Expand Up @@ -86,6 +94,9 @@ class ShaderModel {
static const ShaderModel *Get(Kind Kind, unsigned Major, unsigned Minor);
static const ShaderModel *GetByName(llvm::StringRef Name);
static const char *GetKindName(Kind kind);
static bool IsPreReleaseShaderModel(int Major, int Minor);
static const ShaderModel *GetPreReleaseShaderModel(llvm::StringRef name);
tex3d marked this conversation as resolved.
Show resolved Hide resolved
static const Kind GetKindFromName(llvm::StringRef Name);
static DXIL::ShaderKind KindFromFullName(llvm::StringRef Name);
static const llvm::StringRef FullNameFromKind(DXIL::ShaderKind sk);
static const char *GetNodeLaunchTypeName(DXIL::NodeLaunchType launchTy);
Expand Down
19 changes: 13 additions & 6 deletions lib/DXIL/DxilMetadataHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,13 +284,20 @@ void DxilMDHelper::LoadDxilShaderModel(const ShaderModel *&pSM) {
"_" + std::to_string(Major) + "_" +
(Minor == ShaderModel::kOfflineMinor ? "x" : std::to_string(Minor));
pSM = ShaderModel::GetByName(ShaderModelName.c_str());
if (!pSM->IsValidForDxil()) {
char ErrorMsgTxt[40];
StringCchPrintfA(ErrorMsgTxt, _countof(ErrorMsgTxt),
"Unknown shader model '%s'", ShaderModelName.c_str());
string ErrorMsg(ErrorMsgTxt);
throw hlsl::Exception(DXC_E_INCORRECT_DXIL_METADATA, ErrorMsg);
// check to see if the provided shader model is the pre-release version
if (ShaderModel::IsPreReleaseShaderModel(Major, Minor)) {
tex3d marked this conversation as resolved.
Show resolved Hide resolved
ShaderModel::Kind kind = ShaderModel::GetKindFromName(ShaderModelName);
pSM = ShaderModel::Get(kind, Major, Minor);
} else {
if (!pSM->IsValidForDxil()) {
char ErrorMsgTxt[40];
StringCchPrintfA(ErrorMsgTxt, _countof(ErrorMsgTxt),
"Unknown shader model '%s'", ShaderModelName.c_str());
string ErrorMsg(ErrorMsgTxt);
throw hlsl::Exception(DXC_E_INCORRECT_DXIL_METADATA, ErrorMsg);
}
}

SetShaderModel(pSM);
}

Expand Down
29 changes: 25 additions & 4 deletions lib/DXIL/DxilShaderModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,23 @@ const ShaderModel *ShaderModel::Get(Kind Kind, unsigned Major, unsigned Minor) {
// VALRULE-TEXT:END
}

const ShaderModel *ShaderModel::GetByName(llvm::StringRef Name) {
// [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor]
bool ShaderModel::IsPreReleaseShaderModel(int major, int minor) {
if (DXIL::CompareVersions(major, minor, kHighestReleasedMajor,
kHighestReleasedMinor) <= 0) {
return false;
}
bob80905 marked this conversation as resolved.
Show resolved Hide resolved

// now compare against highest recognized
if (DXIL::CompareVersions(major, minor, kHighestMajor, kHighestMinor) <= 0) {
return true;
}
bob80905 marked this conversation as resolved.
Show resolved Hide resolved
return false;
}

const ShaderModel::Kind ShaderModel::GetKindFromName(llvm::StringRef Name) {
Kind kind;
if (Name.empty()) {
return GetInvalid();
return Kind::Invalid;
}

switch (Name[0]) {
Expand Down Expand Up @@ -229,8 +241,17 @@ const ShaderModel *ShaderModel::GetByName(llvm::StringRef Name) {
kind = Kind::Amplification;
break;
default:
return GetInvalid();
return Kind::Invalid;
}
return kind;
}

const ShaderModel *ShaderModel::GetByName(llvm::StringRef Name) {
// [ps|vs|gs|hs|ds|cs|ms|as]_[major]_[minor]
Kind kind = GetKindFromName(Name);
if (kind == Kind::Invalid)
return GetInvalid();

unsigned Idx = 3;
if (kind != Kind::Library) {
if (Name[1] != 's' || Name[2] != '_')
Expand Down
39 changes: 39 additions & 0 deletions tools/clang/test/HLSLFileCheck/validation/prerelase-hash.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
; RUN: %dxilver 1.9 | %dxv %s | FileCheck %s
tex3d marked this conversation as resolved.
Show resolved Hide resolved

; This tests the validator signs the PREVIEW_BYPASS hash, 02020202020202020202020202020202,
; upon seeing that the dxil container contains a shader model that is higher than the latest
; released shader model.

target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64"
target triple = "dxil-ms-dx"

define void @node01() {
ret void
}

!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!1}
!dx.shaderModel = !{!2}
!dx.typeAnnotations = !{!3}
!dx.entryPoints = !{!7, !8}

!0 = !{!"dxc(private) 1.7.0.4390 (dxil_validation_on_sv_value_node_launch, 6a52940e2258)"}
!1 = !{i32 1, i32 9}
!2 = !{!"lib", i32 6, i32 10}
!3 = !{i32 1, void ()* @node01, !4}
!4 = !{!5}
!5 = !{i32 0, !6, !6}
!6 = !{}
!7 = !{null, !"", null, null, null}
!8 = !{void ()* @node01, !"node01", null, null, !9}
!9 = !{i32 8, i32 15, i32 13, i32 1, i32 23, !10, i32 15, !11, i32 16, i32 -1, i32 22, !12, i32 20, !13, i32 4, !17, i32 5, !18}
!10 = !{i32 16, i32 16, i32 0}
!11 = !{!"node01", i32 0}
!12 = !{i32 32, i32 1, i32 1}
!13 = !{!14}
!14 = !{i32 1, i32 97, i32 2, !15}
!15 = !{i32 0, i32 12, i32 1, !16}
!16 = !{i32 0, i32 5, i32 1}
!17 = !{i32 1, i32 1, i32 1}
!18 = !{i32 0}
31 changes: 28 additions & 3 deletions tools/clang/tools/dxcvalidator/dxcvalidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "dxc/dxcapi.h"
#include "dxcvalidator.h"

#include "dxc/DXIL/DxilShaderModel.h"
#include "dxc/DxilRootSignature/DxilRootSignature.h"
#include "dxc/Support/FileIOHelper.h"
#include "dxc/Support/Global.h"
Expand All @@ -32,7 +33,13 @@
using namespace llvm;
using namespace hlsl;

static void HashAndUpdate(DxilContainerHeader *Container) {
static void HashAndUpdate(DxilContainerHeader *Container, bool isPreRelease) {
if (isPreRelease) {
// If preview bypass is enabled, use the preview hash.
UINT previewHash[4] = {0x02020202, 0x02020202, 0x02020202, 0x02020202};
tex3d marked this conversation as resolved.
Show resolved Hide resolved
memcpy(Container->Hash.Digest, previewHash, sizeof(previewHash));
return;
}
// Compute hash and update stored hash.
// Hash the container from this offset to the end.
static const uint32_t DXBCHashStartOffset =
Expand All @@ -45,8 +52,25 @@ static void HashAndUpdate(DxilContainerHeader *Container) {

static void HashAndUpdateOrCopy(uint32_t Flags, IDxcBlob *Shader,
IDxcBlob **Hashed) {
bool isPreRelease = false;
const DxilContainerHeader *DxilContainer =
IsDxilContainerLike(Shader->GetBufferPointer(), Shader->GetBufferSize());
if (!DxilContainer)
return;

const DxilProgramHeader *ProgramHeader =
GetDxilProgramHeader(DxilContainer, DFCC_DXIL);

if (ProgramHeader) {
tex3d marked this conversation as resolved.
Show resolved Hide resolved
int PV = ProgramHeader->ProgramVersion;
int major = (PV >> 4) & 0xF; // Extract the major version (next 4 bits)
int minor = PV & 0xF; // Extract the minor version (lowest 4 bits)
isPreRelease = ShaderModel::IsPreReleaseShaderModel(major, minor);
tex3d marked this conversation as resolved.
Show resolved Hide resolved
}

if (Flags & DxcValidatorFlags_InPlaceEdit) {
HashAndUpdate((DxilContainerHeader *)Shader->GetBufferPointer());
HashAndUpdate((DxilContainerHeader *)Shader->GetBufferPointer(),
isPreRelease);
*Hashed = Shader;
Shader->AddRef();
} else {
Expand All @@ -55,7 +79,8 @@ static void HashAndUpdateOrCopy(uint32_t Flags, IDxcBlob *Shader,
unsigned long CB;
IFT(HashedBlobStream->Write(Shader->GetBufferPointer(),
Shader->GetBufferSize(), &CB));
HashAndUpdate((DxilContainerHeader *)HashedBlobStream->GetPtr());
HashAndUpdate((DxilContainerHeader *)HashedBlobStream->GetPtr(),
isPreRelease);
IFT(HashedBlobStream.QueryInterface(Hashed));
}
}
Expand Down
123 changes: 111 additions & 12 deletions tools/clang/unittests/HLSL/ValidationTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "dxc/Support/FileIOHelper.h"
#include "dxc/Support/Global.h"

#include "dxc/DXIL/DxilShaderModel.h"
#include "dxc/Test/DxcTestUtils.h"
#include "dxc/Test/HlslTestUtils.h"

Expand Down Expand Up @@ -300,6 +301,7 @@ class ValidationTest : public ::testing::Test {

TEST_METHOD(ValidateWithHash)
TEST_METHOD(ValidateVersionNotAllowed)
TEST_METHOD(ValidatePreviewBypassHash)
TEST_METHOD(CreateHandleNotAllowedSM66)

TEST_METHOD(AtomicsConsts)
Expand Down Expand Up @@ -537,18 +539,10 @@ class ValidationTest : public ::testing::Test {
pLookFors, pReplacements, pErrorMsgs, bRegex);
}

bool RewriteAssemblyToText(IDxcBlobEncoding *pSource, LPCSTR pShaderModel,
LPCWSTR *pArguments, UINT32 argCount,
const DxcDefine *pDefines, UINT32 defineCount,
llvm::ArrayRef<LPCSTR> pLookFors,
llvm::ArrayRef<LPCSTR> pReplacements,
IDxcBlob **pBlob, bool bRegex = false) {
CComPtr<IDxcBlob> pProgram;
std::string disassembly;
if (!CompileSource(pSource, pShaderModel, pArguments, argCount, pDefines,
defineCount, &pProgram))
return false;
DisassembleProgram(pProgram, &disassembly);
void PerformReplacementOnDisassembly(std::string disassembly,
llvm::ArrayRef<LPCSTR> pLookFors,
llvm::ArrayRef<LPCSTR> pReplacements,
IDxcBlob **pBlob, bool bRegex = false) {
for (unsigned i = 0; i < pLookFors.size(); ++i) {
LPCSTR pLookFor = pLookFors[i];
bool bOptional = false;
Expand Down Expand Up @@ -605,6 +599,22 @@ class ValidationTest : public ::testing::Test {
}
}
Utf8ToBlob(m_dllSupport, disassembly.c_str(), pBlob);
}

bool RewriteAssemblyToText(IDxcBlobEncoding *pSource, LPCSTR pShaderModel,
LPCWSTR *pArguments, UINT32 argCount,
const DxcDefine *pDefines, UINT32 defineCount,
llvm::ArrayRef<LPCSTR> pLookFors,
llvm::ArrayRef<LPCSTR> pReplacements,
IDxcBlob **pBlob, bool bRegex = false) {
CComPtr<IDxcBlob> pProgram;
std::string disassembly;
if (!CompileSource(pSource, pShaderModel, pArguments, argCount, pDefines,
defineCount, &pProgram))
return false;
DisassembleProgram(pProgram, &disassembly);
PerformReplacementOnDisassembly(disassembly, pLookFors, pReplacements,
pBlob, bRegex);
return true;
}

Expand Down Expand Up @@ -4149,6 +4159,95 @@ TEST_F(ValidationTest, ValidateWithHash) {
VERIFY_ARE_EQUAL(memcmp(Result, pHeader->Hash.Digest, sizeof(Result)), 0);
}

// verify that containers that are not valid DXIL do not
// get assigned a hash.
TEST_F(ValidationTest, ValidatePreviewBypassHash) {
if (m_ver.SkipDxilVersion(1, 8))
tex3d marked this conversation as resolved.
Show resolved Hide resolved
return;
CComPtr<IDxcBlob> pProgram;

LPCSTR pSource =
R"(float4 main(float a:A, float b:B) : SV_Target { return 1; })";

CComPtr<IDxcBlobEncoding> pSourceBlob;
Utf8ToBlob(m_dllSupport, pSource, &pSourceBlob);
LPCSTR pShaderModel = ShaderModel::Get(ShaderModel::Kind::Pixel,
ShaderModel::kHighestReleasedMajor,
ShaderModel::kHighestReleasedMinor)
->GetName();
bool result = CompileSource(pSourceBlob, pShaderModel, nullptr, 0, nullptr, 0,
&pProgram);

VERIFY_IS_TRUE(result);

CComPtr<IDxcValidator> pValidator;
CComPtr<IDxcOperationResult> pResult;
unsigned Flags = 0;
VERIFY_SUCCEEDED(
m_dllSupport.CreateInstance(CLSID_DxcValidator, &pValidator));

VERIFY_SUCCEEDED(pValidator->Validate(pProgram, Flags, &pResult));
HRESULT status;
VERIFY_IS_NOT_NULL(pResult);
CComPtr<IDxcBlob> pValidationOutput;
pResult->GetStatus(&status);

// expect validation to succeed
VERIFY_SUCCEEDED(status);
pResult->GetResult(&pValidationOutput);

hlsl::DxilContainerHeader *pHeader =
(hlsl::DxilContainerHeader *)pProgram->GetBufferPointer();
// Validate the hash.
constexpr uint32_t HashStartOffset =
offsetof(struct DxilContainerHeader, Version);
auto *DataToHash = (const BYTE *)pHeader + HashStartOffset;
UINT AmountToHash = pHeader->ContainerSizeInBytes - HashStartOffset;
BYTE Result[DxilContainerHashSize];
ComputeHashRetail(DataToHash, AmountToHash, Result);
VERIFY_ARE_EQUAL(memcmp(Result, pHeader->Hash.Digest, sizeof(Result)), 0);

// If there is no available pre-release version to test, return
if (DXIL::CompareVersions(ShaderModel::kHighestMajor,
ShaderModel::kHighestMinor,
ShaderModel::kHighestReleasedMajor,
ShaderModel::kHighestReleasedMinor) <= 0) {
return;
}

// Now test a pre-release version.
pProgram.Release();
pShaderModel =
ShaderModel::Get(ShaderModel::Kind::Pixel, ShaderModel::kHighestMajor,
ShaderModel::kHighestMinor)
->GetName();
result = CompileSource(pSourceBlob, pShaderModel, nullptr, 0, nullptr, 0,
&pProgram);

VERIFY_IS_TRUE(result);
pResult.Release();
VERIFY_SUCCEEDED(pValidator->Validate(pProgram, Flags, &pResult));
VERIFY_IS_NOT_NULL(pResult);
pValidationOutput.Release();
pResult->GetStatus(&status);

// expect validation to succeed
VERIFY_SUCCEEDED(status);
pResult->GetResult(&pValidationOutput);

pHeader = (hlsl::DxilContainerHeader *)pProgram->GetBufferPointer();

// Validate the hash.
BYTE PreviewBypassHash[DxilContainerHashSize] = {2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2};

// Should be equal, this proves the hash is set to the preview bypass hash
// when a prerelease version is used
VERIFY_ARE_EQUAL(memcmp(PreviewBypassHash, pHeader->Hash.Digest,
sizeof(PreviewBypassHash)),
0);
}

TEST_F(ValidationTest, ValidateVersionNotAllowed) {
if (m_ver.SkipDxilVersion(1, 6))
return;
Expand Down
Loading
Loading