Skip to content

Commit

Permalink
Error on diff blob versions part 1 (microsoft#5378)
Browse files Browse the repository at this point in the history
This PR is the first of 2 PRs to prevent libraries of different compiler
versions from linking together. This first PR implements adding the
compiler version information into the Dxil Container itself, to allow
for comparison later. It also adds some basic validation to the new part
in the Dxil Container.
  • Loading branch information
bob80905 authored Jul 10, 2023
1 parent e931c94 commit 6287d51
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 163 deletions.
3 changes: 3 additions & 0 deletions include/dxc/DxilContainer/DxilContainerAssembler.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "dxc/DxilContainer/DxilContainer.h"
#include "llvm/ADT/StringRef.h"

struct IDxcVersionInfo;
struct IStream;
class DxilPipelineStateValidation;

Expand Down Expand Up @@ -51,6 +52,7 @@ DxilPartWriter *NewRootSignatureWriter(const RootSignatureHandle &S);
DxilPartWriter *NewFeatureInfoWriter(const DxilModule &M);
DxilPartWriter *NewPSVWriter(const DxilModule &M, uint32_t PSVVersion = UINT_MAX);
DxilPartWriter *NewRDATWriter(const DxilModule &M);
DxilPartWriter *NewVersionWriter(IDxcVersionInfo *pVersionInfo);

// Store serialized ViewID data from DxilModule to PipelineStateValidation.
void StoreViewIDStateToPSV(const uint32_t *pInputData,
Expand All @@ -77,6 +79,7 @@ void WriteProgramPart(const hlsl::ShaderModel *pModel,

void SerializeDxilContainerForModule(
hlsl::DxilModule *pModule, AbstractMemoryStream *pModuleBitcode,
IDxcVersionInfo *DXCVersionInfo,
AbstractMemoryStream *pStream, llvm::StringRef DebugName,
SerializeDxilFlags Flags, DxilShaderHash *pShaderHashOut = nullptr,
AbstractMemoryStream *pReflectionStreamOut = nullptr,
Expand Down
112 changes: 112 additions & 0 deletions lib/DxilContainer/DxilContainerAssembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,93 @@ class DxilPSVWriter : public DxilPartWriter {
}
};

//////////////////////////////////////////////////////////
// DxilVersionWriter - Writes VERS part
class DxilVersionWriter : public DxilPartWriter {
hlsl::DxilCompilerVersion m_Header = {};
CComHeapPtr<char> m_CommitShaStorage;
llvm::StringRef m_CommitSha = "";
CComHeapPtr<char> m_CustomStringStorage;
llvm::StringRef m_CustomString = "";
public:
DxilVersionWriter(IDxcVersionInfo *pVersion)
{
Init(pVersion);
}

void Init(IDxcVersionInfo *pVersionInfo) {
m_Header = {};

UINT32 Major = 0, Minor = 0;
UINT32 Flags = 0;
IFT(pVersionInfo->GetVersion(&Major, &Minor));
IFT(pVersionInfo->GetFlags(&Flags));

m_Header.Major = Major;
m_Header.Minor = Minor;
m_Header.VersionFlags = Flags;
CComPtr<IDxcVersionInfo2> pVersionInfo2;
if (SUCCEEDED(pVersionInfo->QueryInterface(&pVersionInfo2))) {
UINT32 CommitCount = 0;
IFT(pVersionInfo2->GetCommitInfo(&CommitCount, &m_CommitShaStorage));
m_CommitSha = llvm::StringRef(m_CommitShaStorage.m_pData, strlen(m_CommitShaStorage.m_pData));
m_Header.CommitCount = CommitCount;
m_Header.VersionStringListSizeInBytes += m_CommitSha.size();
}
m_Header.VersionStringListSizeInBytes += /*null term*/ 1;

CComPtr<IDxcVersionInfo3> pVersionInfo3;
if (SUCCEEDED(pVersionInfo->QueryInterface(&pVersionInfo3))) {
IFT(pVersionInfo3->GetCustomVersionString(&m_CustomStringStorage));
m_CustomString = llvm::StringRef(m_CustomStringStorage, strlen(m_CustomStringStorage.m_pData));
m_Header.VersionStringListSizeInBytes += m_CustomString.size();
}
m_Header.VersionStringListSizeInBytes += /*null term*/ 1;
}

static uint32_t PadToDword(uint32_t size, uint32_t *outNumPadding=nullptr) {
uint32_t rem = size % 4;
if (rem) {
uint32_t padding = (4 - rem);
if (outNumPadding)
*outNumPadding = padding;
return size + padding;
}
if (outNumPadding)
*outNumPadding = 0;
return size;
}

UINT32 size() const override {
return PadToDword(sizeof(m_Header) + m_Header.VersionStringListSizeInBytes);
}

void write(AbstractMemoryStream *pStream) override {
const uint8_t padByte = 0;
UINT32 uPadding = 0;
UINT32 uSize = PadToDword(sizeof(m_Header) + m_Header.VersionStringListSizeInBytes, &uPadding);
(void)uSize;

ULONG cbWritten = 0;
IFT(pStream->Write(&m_Header, sizeof(m_Header), &cbWritten));

// Write a null terminator even if the string is empty
IFT(pStream->Write(m_CommitSha.data(), m_CommitSha.size(), &cbWritten));
// Null terminator for the commit sha
IFT(pStream->Write(&padByte, sizeof(padByte), &cbWritten));

// Write the custom version string.
IFT(pStream->Write(m_CustomString.data(), m_CustomString.size(), &cbWritten));
// Null terminator for the custom version string.
IFT(pStream->Write(&padByte, sizeof(padByte), &cbWritten));

// Write padding
for (unsigned i = 0; i < uPadding; i++) {
IFT(pStream->Write(&padByte, sizeof(padByte), &cbWritten));
}
}
};

using namespace DXIL;

class DxilRDATWriter : public DxilPartWriter {
Expand Down Expand Up @@ -1398,6 +1485,10 @@ DxilPartWriter *hlsl::NewRDATWriter(const DxilModule &M) {
return new DxilRDATWriter(M);
}

DxilPartWriter *hlsl::NewVersionWriter(IDxcVersionInfo *DXCVersionInfo) {
return new DxilVersionWriter(DXCVersionInfo);
}

class DxilContainerWriter_impl : public DxilContainerWriter {
private:
class DxilPart {
Expand Down Expand Up @@ -1575,6 +1666,7 @@ void hlsl::StripAndCreateReflectionStream(Module *pReflectionM, uint32_t *pRefle

void hlsl::SerializeDxilContainerForModule(
DxilModule *pModule, AbstractMemoryStream *pModuleBitcode,
IDxcVersionInfo *DXCVersionInfo,
AbstractMemoryStream *pFinalStream, llvm::StringRef DebugName,
SerializeDxilFlags Flags, DxilShaderHash *pShaderHashOut,
AbstractMemoryStream *pReflectionStreamOut,
Expand All @@ -1590,6 +1682,7 @@ void hlsl::SerializeDxilContainerForModule(

unsigned ValMajor, ValMinor;
pModule->GetValidatorVersion(ValMajor, ValMinor);
bool bValidatorAtLeast_1_8 = DXIL::CompareVersions(ValMajor, ValMinor, 1, 8) >= 0;
if (DXIL::CompareVersions(ValMajor, ValMinor, 1, 1) < 0)
Flags &= ~SerializeDxilFlags::IncludeDebugNamePart;
bool bSupportsShaderHash = DXIL::CompareVersions(ValMajor, ValMinor, 1, 5) >= 0;
Expand Down Expand Up @@ -1646,8 +1739,11 @@ void hlsl::SerializeDxilContainerForModule(
});
}
}

std::unique_ptr<DxilVersionWriter> pVERSWriter = nullptr;
std::unique_ptr<DxilRDATWriter> pRDATWriter = nullptr;
std::unique_ptr<DxilPSVWriter> pPSVWriter = nullptr;

unsigned int major, minor;
pModule->GetDxilVersion(major, minor);
RootSignatureWriter rootSigWriter(std::move(pModule->GetSerializedRootSignature())); // Grab RS here
Expand All @@ -1657,6 +1753,22 @@ void hlsl::SerializeDxilContainerForModule(
if (pModule->GetShaderModel()->IsLib()) {
DXASSERT(pModule->GetSerializedRootSignature().empty(),
"otherwise, library has root signature outside subobject definitions");
// Write the DxilCompilerVersion (VERS) part.
if (DXCVersionInfo && bValidatorAtLeast_1_8) {

pVERSWriter = llvm::make_unique<DxilVersionWriter>(DXCVersionInfo);

writer.AddPart(
hlsl::DFCC_CompilerVersion,
pVERSWriter->size(),
[&pVERSWriter](AbstractMemoryStream *pStream) {
pVERSWriter->write(pStream);
return S_OK;
}
);
}


// Write the DxilRuntimeData (RDAT) part.
pRDATWriter = llvm::make_unique<DxilRDATWriter>(*pModule);
writer.AddPart(
Expand Down
86 changes: 86 additions & 0 deletions lib/HLSL/DxilValidation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5539,6 +5539,79 @@ static void VerifyFeatureInfoMatches(_In_ ValidationContext &ValCtx,
VerifyBlobPartMatches(ValCtx, "Feature Info", pWriter.get(), pFeatureInfoData, FeatureInfoSize);
}

// return true if the pBlob is a valid, well-formed CompilerVersion part, false
// otherwise
bool ValidateCompilerVersionPart(const void *pBlobPtr, UINT blobSize) {
// The hlsl::DxilCompilerVersion struct is always 16 bytes. (2 2-byte
// uint16's, 3 4-byte uint32's) The blob size should absolutely never be less
// than 16 bytes.
if (blobSize < sizeof(hlsl::DxilCompilerVersion)) {
return false;
}

const hlsl::DxilCompilerVersion *pDCV =
(const hlsl::DxilCompilerVersion *)pBlobPtr;
if (pDCV->VersionStringListSizeInBytes == 0) {
// No version strings, just make sure there is no extra space.
return blobSize == sizeof(hlsl::DxilCompilerVersion);
}

// after this point, we know VersionStringListSizeInBytes >= 1, because it is
// a UINT

UINT EndOfVersionStringIndex =
sizeof(hlsl::DxilCompilerVersion) + pDCV->VersionStringListSizeInBytes;
// Make sure that the buffer size is large enough to contain both the DCV
// struct and the version string but not any larger than necessary
if (PSVALIGN4(EndOfVersionStringIndex) != blobSize) {
return false;
}

const char *VersionStringsListData =
(const char *)pBlobPtr + sizeof(hlsl::DxilCompilerVersion);
UINT VersionStringListSizeInBytes = pDCV->VersionStringListSizeInBytes;

// now make sure that any pad bytes that were added are null-terminators.
for (UINT i = VersionStringListSizeInBytes;
i < blobSize - sizeof(hlsl::DxilCompilerVersion); i++) {
if (VersionStringsListData[i] != '\0') {
return false;
}
}

// Now, version string validation
// first, the final byte of the string should always be null-terminator so
// that the string ends
if (VersionStringsListData[VersionStringListSizeInBytes - 1] != '\0') {
return false;
}

// construct the first string
// data format for VersionString can be see in the definition for the
// DxilCompilerVersion struct. summary: 2 strings that each end with the null
// terminator, and [0-3] null terminators after the final null terminator
StringRef firstStr(VersionStringsListData);

// if the second string exists, attempt to construct it.
if (VersionStringListSizeInBytes > (firstStr.size() + 1)) {
StringRef secondStr(VersionStringsListData + firstStr.size() + 1);

// the VersionStringListSizeInBytes member should be exactly equal to the
// two string lengths, plus the 2 null terminator bytes.
if (VersionStringListSizeInBytes !=
firstStr.size() + secondStr.size() + 2) {
return false;
}
} else {
// the VersionStringListSizeInBytes member should be exactly equal to the
// first string length, plus the 1 null terminator byte.
if (VersionStringListSizeInBytes != firstStr.size() + 1) {
return false;
}
}

return true;
}

static void VerifyRDATMatches(_In_ ValidationContext &ValCtx,
_In_reads_bytes_(RDATSize) const void *pRDATData,
Expand Down Expand Up @@ -5657,6 +5730,19 @@ HRESULT ValidateDxilContainerParts(llvm::Module *pModule,
case DFCC_FeatureInfo:
VerifyFeatureInfoMatches(ValCtx, GetDxilPartData(pPart), pPart->PartSize);
break;
case DFCC_CompilerVersion:
// This blob is either a PDB, or a library profile
if (ValCtx.isLibProfile) {
if (!ValidateCompilerVersionPart((void *)GetDxilPartData(pPart), pPart->PartSize))
{
ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, { szFourCC });
}
}
else {
ValCtx.EmitFormatError(ValidationRule::ContainerPartInvalid, { szFourCC });
}
break;

case DFCC_RootSignature:
pRootSignaturePart = pPart;
if (ValCtx.isLibProfile) {
Expand Down
Loading

0 comments on commit 6287d51

Please sign in to comment.