From ae169e236866401ab21073d44738221455dc4467 Mon Sep 17 00:00:00 2001 From: Iuri de Silvio Date: Mon, 18 May 2026 10:12:40 +0200 Subject: [PATCH] Free native resources on three rare decoder error paths - Image::loadGIFFromBuffer: delete[] the decode buffer before returning CAIRO_STATUS_READ_ERROR when neither a local nor a global color map exists. The full width*height*4 buffer was previously dropped. - Image::assignDataAsMime: capture the cairo_surface_set_mime_data return status; on non-success, reverse the AdjustExternalMemory(env, len) and free both mime_data (a copy of the JPEG buffer) and mime_closure, which cairo never takes ownership of in the failure case. - create_transparent_pattern: destroy mask_context and mask_surface before returning NULL when cairo_create reports an error status (cairo OOM). All three are real ownership bugs but sit behind error/OOM gates that aren't reachable in normal operation, so there's no measurable RSS slope to attach. Bundled because each is a one- or two-line fix in the same family. Fixes #2586 --- src/CanvasRenderingContext2d.cc | 2 ++ src/Image.cc | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index 9c5482074..1529b08b8 100644 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -358,6 +358,8 @@ create_transparent_pattern(cairo_pattern_t *source, float alpha) { height); cairo_t *mask_context = cairo_create(mask_surface); if (cairo_status(mask_context) != CAIRO_STATUS_SUCCESS) { + cairo_destroy(mask_context); + cairo_surface_destroy(mask_surface); return NULL; } cairo_set_source(mask_context, source); diff --git a/src/Image.cc b/src/Image.cc index 06dd564ae..f91f66685 100644 --- a/src/Image.cc +++ b/src/Image.cc @@ -617,6 +617,7 @@ Image::loadGIFFromBuffer(uint8_t *buf, unsigned len) { if (colormap == nullptr) { GIF_CLOSE_FILE(gif); + delete[] data; return CAIRO_STATUS_READ_ERROR; } @@ -1041,12 +1042,20 @@ Image::assignDataAsMime(uint8_t *data, int len, const char *mime_type) { Napi::MemoryManagement::AdjustExternalMemory(env, len); - return cairo_surface_set_mime_data(_surface + cairo_status_t status = cairo_surface_set_mime_data(_surface , mime_type , mime_data , len , clearMimeData , mime_closure); + + if (status) { + Napi::MemoryManagement::AdjustExternalMemory(env, -len); + free(mime_data); + free(mime_closure); + } + + return status; } /*