From 756afaf8401ea79da6f3f7535b77e2bcd6649f0c Mon Sep 17 00:00:00 2001 From: LautaroPetaccio Date: Tue, 5 May 2026 11:53:30 -0300 Subject: [PATCH] Free input data pointer when rustbuffer_from_bytes throws UniffiRustBufferValue.allocateWithBytes calls createPointer to wrap the input bytes, hands the inner pointer to ffi_rustbuffer_from_bytes through uniffiCaller.rustCall, and only then calls freePointer. If the rustCall throws (panic, CALL_ERROR with a lifted error, or any other exception), control jumps over the freePointer call and the wrapper Box allocated by ffi-rs is leaked. The leak is small per occurrence but is guaranteed on every failure path that goes through this allocator. Move the freePointer call into a finally block so the pointer is always released, regardless of how the rustCall returns. --- templates/sys.ts | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/templates/sys.ts b/templates/sys.ts index cc0ca1a..3f4c240 100644 --- a/templates/sys.ts +++ b/templates/sys.ts @@ -317,24 +317,26 @@ export class UniffiRustBufferValue { paramsValue: [bytes], }); - const rustBuffer = uniffiCaller.rustCall( - (callStatus) => { - return FFI_DYNAMIC_LIB.{{ci.ffi_rustbuffer_from_bytes().name()}}([ - // TODO: figure out why this is necessary. - { data: unwrapPointer([dataPointer])[0], len: bytes.byteLength }, - callStatus, - ]); - }, - /*liftString:*/ {{ &Type::String | typescript_ffi_converter_name }}.lift, - ); - - freePointer({ - paramsType: [arrayConstructor({ type: DataType.U8Array, length: bytes.byteLength })], - paramsValue: [dataPointer], - pointerType: PointerType.RsPointer - }); - - return new UniffiRustBufferValue(rustBuffer); + try { + const rustBuffer = uniffiCaller.rustCall( + (callStatus) => { + return FFI_DYNAMIC_LIB.{{ci.ffi_rustbuffer_from_bytes().name()}}([ + // TODO: figure out why this is necessary. + { data: unwrapPointer([dataPointer])[0], len: bytes.byteLength }, + callStatus, + ]); + }, + /*liftString:*/ {{ &Type::String | typescript_ffi_converter_name }}.lift, + ); + + return new UniffiRustBufferValue(rustBuffer); + } finally { + freePointer({ + paramsType: [arrayConstructor({ type: DataType.U8Array, length: bytes.byteLength })], + paramsValue: [dataPointer], + pointerType: PointerType.RsPointer + }); + } } static allocateEmpty() {