From bed00efc9f9447a1796891ae2de76f0edb28268b Mon Sep 17 00:00:00 2001 From: Iuri de Silvio Date: Mon, 18 May 2026 10:03:28 +0200 Subject: [PATCH] Release RsvgHandle and partial surface on SVG decode error paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Image::loadSVGFromBuffer() did not check the return value of rsvg_handle_get_intrinsic_size_in_pixels(), so when the SVG parsed but had no intrinsic size (no width/height attribute and no element that implies one), d_width/d_height were read uninitialized and the function returned CAIRO_STATUS_READ_ERROR with _rsvg still attached and _is_svg still set. The orphaned RsvgHandle then survived until the JS Image wrapper was finalized — visible as ~4 KiB/iter of steady RSS growth across repeated loadImage() of size-less SVG input. Image::renderSVGToSurface() had the inverse shape: each error branch unreffed _rsvg but did not null it, and left _surface (and the cairo context on later branches) live. Image::surface() then ran a redundant g_object_unref(_rsvg) on the error path, double-touching state that may or may not have already been released. Fix: - initialize d_width/d_height and gate the assignment on the return value - on the missing-dimensions error path in loadSVGFromBuffer, unref and null _rsvg and clear _is_svg before returning - on each error branch in renderSVGToSurface, destroy the cairo context if created, destroy and null _surface, unref and null _rsvg, clear _is_svg - drop the now-redundant g_object_unref(_rsvg) in Image::surface()'s renderSVGToSurface failure path Fixes #2584 --- src/Image.cc | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/Image.cc b/src/Image.cc index 06dd564ae..83c128e47 100644 --- a/src/Image.cc +++ b/src/Image.cc @@ -405,7 +405,6 @@ cairo_surface_t *Image::surface() { cairo_status_t status = renderSVGToSurface(); if (status != CAIRO_STATUS_SUCCESS) { - g_object_unref(_rsvg); Napi::Error::New(env, cairo_status_to_string(status)).ThrowAsJavaScriptException(); return NULL; @@ -1473,16 +1472,19 @@ Image::loadSVGFromBuffer(uint8_t *buf, unsigned len) { return CAIRO_STATUS_READ_ERROR; } - double d_width; - double d_height; + double d_width = 0; + double d_height = 0; - rsvg_handle_get_intrinsic_size_in_pixels(_rsvg, &d_width, &d_height); + gboolean has_size = rsvg_handle_get_intrinsic_size_in_pixels(_rsvg, &d_width, &d_height); - width = naturalWidth = d_width; - height = naturalHeight = d_height; + width = naturalWidth = has_size ? d_width : 0; + height = naturalHeight = has_size ? d_height : 0; if (width <= 0 || height <= 0) { this->errorInfo.set("Width and height must be set on the svg element"); + g_object_unref(_rsvg); + _rsvg = NULL; + _is_svg = false; return CAIRO_STATUS_READ_ERROR; } @@ -1501,6 +1503,10 @@ Image::renderSVGToSurface() { status = cairo_surface_status(_surface); if (status != CAIRO_STATUS_SUCCESS) { g_object_unref(_rsvg); + _rsvg = NULL; + cairo_surface_destroy(_surface); + _surface = NULL; + _is_svg = false; return status; } @@ -1508,6 +1514,11 @@ Image::renderSVGToSurface() { status = cairo_status(cr); if (status != CAIRO_STATUS_SUCCESS) { g_object_unref(_rsvg); + _rsvg = NULL; + cairo_destroy(cr); + cairo_surface_destroy(_surface); + _surface = NULL; + _is_svg = false; return status; } @@ -1520,7 +1531,11 @@ Image::renderSVGToSurface() { gboolean render_ok = rsvg_handle_render_document(_rsvg, cr, &viewport, nullptr); if (!render_ok) { g_object_unref(_rsvg); + _rsvg = NULL; cairo_destroy(cr); + cairo_surface_destroy(_surface); + _surface = NULL; + _is_svg = false; return CAIRO_STATUS_READ_ERROR; // or WRITE? }