ui/pixman: fix zero rowstride in qemu_pixman_image_new_shareable()

qemu_create_displaysurface_from() callers such as xlnx_dp.c pass
linesize=0 with data=NULL, relying on pixman to compute the stride.
Since 1ff788db97 ("ui: use a shareable type"), the data=NULL path
goes through qemu_pixman_image_new_shareable() which computes
size = height * rowstride_bytes, resulting in a zero-size allocation
and an abort in qemu_memfd_alloc().

Introduce qemu-pixman-helpers.h with overflow-safe stride and buffer
size computation (matching pixman's create_bits() formula), and use it
from both qemu_pixman_image_new_shareable() and pixman-minimal's
create_bits().

Reported-by: Peter Maydell <peter.maydell@linaro.org>
Fixes: 1ff788db97 ("ui: use a shareable type")
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-ID: <20260611113614.1935094-1-marcandre.lureau@redhat.com>
This commit is contained in:
Marc-André Lureau
2026-06-11 15:36:14 +04:00
parent ebaa76fdcb
commit f7add4533d
4 changed files with 61 additions and 22 deletions

View File

@@ -113,6 +113,8 @@ typedef struct pixman_color {
uint16_t alpha; uint16_t alpha;
} pixman_color_t; } pixman_color_t;
#include "qemu-pixman-helpers.h"
static inline uint32_t *create_bits(pixman_format_code_t format, static inline uint32_t *create_bits(pixman_format_code_t format,
int width, int width,
int height, int height,
@@ -120,28 +122,9 @@ static inline uint32_t *create_bits(pixman_format_code_t format,
{ {
int stride = 0; int stride = 0;
size_t buf_size = 0; size_t buf_size = 0;
int bpp = PIXMAN_FORMAT_BPP(format);
/* if (!qemu_pixman_image_calc_size(format, width, height,
* Calculate the following while checking for overflow truncation: &stride, &buf_size)) {
* stride = ((width * bpp + 0x1f) >> 5) * sizeof(uint32_t);
*/
if (unlikely(__builtin_mul_overflow(width, bpp, &stride))) {
return NULL;
}
if (unlikely(__builtin_add_overflow(stride, 0x1f, &stride))) {
return NULL;
}
stride >>= 5;
stride *= sizeof(uint32_t);
if (unlikely(__builtin_mul_overflow((size_t) height,
(size_t) stride,
&buf_size))) {
return NULL; return NULL;
} }

View File

@@ -0,0 +1,49 @@
/* SPDX-License-Identifier: MIT */
/*
* Pixman stride and buffer size helpers.
* Expects PIXMAN_FORMAT_BPP() and pixman_format_code_t to be
* already defined (by either <pixman.h> or "pixman-minimal.h").
*/
#ifndef QEMU_PIXMAN_HELPERS_H
#define QEMU_PIXMAN_HELPERS_H
/*
* Compute the row stride for a pixman image, aligned to sizeof(uint32_t),
* as pixman does. Returns -1 on integer overflow.
*/
static inline int qemu_pixman_stride(pixman_format_code_t format, int width)
{
int stride;
if (unlikely(__builtin_mul_overflow(width, PIXMAN_FORMAT_BPP(format),
&stride)) ||
unlikely(__builtin_add_overflow(stride, 31, &stride))) {
return -1;
}
return (stride / 32) * sizeof(uint32_t);
}
/*
* Compute stride and buffer size for a pixman image.
* If *rowstride_bytes is 0, compute it from format and width
* (aligned to sizeof(uint32_t), as pixman does).
* Returns false on integer overflow.
*/
static inline bool qemu_pixman_image_calc_size(pixman_format_code_t format,
int width, int height,
int *rowstride_bytes,
size_t *buf_size)
{
if (!*rowstride_bytes) {
*rowstride_bytes = qemu_pixman_stride(format, width);
if (*rowstride_bytes < 0) {
return false;
}
}
return likely(!__builtin_mul_overflow((size_t)height,
(size_t)*rowstride_bytes, buf_size));
}
#endif /* QEMU_PIXMAN_HELPERS_H */

View File

@@ -8,6 +8,7 @@
#ifdef CONFIG_PIXMAN #ifdef CONFIG_PIXMAN
#include <pixman.h> #include <pixman.h>
#include "qemu-pixman-helpers.h"
#else #else
#include "pixman-minimal.h" #include "pixman-minimal.h"
#endif #endif

View File

@@ -320,12 +320,18 @@ qemu_pixman_image_new_shareable(pixman_image_t **image,
Error **errp) Error **errp)
{ {
ERRP_GUARD(); ERRP_GUARD();
size_t size = height * rowstride_bytes; size_t size;
void *bits = NULL; void *bits = NULL;
g_return_val_if_fail(image != NULL, false); g_return_val_if_fail(image != NULL, false);
g_return_val_if_fail(handle != NULL, false); g_return_val_if_fail(handle != NULL, false);
if (!qemu_pixman_image_calc_size(format, width, height,
&rowstride_bytes, &size)) {
error_setg(errp, "Image dimensions overflow");
return false;
}
bits = qemu_pixman_shareable_alloc(name, size, handle, errp); bits = qemu_pixman_shareable_alloc(name, size, handle, errp);
if (!bits) { if (!bits) {
return false; return false;