From 4b0a1df21f5df63d6f7ef01c481289727654f253 Mon Sep 17 00:00:00 2001 From: "Edgar San Martin, Jr." Date: Tue, 23 Dec 2025 11:36:06 -0500 Subject: [PATCH] GPU: Add bounds validation for slot bindings and uniform data pushes. (#14692) (cherry picked from commit bd29d60d3c10f50732d0166b9dc51dc45b712625) --- 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 4c04771356..d16b02d5ef 100644 --- a/src/gpu/SDL_gpu.c +++ b/src/gpu/SDL_gpu.c @@ -994,13 +994,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) { @@ -1073,6 +1095,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; } @@ -1081,6 +1104,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; } @@ -1161,6 +1185,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( @@ -1620,6 +1664,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 @@ -1646,6 +1694,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 @@ -1672,6 +1724,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 @@ -1974,6 +2030,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 @@ -2009,6 +2069,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 @@ -2040,6 +2104,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 @@ -2070,6 +2138,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 @@ -2104,6 +2176,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 @@ -2135,6 +2211,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 @@ -2418,6 +2498,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 @@ -2448,6 +2532,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 @@ -2478,6 +2566,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 @@ -3097,6 +3189,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!"); } }