Free input data pointer when rustbuffer_from_bytes throws#39
Open
LautaroPetaccio wants to merge 1 commit intolivekit:mainfrom
Open
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
UniffiRustBufferValue.allocateWithBytesonly frees its input-data pointer on the happy path. Move thefreePointercall into afinallyblock so the allocation is reclaimed even when the underlying Rust call throws.Why this is a leak
The current shape is:
ffi-rs'screatePointerfor aU8Arraydoes (inutils/dataprocess.rs):It heap-allocates a wrapper
Box<*mut u8>that must be reclaimed viafreePointer(the pointeeUint8Arrayitself is V8-managed and GC'd separately). BecauseJsExternaldoes not free its underlying memory on garbage collection — per ffi-rs's documented contract — the wrapper Box stays live until the matchingfreePointerruns.If
uniffiCaller.rustCallthrows — which is exactly what happens when Rust returnsCALL_ERROR, panics, or any lower-level exception bubbles up — control skips over thefreePointercall. The pointer leaks. The leak is small per occurrence but is deterministic on every error path that flows through this allocator (notably anywhereallocateEmptyor buffer construction is used during error-buffer round-tripping).How the fix works
Wrap the
rustCalland theUniffiRustBufferValueconstruction intry { ... } finally { freePointer(...) }. ThefreePointerarguments are unchanged — sameparamsType, sameparamsValue,PointerType.RsPointer— so the success path frees the wrapper exactly as before, and the failure path now does too. ffi-rs's free path forU8Array(free_rs_pointer_memory) justBox::from_raws the wrapper, leaving the underlying buffer untouched, so this is safe to run unconditionally.