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

Reply via email to