Skip to content

GPU: Validate shader bytecode #12707

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

Merged
merged 1 commit into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/gpu/d3d12/SDL_gpu_d3d12.c
Original file line number Diff line number Diff line change
Expand Up @@ -2473,12 +2473,32 @@ static D3D12GraphicsRootSignature *D3D12_INTERNAL_CreateGraphicsRootSignature(
return d3d12GraphicsRootSignature;
}

static bool D3D12_INTERNAL_IsValidShaderBytecode(
const Uint8 *code,
size_t codeSize)
{
// Both DXIL and DXBC bytecode have a 4 byte header containing `DXBC`.
if (codeSize < 4 || code == NULL) {
return false;
}
return SDL_memcmp(code, "DXBC", 4) == 0;
}

static bool D3D12_INTERNAL_CreateShaderBytecode(
D3D12Renderer *renderer,
const Uint8 *code,
size_t codeSize,
SDL_GPUShaderFormat format,
void **pBytecode,
size_t *pBytecodeSize)
{
if (!D3D12_INTERNAL_IsValidShaderBytecode(code, codeSize)) {
if (format == SDL_GPU_SHADERFORMAT_DXBC) {
SET_STRING_ERROR_AND_RETURN("The provided shader code is not valid DXBC!", false);
}
SET_STRING_ERROR_AND_RETURN("The provided shader code is not valid DXIL!", false);
}

if (pBytecode != NULL) {
*pBytecode = SDL_malloc(codeSize);
if (!*pBytecode) {
Expand Down Expand Up @@ -2696,8 +2716,10 @@ static SDL_GPUComputePipeline *D3D12_CreateComputePipeline(
ID3D12PipelineState *pipelineState;

if (!D3D12_INTERNAL_CreateShaderBytecode(
renderer,
createinfo->code,
createinfo->code_size,
createinfo->format,
&bytecode,
&bytecodeSize)) {
return NULL;
Expand Down Expand Up @@ -3104,13 +3126,16 @@ static SDL_GPUShader *D3D12_CreateShader(
SDL_GPURenderer *driverData,
const SDL_GPUShaderCreateInfo *createinfo)
{
D3D12Renderer *renderer = (D3D12Renderer *)driverData;
void *bytecode;
size_t bytecodeSize;
D3D12Shader *shader;

if (!D3D12_INTERNAL_CreateShaderBytecode(
renderer,
createinfo->code,
createinfo->code_size,
createinfo->format,
&bytecode,
&bytecodeSize)) {
return NULL;
Expand Down
16 changes: 16 additions & 0 deletions src/gpu/metal/SDL_gpu_metal.m
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,17 @@ static void METAL_INTERNAL_TrackUniformBuffer(
id<MTLFunction> function;
} MetalLibraryFunction;

static bool METAL_INTERNAL_IsValidMetalLibrary(
const Uint8 *code,
size_t codeSize)
{
// Metal libraries have a 4 byte header containing `MTLB`.
if (codeSize < 4 || code == NULL) {
return false;
}
return SDL_memcmp(code, "MTLB", 4) == 0;
}

// This function assumes that it's called from within an autorelease pool
static MetalLibraryFunction METAL_INTERNAL_CompileShader(
MetalRenderer *renderer,
Expand Down Expand Up @@ -854,6 +865,11 @@ static MetalLibraryFunction METAL_INTERNAL_CompileShader(
options:nil
error:&error];
} else if (format == SDL_GPU_SHADERFORMAT_METALLIB) {
if (!METAL_INTERNAL_IsValidMetalLibrary(code, codeSize)) {
SET_STRING_ERROR_AND_RETURN(
"The provided shader code is not a valid Metal library!",
libraryFunction);
}
data = dispatch_data_create(
code,
codeSize,
Expand Down
27 changes: 27 additions & 0 deletions src/gpu/vulkan/SDL_gpu_vulkan.c
Original file line number Diff line number Diff line change
Expand Up @@ -6488,6 +6488,25 @@ static SDL_GPUGraphicsPipeline *VULKAN_CreateGraphicsPipeline(
return (SDL_GPUGraphicsPipeline *)graphicsPipeline;
}

static bool VULKAN_INTERNAL_IsValidShaderBytecode(
const Uint8 *code,
size_t codeSize)
{
// SPIR-V bytecode has a 4 byte header containing 0x07230203. SPIR-V is
// defined as a stream of words and not a stream of bytes so both byte
// orders need to be considered.
//
// FIXME: It is uncertain if drivers are able to load both byte orders. If
// needed we may need to do an optional swizzle internally so apps can
// continue to treat shader code as an opaque blob.
if (codeSize < 4 || code == NULL) {
return false;
}
const Uint32 magic = 0x07230203;
const Uint32 magicInv = 0x03022307;
return SDL_memcmp(code, &magic, 4) == 0 || SDL_memcmp(code, &magicInv, 4) == 0;
}

static SDL_GPUComputePipeline *VULKAN_CreateComputePipeline(
SDL_GPURenderer *driverData,
const SDL_GPUComputePipelineCreateInfo *createinfo)
Expand All @@ -6503,6 +6522,10 @@ static SDL_GPUComputePipeline *VULKAN_CreateComputePipeline(
SET_STRING_ERROR_AND_RETURN("Incompatible shader format for Vulkan!", NULL);
}

if (!VULKAN_INTERNAL_IsValidShaderBytecode(createinfo->code, createinfo->code_size)) {
SET_STRING_ERROR_AND_RETURN("The provided shader code is not valid SPIR-V!", NULL);
}

vulkanComputePipeline = SDL_malloc(sizeof(VulkanComputePipeline));
shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shaderModuleCreateInfo.pNext = NULL;
Expand Down Expand Up @@ -6648,6 +6671,10 @@ static SDL_GPUShader *VULKAN_CreateShader(
VkShaderModuleCreateInfo vkShaderModuleCreateInfo;
VulkanRenderer *renderer = (VulkanRenderer *)driverData;

if (!VULKAN_INTERNAL_IsValidShaderBytecode(createinfo->code, createinfo->code_size)) {
SET_STRING_ERROR_AND_RETURN("The provided shader code is not valid SPIR-V!", NULL);
}

vulkanShader = SDL_malloc(sizeof(VulkanShader));
vkShaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
vkShaderModuleCreateInfo.pNext = NULL;
Expand Down
Loading