From d76e4f60544464297123526636327936607a6d2e Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Fri, 17 Apr 2026 09:48:43 +0200 Subject: [PATCH] pan/lib: validate data_size_B in drivers In order to be able to properly check for maxResourceSize on Vulkan, we need to be able to report the size even for resources that overflow that limit. Otherwise we end up failing to find a usable modifier rather than properly report the problem to the application. This means we need to move the check out of the mod-handler. There's no need to validate the slice-stride. The reason is a little bit complicated, but we have two possible cases: 1. V10 and before: the image-size and the slice-stride are both limited to UINT32_MAX. Since the image-size is always at least as large as the slice-stride, it's enough to check the image-stride. 2. V11 and later: 37 bits is large enough to store any valid slice-stride. The only way we could blow this one up, would be to pass out-of-range width or height, which is already either validated by higher-level logic (gallium) or UB (vulkan). This is important, because we don't have another mandate to reject large resources on Vulkan; we can only reject due to maxResourceSize, not an individual plane. So let's move this out to the call-site. We don't need to do anything for PanVK, becuase it already checks for maxResourceSize. To keep the Gallium and Vulkan driver as similar as reasonably possible, check against the whole resource even in Gallium, where we could have gotten away with checking a plane at the time instead. Reviewed-by: Boris Brezillon Reviewed-by: Lars-Ivar Hesselberg Simonsen Part-of: --- src/gallium/drivers/panfrost/pan_resource.c | 36 +++++++++++++++------ src/panfrost/lib/pan_mod.c | 30 +---------------- src/panfrost/lib/tests/test-layout.cpp | 19 +++++++++++ 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/src/gallium/drivers/panfrost/pan_resource.c b/src/gallium/drivers/panfrost/pan_resource.c index d924c3c6cdd..5ac43cdeeae 100644 --- a/src/gallium/drivers/panfrost/pan_resource.c +++ b/src/gallium/drivers/panfrost/pan_resource.c @@ -85,6 +85,12 @@ panfrost_clear_render_target(struct pipe_context *pipe, height); } +static uint64_t +panfrost_max_res_size_b(unsigned arch) +{ + return u_uintN_max(arch < 11 ? 32 : 48); +} + static bool panfrost_resource_init_image( struct pipe_screen *screen, struct panfrost_resource *rsc, @@ -111,22 +117,32 @@ panfrost_resource_init_image( /* The rest of the resource planes will be initialized when we hit the first * plane. */ - if (plane_idx > 0 || format_plane_count == 1) + if (plane_idx > 0) return true; - plane_idx = 1; - for (struct panfrost_resource *plane = pan_resource(rsc->base.next); - plane && plane_idx < ARRAY_SIZE(rsc->image.planes); - plane = pan_resource(plane->base.next)) - rsc->image.planes[plane_idx++] = &plane->plane; + if (format_plane_count > 1) { + plane_idx = 1; + for (struct panfrost_resource *plane = pan_resource(rsc->base.next); + plane && plane_idx < ARRAY_SIZE(rsc->image.planes); + plane = pan_resource(plane->base.next)) + rsc->image.planes[plane_idx++] = &plane->plane; - assert(plane_idx == util_format_get_num_planes(iprops->format)); + assert(plane_idx == util_format_get_num_planes(iprops->format)); - for (struct panfrost_resource *plane = pan_resource(rsc->base.next); - plane; plane = pan_resource(plane->base.next)) { - memcpy(plane->image.planes, rsc->image.planes, sizeof(plane->image.planes)); + for (struct panfrost_resource *plane = pan_resource(rsc->base.next); + plane; plane = pan_resource(plane->base.next)) { + memcpy(plane->image.planes, rsc->image.planes, sizeof(plane->image.planes)); + } } + /* validate layout */ + uint64_t res_size = 0; + for (uint32_t i = 0; i < util_format_get_num_planes(iprops->format); i++) + res_size += rsc->image.planes[i]->layout.data_size_B; + + if (res_size > panfrost_max_res_size_b(dev->arch)) + return false; + return true; } diff --git a/src/panfrost/lib/pan_mod.c b/src/panfrost/lib/pan_mod.c index ede832d56ec..f480946de7d 100644 --- a/src/panfrost/lib/pan_mod.c +++ b/src/panfrost/lib/pan_mod.c @@ -16,14 +16,6 @@ #include "util/format/u_format.h" -#if PAN_ARCH <= 10 -#define MAX_SIZE_B u_uintN_max(32) -#define MAX_SLICE_STRIDE_B u_uintN_max(32) -#else -#define MAX_SIZE_B u_uintN_max(48) -#define MAX_SLICE_STRIDE_B u_uintN_max(37) -#endif - static bool pan_mod_afbc_match(uint64_t mod) { @@ -179,8 +171,7 @@ pan_mod_afbc_init_slice_layout( slayout->afbc.surface_stride_B = surf_stride_B; slayout->size_B = surf_stride_B * mip_extent_px.depth; - if (hdr_surf_size_B > UINT32_MAX || surf_stride_B > MAX_SLICE_STRIDE_B || - slayout->size_B > MAX_SIZE_B) + if (hdr_surf_size_B > UINT32_MAX) return false; return true; @@ -427,11 +418,6 @@ pan_mod_afrc_init_slice_layout( slayout->size_B = surf_stride_B * aligned_extent_px.depth * props->nr_samples; - /* Make sure the stride/size fits in the descriptor fields. */ - if (slayout->size_B > MAX_SIZE_B || - slayout->tiled_or_linear.surface_stride_B > MAX_SLICE_STRIDE_B) - return false; - return true; } @@ -571,11 +557,6 @@ pan_mod_u_tiled_init_slice_layout( slayout->tiled_or_linear.surface_stride_B = surf_stride_B; slayout->size_B = surf_stride_B * mip_extent_el.depth * props->nr_samples; - /* Make sure the stride/size fits in the descriptor fields. */ - if (slayout->size_B > MAX_SIZE_B || - slayout->tiled_or_linear.surface_stride_B > MAX_SLICE_STRIDE_B) - return false; - return true; } @@ -620,10 +601,6 @@ pan_mod_interleaved_64k_init_slice_layout( slayout->tiled_or_linear.row_stride_B = row_stride_B; slayout->tiled_or_linear.surface_stride_B = surf_stride_B; - if (slayout->size_B > MAX_SIZE_B || - slayout->tiled_or_linear.surface_stride_B > MAX_SLICE_STRIDE_B) - return false; - return true; } @@ -802,11 +779,6 @@ pan_mod_linear_init_slice_layout( (uint64_t)slayout->tiled_or_linear.row_stride_B * mip_extent_el.height; surf_stride_B = ALIGN_POT(surf_stride_B, (uint64_t)align_mask + 1); - /* Surface stride is passed as a 32-bit unsigned integer to RT/ZS and texture - * descriptors, make sure it fits. */ - if (surf_stride_B > MAX_SLICE_STRIDE_B) - return false; - slayout->tiled_or_linear.surface_stride_B = surf_stride_B; slayout->size_B = surf_stride_B * mip_extent_el.depth * props->nr_samples; return true; diff --git a/src/panfrost/lib/tests/test-layout.cpp b/src/panfrost/lib/tests/test-layout.cpp index a09e2f4bb82..b168e190ba7 100644 --- a/src/panfrost/lib/tests/test-layout.cpp +++ b/src/panfrost/lib/tests/test-layout.cpp @@ -249,6 +249,25 @@ layout_init(unsigned arch, const struct pan_image_props *props, return true; } +TEST(Layout, LargeImage) +{ + struct pan_image_props p = { + .modifier = DRM_FORMAT_MOD_LINEAR, + .format = PIPE_FORMAT_R8G8B8A8_UNORM, + .extent_px = { + .width = 65536, + .height = 65536, + .depth = 1, + }, + .nr_samples = 1, + .dim = MALI_TEXTURE_DIMENSION_2D, + .nr_slices = 1, + }; + struct pan_image_layout l = {}; + + ASSERT_TRUE(layout_init(0, &p, 0, NULL, &l)); +} + /* dEQP-GLES3.functional.texture.format.compressed.etc1_2d_pot */ TEST(Layout, ImplicitLayoutInterleavedETC2) {