From 22dfde8e7cb3a052c03d56c0a1872d7cb53fe9bd Mon Sep 17 00:00:00 2001 From: omigamedev Date: Tue, 2 Jun 2026 18:00:30 +0200 Subject: [PATCH] Validate shader resource label bounds --- docs/modernization/build-inventory.md | 2 +- docs/modernization/roadmap.md | 1 + src/renderer_api/renderer_api.cpp | 5 ++-- tests/renderer_api/renderer_api_tests.cpp | 31 +++++++++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/docs/modernization/build-inventory.md b/docs/modernization/build-inventory.md index 14ef7ba..0a03c15 100644 --- a/docs/modernization/build-inventory.md +++ b/docs/modernization/build-inventory.md @@ -301,7 +301,7 @@ Known local toolchain state: explicit texture usage flags, command order, render-pass color/depth/stencil clear intent, scissor state, depth state, blend state, texture-slot binding, sampler-state binding, texture-upload byte - counts, texture mip-level counts, resource debug labels, mipmap-generation commands, + counts, texture mip-level counts, texture/mesh/shader resource debug labels, mipmap-generation commands, texture-state transitions, shader-uniform writes, explicit draw descriptor ranges, texture-copy regions, readback bounds, frame-capture sources, destination buffer sizes, and render-target blit regions, records diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 8d874c6..c122817 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -431,6 +431,7 @@ mipmap-generation command validation, texture-state transition validation, frame frame-capture command validation, render-target blit validation, texture-slot binding validation, blend-state validation, scissor-state validation, depth-state validation, trace marker/scope validation, sampler-state validation, +texture/mesh/shader resource-label validation, recording-device reuse/reset validation, and the canonical PanoPainter shader catalog now consumed by the legacy OpenGL app initialization path. `pp_renderer_gl` now exists as the first OpenGL backend library and owns pure diff --git a/src/renderer_api/renderer_api.cpp b/src/renderer_api/renderer_api.cpp index d5caa0d..3b63213 100644 --- a/src/renderer_api/renderer_api.cpp +++ b/src/renderer_api/renderer_api.cpp @@ -503,8 +503,9 @@ pp::foundation::Status validate_texture_slot(std::uint32_t slot) noexcept pp::foundation::Status validate_shader_program_desc(ShaderProgramDesc desc) noexcept { - if (desc.debug_name == nullptr) { - return pp::foundation::Status::invalid_argument("shader debug name must not be null"); + const auto label_status = validate_resource_label(desc.debug_name); + if (!label_status.ok()) { + return label_status; } const auto vertex_status = validate_shader_stage_source( diff --git a/tests/renderer_api/renderer_api_tests.cpp b/tests/renderer_api/renderer_api_tests.cpp index 8afda9f..d15de6f 100644 --- a/tests/renderer_api/renderer_api_tests.cpp +++ b/tests/renderer_api/renderer_api_tests.cpp @@ -801,6 +801,13 @@ void validates_resource_labels(pp::tests::Harness& h) .debug_name = "brush-quad", }) .ok()); + static constexpr char shader_source[] = "void main() {}"; + PP_EXPECT(h, validate_shader_program_desc(ShaderProgramDesc { + .debug_name = "brush-shader", + .vertex = ShaderStageSource { .source = shader_source, .source_size = sizeof(shader_source) - 1U }, + .fragment = ShaderStageSource { .source = shader_source, .source_size = sizeof(shader_source) - 1U }, + }) + .ok()); const auto null_label = validate_resource_label(nullptr); const auto oversized = validate_resource_label(oversized_label.data()); @@ -814,6 +821,16 @@ void validates_resource_labels(pp::tests::Harness& h) .topology = PrimitiveTopology::triangles, .debug_name = oversized_label.data(), }); + const auto null_shader_label = validate_shader_program_desc(ShaderProgramDesc { + .debug_name = nullptr, + .vertex = ShaderStageSource { .source = shader_source, .source_size = sizeof(shader_source) - 1U }, + .fragment = ShaderStageSource { .source = shader_source, .source_size = sizeof(shader_source) - 1U }, + }); + const auto oversized_shader_label = validate_shader_program_desc(ShaderProgramDesc { + .debug_name = oversized_label.data(), + .vertex = ShaderStageSource { .source = shader_source, .source_size = sizeof(shader_source) - 1U }, + .fragment = ShaderStageSource { .source = shader_source, .source_size = sizeof(shader_source) - 1U }, + }); PP_EXPECT(h, !null_label.ok()); PP_EXPECT(h, null_label.code == StatusCode::invalid_argument); @@ -823,6 +840,10 @@ void validates_resource_labels(pp::tests::Harness& h) PP_EXPECT(h, null_texture_label.code == StatusCode::invalid_argument); PP_EXPECT(h, !oversized_mesh_label.ok()); PP_EXPECT(h, oversized_mesh_label.code == StatusCode::out_of_range); + PP_EXPECT(h, !null_shader_label.ok()); + PP_EXPECT(h, null_shader_label.code == StatusCode::invalid_argument); + PP_EXPECT(h, !oversized_shader_label.ok()); + PP_EXPECT(h, oversized_shader_label.code == StatusCode::out_of_range); } void validates_mipmap_generation_contract(pp::tests::Harness& h) @@ -1686,6 +1707,9 @@ void renderer_interfaces_support_backend_neutral_dispatch(pp::tests::Harness& h) void render_devices_create_validated_resources(pp::tests::Harness& h) { static constexpr char shader_source[] = "void main() {}"; + std::array oversized_label {}; + oversized_label.fill('s'); + oversized_label[max_resource_label_bytes + 1U] = '\0'; RecordingRenderDevice device; const auto texture = device.create_texture(TextureDesc { @@ -1750,6 +1774,11 @@ void render_devices_create_validated_resources(pp::tests::Harness& h) .vertex = ShaderStageSource {}, .fragment = ShaderStageSource {}, }); + const auto oversized_shader_label = device.create_shader_program(ShaderProgramDesc { + .debug_name = oversized_label.data(), + .vertex = ShaderStageSource { .source = shader_source, .source_size = sizeof(shader_source) - 1U }, + .fragment = ShaderStageSource { .source = shader_source, .source_size = sizeof(shader_source) - 1U }, + }); const auto bad_mesh = device.create_mesh(MeshDesc {}); const auto bad_readback = device.create_readback_buffer(0); @@ -1759,6 +1788,8 @@ void render_devices_create_validated_resources(pp::tests::Harness& h) PP_EXPECT(h, bad_target.status().code == StatusCode::invalid_argument); PP_EXPECT(h, !bad_shader.ok()); PP_EXPECT(h, bad_shader.status().code == StatusCode::invalid_argument); + PP_EXPECT(h, !oversized_shader_label.ok()); + PP_EXPECT(h, oversized_shader_label.status().code == StatusCode::out_of_range); PP_EXPECT(h, !bad_mesh.ok()); PP_EXPECT(h, bad_mesh.status().code == StatusCode::invalid_argument); PP_EXPECT(h, !bad_readback.ok());