From bd29d60d3c10f50732d0166b9dc51dc45b712625 Mon Sep 17 00:00:00 2001
From: "Edgar San Martin, Jr." <[EMAIL REDACTED]>
Date: Tue, 23 Dec 2025 11:36:06 -0500
Subject: [PATCH] GPU: Add bounds validation for slot bindings and uniform data
pushes. (#14692)
---
src/gpu/SDL_gpu.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 93 insertions(+)
diff --git a/src/gpu/SDL_gpu.c b/src/gpu/SDL_gpu.c
index 38ac785020601..132bc3b83d86c 100644
--- a/src/gpu/SDL_gpu.c
+++ b/src/gpu/SDL_gpu.c
@@ -981,13 +981,35 @@ SDL_GPUComputePipeline *SDL_CreateGPUComputePipeline(
return NULL;
}
if (createinfo->num_readwrite_storage_textures > MAX_COMPUTE_WRITE_TEXTURES) {
+ SDL_COMPILE_TIME_ASSERT(compute_write_textures, MAX_COMPUTE_WRITE_TEXTURES == 8);
SDL_assert_release(!"Compute pipeline write-only texture count cannot be higher than 8!");
return NULL;
}
if (createinfo->num_readwrite_storage_buffers > MAX_COMPUTE_WRITE_BUFFERS) {
+ SDL_COMPILE_TIME_ASSERT(compute_write_buffers, MAX_COMPUTE_WRITE_BUFFERS == 8);
SDL_assert_release(!"Compute pipeline write-only buffer count cannot be higher than 8!");
return NULL;
}
+ if (createinfo->num_samplers > MAX_TEXTURE_SAMPLERS_PER_STAGE) {
+ SDL_COMPILE_TIME_ASSERT(compute_texture_samplers, MAX_TEXTURE_SAMPLERS_PER_STAGE == 16);
+ SDL_assert_release(!"Compute pipeline sampler count cannot be higher than 16!");
+ return NULL;
+ }
+ if (createinfo->num_readonly_storage_textures > MAX_STORAGE_TEXTURES_PER_STAGE) {
+ SDL_COMPILE_TIME_ASSERT(compute_storage_textures, MAX_STORAGE_TEXTURES_PER_STAGE == 8);
+ SDL_assert_release(!"Compute pipeline readonly storage texture count cannot be higher than 8!");
+ return NULL;
+ }
+ if (createinfo->num_readonly_storage_buffers > MAX_STORAGE_BUFFERS_PER_STAGE) {
+ SDL_COMPILE_TIME_ASSERT(compute_storage_buffers, MAX_STORAGE_BUFFERS_PER_STAGE == 8);
+ SDL_assert_release(!"Compute pipeline readonly storage buffer count cannot be higher than 8!");
+ return NULL;
+ }
+ if (createinfo->num_uniform_buffers > MAX_UNIFORM_BUFFERS_PER_STAGE) {
+ SDL_COMPILE_TIME_ASSERT(compute_uniform_buffers, MAX_UNIFORM_BUFFERS_PER_STAGE == 4);
+ SDL_assert_release(!"Compute pipeline uniform buffer count cannot be higher than 4!");
+ return NULL;
+ }
if (createinfo->threadcount_x == 0 ||
createinfo->threadcount_y == 0 ||
createinfo->threadcount_z == 0) {
@@ -1075,6 +1097,7 @@ SDL_GPUGraphicsPipeline *SDL_CreateGPUGraphicsPipeline(
return NULL;
}
if (graphicsPipelineCreateInfo->vertex_input_state.num_vertex_buffers > MAX_VERTEX_BUFFERS) {
+ SDL_COMPILE_TIME_ASSERT(vertex_buffers, MAX_VERTEX_BUFFERS == 16);
SDL_assert_release(!"The number of vertex buffer descriptions in a vertex input state must not exceed 16!");
return NULL;
}
@@ -1083,6 +1106,7 @@ SDL_GPUGraphicsPipeline *SDL_CreateGPUGraphicsPipeline(
return NULL;
}
if (graphicsPipelineCreateInfo->vertex_input_state.num_vertex_attributes > MAX_VERTEX_ATTRIBUTES) {
+ SDL_COMPILE_TIME_ASSERT(vertex_attributes, MAX_VERTEX_ATTRIBUTES == 16);
SDL_assert_release(!"The number of vertex attributes in a vertex input state must not exceed 16!");
return NULL;
}
@@ -1179,6 +1203,26 @@ SDL_GPUShader *SDL_CreateGPUShader(
SDL_assert_release(!"Incompatible shader format for GPU backend");
return NULL;
}
+ if (createinfo->num_samplers > MAX_TEXTURE_SAMPLERS_PER_STAGE) {
+ SDL_COMPILE_TIME_ASSERT(shader_texture_samplers, MAX_TEXTURE_SAMPLERS_PER_STAGE == 16);
+ SDL_assert_release(!"Shader sampler count cannot be higher than 16!");
+ return NULL;
+ }
+ if (createinfo->num_storage_textures > MAX_STORAGE_TEXTURES_PER_STAGE) {
+ SDL_COMPILE_TIME_ASSERT(shader_storage_textures, MAX_STORAGE_TEXTURES_PER_STAGE == 8);
+ SDL_assert_release(!"Shader storage texture count cannot be higher than 8!");
+ return NULL;
+ }
+ if (createinfo->num_storage_buffers > MAX_STORAGE_BUFFERS_PER_STAGE) {
+ SDL_COMPILE_TIME_ASSERT(shader_storage_buffers, MAX_STORAGE_BUFFERS_PER_STAGE == 8);
+ SDL_assert_release(!"Shader storage buffer count cannot be higher than 8!");
+ return NULL;
+ }
+ if (createinfo->num_uniform_buffers > MAX_UNIFORM_BUFFERS_PER_STAGE) {
+ SDL_COMPILE_TIME_ASSERT(shader_uniform_buffers, MAX_UNIFORM_BUFFERS_PER_STAGE == 4);
+ SDL_assert_release(!"Shader uniform buffer count cannot be higher than 4!");
+ return NULL;
+ }
}
return device->CreateShader(
@@ -1650,6 +1694,10 @@ void SDL_PushGPUVertexUniformData(
SDL_InvalidParamError("data");
return;
}
+ CHECK_PARAM(slot_index >= MAX_UNIFORM_BUFFERS_PER_STAGE) {
+ SDL_SetError("slot_index exceeds MAX_UNIFORM_BUFFERS_PER_STAGE");
+ return;
+ }
if (COMMAND_BUFFER_DEVICE->debug_mode) {
CHECK_COMMAND_BUFFER
@@ -1676,6 +1724,10 @@ void SDL_PushGPUFragmentUniformData(
SDL_InvalidParamError("data");
return;
}
+ CHECK_PARAM(slot_index >= MAX_UNIFORM_BUFFERS_PER_STAGE) {
+ SDL_SetError("slot_index exceeds MAX_UNIFORM_BUFFERS_PER_STAGE");
+ return;
+ }
if (COMMAND_BUFFER_DEVICE->debug_mode) {
CHECK_COMMAND_BUFFER
@@ -1702,6 +1754,10 @@ void SDL_PushGPUComputeUniformData(
SDL_InvalidParamError("data");
return;
}
+ CHECK_PARAM(slot_index >= MAX_UNIFORM_BUFFERS_PER_STAGE) {
+ SDL_SetError("slot_index exceeds MAX_UNIFORM_BUFFERS_PER_STAGE");
+ return;
+ }
if (COMMAND_BUFFER_DEVICE->debug_mode) {
CHECK_COMMAND_BUFFER
@@ -2008,6 +2064,10 @@ void SDL_BindGPUVertexSamplers(
SDL_InvalidParamError("texture_sampler_bindings");
return;
}
+ CHECK_PARAM(first_slot + num_bindings > MAX_TEXTURE_SAMPLERS_PER_STAGE) {
+ SDL_SetError("first_slot + num_bindings exceeds MAX_TEXTURE_SAMPLERS_PER_STAGE");
+ return;
+ }
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
@@ -2043,6 +2103,10 @@ void SDL_BindGPUVertexStorageTextures(
SDL_InvalidParamError("storage_textures");
return;
}
+ CHECK_PARAM(first_slot + num_bindings > MAX_STORAGE_TEXTURES_PER_STAGE) {
+ SDL_SetError("first_slot + num_bindings exceeds MAX_STORAGE_TEXTURES_PER_STAGE");
+ return;
+ }
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
@@ -2074,6 +2138,10 @@ void SDL_BindGPUVertexStorageBuffers(
SDL_InvalidParamError("storage_buffers");
return;
}
+ CHECK_PARAM(first_slot + num_bindings > MAX_STORAGE_BUFFERS_PER_STAGE) {
+ SDL_SetError("first_slot + num_bindings exceeds MAX_STORAGE_BUFFERS_PER_STAGE");
+ return;
+ }
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
@@ -2104,6 +2172,10 @@ void SDL_BindGPUFragmentSamplers(
SDL_InvalidParamError("texture_sampler_bindings");
return;
}
+ CHECK_PARAM(first_slot + num_bindings > MAX_TEXTURE_SAMPLERS_PER_STAGE) {
+ SDL_SetError("first_slot + num_bindings exceeds MAX_TEXTURE_SAMPLERS_PER_STAGE");
+ return;
+ }
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
@@ -2138,6 +2210,10 @@ void SDL_BindGPUFragmentStorageTextures(
SDL_InvalidParamError("storage_textures");
return;
}
+ CHECK_PARAM(first_slot + num_bindings > MAX_STORAGE_TEXTURES_PER_STAGE) {
+ SDL_SetError("first_slot + num_bindings exceeds MAX_STORAGE_TEXTURES_PER_STAGE");
+ return;
+ }
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
@@ -2169,6 +2245,10 @@ void SDL_BindGPUFragmentStorageBuffers(
SDL_InvalidParamError("storage_buffers");
return;
}
+ CHECK_PARAM(first_slot + num_bindings > MAX_STORAGE_BUFFERS_PER_STAGE) {
+ SDL_SetError("first_slot + num_bindings exceeds MAX_STORAGE_BUFFERS_PER_STAGE");
+ return;
+ }
if (RENDERPASS_DEVICE->debug_mode) {
CHECK_RENDERPASS
@@ -2453,6 +2533,10 @@ void SDL_BindGPUComputeSamplers(
SDL_InvalidParamError("texture_sampler_bindings");
return;
}
+ CHECK_PARAM(first_slot + num_bindings > MAX_TEXTURE_SAMPLERS_PER_STAGE) {
+ SDL_SetError("first_slot + num_bindings exceeds MAX_TEXTURE_SAMPLERS_PER_STAGE");
+ return;
+ }
if (COMPUTEPASS_DEVICE->debug_mode) {
CHECK_COMPUTEPASS
@@ -2483,6 +2567,10 @@ void SDL_BindGPUComputeStorageTextures(
SDL_InvalidParamError("storage_textures");
return;
}
+ CHECK_PARAM(first_slot + num_bindings > MAX_STORAGE_TEXTURES_PER_STAGE) {
+ SDL_SetError("first_slot + num_bindings exceeds MAX_STORAGE_TEXTURES_PER_STAGE");
+ return;
+ }
if (COMPUTEPASS_DEVICE->debug_mode) {
CHECK_COMPUTEPASS
@@ -2513,6 +2601,10 @@ void SDL_BindGPUComputeStorageBuffers(
SDL_InvalidParamError("storage_buffers");
return;
}
+ CHECK_PARAM(first_slot + num_bindings > MAX_STORAGE_BUFFERS_PER_STAGE) {
+ SDL_SetError("first_slot + num_bindings exceeds MAX_STORAGE_BUFFERS_PER_STAGE");
+ return;
+ }
if (COMPUTEPASS_DEVICE->debug_mode) {
CHECK_COMPUTEPASS
@@ -3146,6 +3238,7 @@ bool SDL_SetGPUAllowedFramesInFlight(
if (device->debug_mode) {
if (allowed_frames_in_flight < 1 || allowed_frames_in_flight > 3)
{
+ SDL_COMPILE_TIME_ASSERT(max_frames_in_flight, MAX_FRAMES_IN_FLIGHT == 3);
SDL_assert_release(!"allowed_frames_in_flight value must be between 1 and 3!");
}
}