Graphics: Disallow PNGs of size greater than MAX_IMAGE_DIMENSION to match behavior with loading RGB data

This commit is contained in:
Kovid Goyal
2025-11-14 07:58:04 +05:30
parent 9bc29a7fa6
commit d4633bf5f9
3 changed files with 10 additions and 6 deletions

View File

@@ -23,6 +23,7 @@
#include "png-reader.h" #include "png-reader.h"
PyTypeObject GraphicsManager_Type; PyTypeObject GraphicsManager_Type;
#define MAX_IMAGE_DIMENSION 10000u
#define DEFAULT_STORAGE_LIMIT 320u * (1024u * 1024u) #define DEFAULT_STORAGE_LIMIT 320u * (1024u * 1024u)
#define REPORT_ERROR(...) { log_error(__VA_ARGS__); } #define REPORT_ERROR(...) { log_error(__VA_ARGS__); }
#define RAII_CoalescedFrameData(name, initializer) __attribute__((cleanup(cfd_free))) CoalescedFrameData name = initializer #define RAII_CoalescedFrameData(name, initializer) __attribute__((cleanup(cfd_free))) CoalescedFrameData name = initializer
@@ -380,7 +381,7 @@ png_error_handler(png_read_data *d UNUSED, const char *code, const char *msg) {
static bool static bool
inflate_png(LoadData *load_data, uint8_t *buf, size_t bufsz) { inflate_png(LoadData *load_data, uint8_t *buf, size_t bufsz) {
png_read_data d = {.err_handler=png_error_handler}; png_read_data d = {.err_handler=png_error_handler};
inflate_png_inner(&d, buf, bufsz); inflate_png_inner(&d, buf, bufsz, MAX_IMAGE_DIMENSION);
if (d.ok) { if (d.ok) {
free_load_data(load_data); free_load_data(load_data);
load_data->buf = d.decompressed; load_data->buf = d.decompressed;
@@ -415,7 +416,7 @@ print_png_read_error(png_read_data *d, const char *code, const char* msg) {
bool bool
png_from_data(void *png_data, size_t png_data_sz, const char *path_for_error_messages, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz) { png_from_data(void *png_data, size_t png_data_sz, const char *path_for_error_messages, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz) {
png_read_data d = {.err_handler=print_png_read_error}; png_read_data d = {.err_handler=print_png_read_error};
inflate_png_inner(&d, png_data, png_data_sz); inflate_png_inner(&d, png_data, png_data_sz, MAX_IMAGE_DIMENSION);
if (!d.ok) { if (!d.ok) {
log_error("Failed to decode PNG image at: %s with error: %s", path_for_error_messages, d.error.used > 0 ? d.error.buf : ""); log_error("Failed to decode PNG image at: %s with error: %s", path_for_error_messages, d.error.used > 0 ? d.error.buf : "");
free(d.decompressed); free(d.row_pointers); free(d.error.buf); free(d.decompressed); free(d.row_pointers); free(d.error.buf);
@@ -693,7 +694,6 @@ initialize_load_data(GraphicsManager *self, const GraphicsCommand *g, Image *img
tt = g->transmission_type ? g->transmission_type : 'd'; \ tt = g->transmission_type ? g->transmission_type : 'd'; \
fmt = g->format ? g->format : RGBA; \ fmt = g->format ? g->format : RGBA; \
} }
#define MAX_IMAGE_DIMENSION 10000u
static void static void
upload_to_gpu(GraphicsManager *self, Image *img, const bool is_opaque, const bool is_4byte_aligned, const uint8_t *data) { upload_to_gpu(GraphicsManager *self, Image *img, const bool is_opaque, const bool is_4byte_aligned, const uint8_t *data) {

View File

@@ -47,7 +47,7 @@ read_png_warn_handler(png_structp UNUSED png_ptr, png_const_charp msg) {
#define ABRT(code, msg) { if(d->err_handler) d->err_handler(d, #code, msg); goto err; } #define ABRT(code, msg) { if(d->err_handler) d->err_handler(d, #code, msg); goto err; }
void void
inflate_png_inner(png_read_data *d, const uint8_t *buf, size_t bufsz) { inflate_png_inner(png_read_data *d, const uint8_t *buf, size_t bufsz, int max_image_dimension) {
struct fake_file f = {.buf = buf, .sz = bufsz}; struct fake_file f = {.buf = buf, .sz = bufsz};
png_structp png = NULL; png_structp png = NULL;
png_infop info = NULL; png_infop info = NULL;
@@ -64,6 +64,10 @@ inflate_png_inner(png_read_data *d, const uint8_t *buf, size_t bufsz) {
png_byte color_type, bit_depth; png_byte color_type, bit_depth;
d->width = png_get_image_width(png, info); d->width = png_get_image_width(png, info);
d->height = png_get_image_height(png, info); d->height = png_get_image_height(png, info);
// libpng uses too much memory for overly large images
if (d->width > max_image_dimension || d->height > max_image_dimension) {
ABRT(ENOMEM, "PNG image is too large");
}
color_type = png_get_color_type(png, info); color_type = png_get_color_type(png, info);
bit_depth = png_get_bit_depth(png, info); bit_depth = png_get_bit_depth(png, info);
double image_gamma; double image_gamma;
@@ -208,7 +212,7 @@ load_png_data(PyObject *self UNUSED, PyObject *args) {
const char *data; const char *data;
if (!PyArg_ParseTuple(args, "s#", &data, &sz)) return NULL; if (!PyArg_ParseTuple(args, "s#", &data, &sz)) return NULL;
png_read_data d = {.err_handler=png_error_handler}; png_read_data d = {.err_handler=png_error_handler};
inflate_png_inner(&d, (const uint8_t*)data, sz); inflate_png_inner(&d, (const uint8_t*)data, sz, 10000);
PyObject *ans = NULL; PyObject *ans = NULL;
if (d.ok && !PyErr_Occurred()) { if (d.ok && !PyErr_Occurred()) {
ans = Py_BuildValue("y#ii", d.decompressed, (int)d.sz, d.width, d.height); ans = Py_BuildValue("y#ii", d.decompressed, (int)d.sz, d.width, d.height);

View File

@@ -27,5 +27,5 @@ typedef struct png_read_data {
} error; } error;
} png_read_data; } png_read_data;
void inflate_png_inner(png_read_data *d, const uint8_t *buf, size_t bufsz); void inflate_png_inner(png_read_data *d, const uint8_t *buf, size_t bufsz, int max_image_dimension);
const char* png_from_32bit_rgba(uint32_t *data, size_t width, size_t height, size_t *out_size, bool flip_vertically); const char* png_from_32bit_rgba(uint32_t *data, size_t width, size_t height, size_t *out_size, bool flip_vertically);