From f9c4311808e1ab680bb125ecaa3a91ed3064648f Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Wed, 28 May 2025 20:00:32 +0200 Subject: [PATCH 1/2] Fix mem leak in `Source::newFromMemory()` --- src/Source.php | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/Source.php b/src/Source.php index 9a028a7..6294cc0 100644 --- a/src/Source.php +++ b/src/Source.php @@ -12,6 +12,16 @@ class Source extends Connection */ public \FFI\CData $pointer; + /** + * Pointer to the underlying memory buffer when using + * @see Source::newFromMemory() + * + * Must be freed when no longer needed. + * + * @internal + */ + public ?\FFI\CData $memory = null; + public function __construct(\FFI\CData $pointer) { $this->pointer = FFI::vips()->cast(FFI::ctypes('VipsSource'), $pointer); @@ -64,17 +74,27 @@ public static function newFromFile(string $filename): self */ public static function newFromMemory(string $data): self { - # we need to set the memory to a copy of the data that vips_lib - # can own and free + # we need to set the memory to a copy of the data $n = strlen($data); $memory = FFI::vips()->new("char[$n]", false, true); \FFI::memcpy($memory, $data, $n); $pointer = FFI::vips()->vips_source_new_from_memory($memory, $n); if ($pointer === null) { + \FFI::free($memory); throw new Exception("can't create source from memory"); } - return new self($pointer); + $source = new self($pointer); + $source->memory = $memory; + return $source; + } + + public function __destruct() + { + if ($this->memory !== null) { + \FFI::free($this->memory); + } + parent::__destruct(); } } From f14e31ac2a51f22994f2c571d9691f7dd9bfe9d8 Mon Sep 17 00:00:00 2001 From: Kleis Auke Wolthuizen Date: Thu, 29 May 2025 10:57:44 +0200 Subject: [PATCH 2/2] Simplify --- src/FFI.php | 5 +++-- src/Source.php | 32 +++++++------------------------- 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/src/FFI.php b/src/FFI.php index f259f7b..ff7d689 100644 --- a/src/FFI.php +++ b/src/FFI.php @@ -539,6 +539,8 @@ private static function init(): void typedef void (*FreeFn)(void* a); void vips_value_set_blob (GValue* value, FreeFn free_fn, void* data, size_t length); +void* vips_blob_copy (const void *data, size_t length); +void vips_area_unref (void *area); const char* vips_value_get_ref_string (const GValue* value, size_t* length); @@ -760,8 +762,7 @@ private static function init(): void VipsSource* vips_source_new_from_descriptor (int descriptor); VipsSource* vips_source_new_from_file (const char* filename); -VipsSource* vips_source_new_from_memory (const void* data, - size_t size); +VipsSource* vips_source_new_from_blob (void* blob); typedef struct _VipsSourceCustom { VipsSource parent_object; diff --git a/src/Source.php b/src/Source.php index 6294cc0..5afdbb0 100644 --- a/src/Source.php +++ b/src/Source.php @@ -12,16 +12,6 @@ class Source extends Connection */ public \FFI\CData $pointer; - /** - * Pointer to the underlying memory buffer when using - * @see Source::newFromMemory() - * - * Must be freed when no longer needed. - * - * @internal - */ - public ?\FFI\CData $memory = null; - public function __construct(\FFI\CData $pointer) { $this->pointer = FFI::vips()->cast(FFI::ctypes('VipsSource'), $pointer); @@ -74,27 +64,19 @@ public static function newFromFile(string $filename): self */ public static function newFromMemory(string $data): self { - # we need to set the memory to a copy of the data - $n = strlen($data); - $memory = FFI::vips()->new("char[$n]", false, true); - \FFI::memcpy($memory, $data, $n); - $pointer = FFI::vips()->vips_source_new_from_memory($memory, $n); + $blob = FFI::vips()->vips_blob_copy($data, strlen($data)); + if ($blob === null) { + throw new Exception("can't create source from memory"); + } + $pointer = FFI::vips()->vips_source_new_from_blob($blob); if ($pointer === null) { - \FFI::free($memory); + FFI::vips()->vips_area_unref($blob); throw new Exception("can't create source from memory"); } $source = new self($pointer); - $source->memory = $memory; + FFI::vips()->vips_area_unref($blob); return $source; } - - public function __destruct() - { - if ($this->memory !== null) { - \FFI::free($this->memory); - } - parent::__destruct(); - } }