This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-11878-68f3f54ebc7171c566a198305a65485087d5f8d4 in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
commit 70d8190350ffb2fd3a34c92d8844d505eaf0af75 Author: timmyb32r <[email protected]> AuthorDate: Wed Oct 28 23:04:49 2026 +0000 Fix memory reservation leak in Buffer::into_vec (#11878) ### Which issue does this PR close? None — standalone bug fix. ### Rationale for this change `Buffer::into_vec` calls `mem::forget(bytes)` to transfer ownership of the allocation to a `Vec`. With the `pool` feature enabled, this also leaks the memory reservation: its destructor never runs, so the pool continues reporting the allocation as used even after the vector is dropped. ### What changes are included in this PR? Release the memory reservation before forgetting `Bytes` and transferring the allocation to the vector. ### Are these changes tested? Added a regression test verifying that: - Successful conversion releases the reservation and preserves the allocation and its contents. - Failed conversion of a shared buffer keeps the reservation intact. The test fails before the fix and passes afterward. Validation passed: - `cargo test -p arrow-buffer` - `cargo test -p arrow-buffer --all-features` - `cargo test --workspace` - `cargo clippy --workspace --all-targets --all-features -- -D warnings` - Formatting and spelling checks. ### Are there any significant user-facing changes? With the `pool` feature enabled, converting a buffer into a vector now releases its memory reservation. ### AI assistance Codex identified the issue, generated the fix and regression test, ran validation, and drafted this PR description. --- arrow-buffer/src/buffer/immutable.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/arrow-buffer/src/buffer/immutable.rs b/arrow-buffer/src/buffer/immutable.rs index 658d045f06..318fd3504e 100644 --- a/arrow-buffer/src/buffer/immutable.rs +++ b/arrow-buffer/src/buffer/immutable.rs @@ -440,6 +440,9 @@ impl Buffer { /// /// This can be used to reuse / mutate the underlying data. /// + /// With the `pool` feature enabled, a successful conversion releases the buffer's + /// memory reservation. The returned `Vec` is not tracked by the memory pool. + /// /// # Errors /// /// Returns `Err(self)` if @@ -469,6 +472,8 @@ impl Buffer { Arc::try_unwrap(self.data) .map(|bytes| unsafe { let ptr = bytes.ptr().as_ptr().cast(); + #[cfg(feature = "pool")] + drop(bytes.reservation.take()); std::mem::forget(bytes); // Safety // Verified that bytes layout matches that of Vec @@ -660,6 +665,8 @@ impl<T: ArrowNativeType> FromIterator<T> for Buffer { #[cfg(test)] mod tests { use crate::i256; + #[cfg(feature = "pool")] + use crate::pool::{MemoryPool, TrackingMemoryPool}; use std::panic::{RefUnwindSafe, UnwindSafe}; use std::thread; @@ -962,6 +969,29 @@ mod tests { buffer.slice_with_length(2, usize::MAX); } + #[test] + #[cfg(feature = "pool")] + fn test_into_vec_releases_reservation() { + let pool = TrackingMemoryPool::default(); + let buffer = Buffer::from_vec(vec![1_u8, 2, 3]); + buffer.claim(&pool); + assert_eq!(pool.used(), 3); + + let shared = buffer.clone(); + let buffer = buffer.into_vec::<u8>().unwrap_err(); + assert_eq!(pool.used(), 3); + drop(shared); + + let ptr = buffer.as_ptr(); + let values = buffer.into_vec::<u8>().unwrap(); + assert_eq!(values.as_ptr(), ptr); + assert_eq!(values, [1, 2, 3]); + assert_eq!(pool.used(), 0); + + drop(values); + assert_eq!(pool.used(), 0); + } + #[test] fn test_vec_interop() { // Test empty vec
