diff --git a/kitty/graphics.c b/kitty/graphics.c index 3e0dd6ed9..b72dbbcf1 100644 --- a/kitty/graphics.c +++ b/kitty/graphics.c @@ -130,7 +130,18 @@ img_by_internal_id(GraphicsManager *self, size_t id) { static char add_response[512] = {0}; static bool has_add_respose = false; -#define ABRT(...) { snprintf(add_response, (sizeof(add_response)/sizeof(add_response[0])) - 1, __VA_ARGS__); has_add_respose = true; goto err; } +static inline void +set_add_response(const char *code, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + size_t sz = sizeof(add_response)/sizeof(add_response[0]); + int num = snprintf(add_response, sz, "%s:", code); + vsnprintf(add_response + num, sz - num, fmt, args); + va_end(args); + has_add_respose = true; +} + +#define ABRT(code, ...) { set_add_response(#code, __VA_ARGS__); goto err; } static inline bool inflate_zlib(GraphicsManager UNUSED *self, Image *img, uint8_t *buf, size_t bufsz) { bool ok = false; @@ -145,9 +156,9 @@ inflate_zlib(GraphicsManager UNUSED *self, Image *img, uint8_t *buf, size_t bufs z.avail_out = img->load_data.data_sz; z.next_out = decompressed; int ret; - if ((ret = inflateInit(&z)) != Z_OK) ABRT("Failed to initialize inflate with error code: %d", ret); - if ((ret = inflate(&z, Z_FINISH)) != Z_STREAM_END) ABRT("Failed to inflate image data with error code: %d", ret); - if (z.avail_out) ABRT("Failed to inflate image data with error code: %d", ret); + if ((ret = inflateInit(&z)) != Z_OK) ABRT(ENOMEM, "Failed to initialize inflate with error code: %d", ret); + if ((ret = inflate(&z, Z_FINISH)) != Z_STREAM_END) ABRT(EINVAL, "Failed to inflate image data with error code: %d", ret); + if (z.avail_out) ABRT(EINVAL, "Image data size post inflation does not match expected size"); free_load_data(&img->load_data); img->load_data.buf_capacity = img->load_data.data_sz; img->load_data.buf = decompressed; @@ -173,7 +184,6 @@ read_png_from_buffer(png_structp png, png_bytep out, png_size_t length) { static inline bool inflate_png(GraphicsManager UNUSED *self, Image *img, uint8_t *buf, size_t bufsz) { -#define RE(...) { REPORT_ERROR(__VA_ARGS__); goto err; } bool ok = false; uint8_t *decompressed = NULL; png_structp png = NULL; @@ -182,11 +192,11 @@ inflate_png(GraphicsManager UNUSED *self, Image *img, uint8_t *buf, size_t bufsz struct fake_file f = {.buf = buf, .sz = bufsz}; png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png) RE("%s", "Failed to create PNG read structure"); + if (!png) ABRT(ENOMEM, "Failed to create PNG read structure"); info = png_create_info_struct(png); - if (!info) RE("%s", "Failed to create PNG info structure"); + if (!info) ABRT(ENOMEM, "Failed to create PNG info structure"); - if(setjmp(png_jmpbuf(png))) RE("%s", "Invalid PNG data"); + if(setjmp(png_jmpbuf(png))) ABRT(EINVAL, "Invalid PNG data"); png_set_read_fn(png, &f, read_png_from_buffer); png_read_info(png, info); @@ -214,9 +224,9 @@ inflate_png(GraphicsManager UNUSED *self, Image *img, uint8_t *buf, size_t bufsz int rowbytes = png_get_rowbytes(png, info); size_t sz = rowbytes * height * sizeof(png_byte); decompressed = malloc(sz + 16); - if (decompressed == NULL) RE("%s", "Out of memory allocating decompression buffer for PNG"); + if (decompressed == NULL) ABRT(ENOMEM, "Out of memory allocating decompression buffer for PNG"); row_pointers = malloc(height * sizeof(png_bytep)); - if (row_pointers == NULL) RE("%s", "Out of memory allocating row_pointers buffer for PNG"); + if (row_pointers == NULL) ABRT(ENOMEM, "Out of memory allocating row_pointers buffer for PNG"); for (int i = 0; i < height; i++) row_pointers[height - 1 - i] = decompressed + i * rowbytes; png_read_image(png, row_pointers); @@ -231,7 +241,6 @@ err: if (!ok) free(decompressed); free(row_pointers); return ok; -#undef RE } #undef ABRT @@ -242,7 +251,7 @@ add_trim_predicate(Image *img) { static bool handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload) { -#define ABRT(...) { snprintf(add_response, (sizeof(add_response)/sizeof(add_response[0])) - 1, __VA_ARGS__); has_add_respose = true; return false; } +#define ABRT(code, ...) { set_add_response(#code, __VA_ARGS__); return false; } bool existing, init_img = true; Image *img; unsigned char tt = g->transmission_type ? g->transmission_type : 'd'; @@ -251,7 +260,8 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ if (tt == 'd' && (g->more && self->loading_image)) init_img = false; if (init_img) { size_t sz = g->data_width * g->data_height; - if (!sz) return false; // ignore images with zero size + if (!sz) ABRT(EINVAL, "Zero width/height not allowed"); + if (g->data_width > 10000 || g->data_height > 10000) ABRT(EINVAL, "Image too large"); remove_images(self, add_trim_predicate); img = find_or_create_image(self, g->id, &existing); if (existing) { @@ -273,7 +283,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ img->load_data.is_4byte_aligned = fmt == RGBA || (img->width % 4 == 0); break; default: - ABRT("Unknown image format: %u", fmt); + ABRT(EINVAL, "Unknown image format: %u", fmt); } img->load_data.data_sz = sz; if (tt == 'd') { @@ -287,14 +297,14 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ img = img_by_internal_id(self, self->loading_image); if (img == NULL) { self->loading_image = 0; - ABRT("%s", "More payload loading refers to non-existent image"); + ABRT(EILSEQ, "More payload loading refers to non-existent image"); } } int fd; switch(tt) { case 'd': // direct if (g->payload_sz >= img->load_data.buf_capacity - img->load_data.buf_used) { - ABRT("%s", "Too much data transmitted"); + ABRT(EFBIG, "Too much data transmitted"); } memcpy(img->load_data.buf + img->load_data.buf_used, payload, g->payload_sz); img->load_data.buf_used += g->payload_sz; @@ -306,7 +316,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ if (tt == 's') fd = shm_open((const char*)payload, O_RDONLY, 0); else fd = open((const char*)payload, O_CLOEXEC | O_RDONLY); if (fd == -1) { - ABRT("Failed to open file for graphics transmission with error: [%d] %s", errno, strerror(errno)); + ABRT(EBADF, "Failed to open file for graphics transmission with error: [%d] %s", errno, strerror(errno)); } img->load_data.fd = fd; img->data_loaded = mmap_img_file(self, img); @@ -314,7 +324,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ else if (tt == 's') shm_unlink((const char*)payload); break; default: - ABRT("Unknown transmission type: %c", g->transmission_type); + ABRT(EINVAL, "Unknown transmission type: %c", g->transmission_type); } if (!img->data_loaded) return false; bool needs_processing = g->compressed || fmt == PNG; @@ -331,7 +341,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ case 0: break; default: - ABRT("Unknown image compression: %c", g->compressed); + ABRT(EINVAL, "Unknown image compression: %c", g->compressed); img->data_loaded = false; return false; } switch(fmt) { @@ -346,18 +356,18 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ #undef IB img->load_data.data = img->load_data.buf; if (img->load_data.buf_used < img->load_data.data_sz) { - ABRT("Insufficient image data: %zu < %zu", img->load_data.buf_used, img->load_data.data_sz); + ABRT(ENODATA, "Insufficient image data: %zu < %zu", img->load_data.buf_used, img->load_data.data_sz); img->data_loaded = false; } } else { if (tt == 'd') { if (img->load_data.buf_used < img->load_data.data_sz) { - ABRT("Insufficient image data: %zu < %zu", img->load_data.buf_used, img->load_data.data_sz); + ABRT(ENODATA, "Insufficient image data: %zu < %zu", img->load_data.buf_used, img->load_data.data_sz); img->data_loaded = false; } else img->load_data.data = img->load_data.buf; } else { if (img->load_data.mapped_file_sz < img->load_data.data_sz) { - ABRT("Insufficient image data: %zu < %zu", img->load_data.mapped_file_sz, img->load_data.data_sz); + ABRT(ENODATA, "Insufficient image data: %zu < %zu", img->load_data.mapped_file_sz, img->load_data.data_sz); img->data_loaded = false; } else img->load_data.data = img->load_data.mapped_file; } diff --git a/kitty/parser.c b/kitty/parser.c index 137d86ea3..98b245c01 100644 --- a/kitty/parser.c +++ b/kitty/parser.c @@ -665,10 +665,6 @@ parse_graphics_code(Screen *screen, PyObject UNUSED *dump_callback) { default: break; } - if (g.data_width > 10000 || g.data_height > 10000) { - REPORT_ERROR("Image too large"); - return; - } #define A(x) #x, g.x #define U(x) #x, (unsigned int)(g.x) #define I(x) #x, (int)(g.x) diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index 54c18514f..852715c28 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -230,5 +230,3 @@ class TestParser(BaseTest): e('s=', 'Malformed graphics control block, expecting an integer value') e('s==', 'Malformed graphics control block, expecting an integer value') e('s=1=', 'Malformed graphics control block, expecting a comma or semi-colon after a value, found: 0x3d') - e('s=20000', 'Image too large') - e('v=20000', 'Image too large')