This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git
The following commit(s) were added to refs/heads/main by this push:
new 70d8190350 Fix memory reservation leak in Buffer::into_vec (#11878)
70d8190350 is described below
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