Evian-Zhang commented on issue #10281:
URL: https://github.com/apache/arrow-rs/issues/10281#issuecomment-5013800171

   @Rich-T-kid The reproduction PoCs are listed below.
   
   All PoCs' `Cargo.toml`:
   
   ```toml
   [dependencies]
   arrow-buffer = { version = "59.1.0", features = ["pool"] }
   ```
   
   **The following code is originally generated by LLM (since I'm not so 
familiar with arrow-buffer's APIs). I tested it locally and reviewed each line 
and modified the code to make it concise. I promise that I'm not just 
copy-pasting LLM code without checking.**
   
   # Issue 1
   
   ```rust
   use arrow_buffer::buffer::MutableBuffer;
   use arrow_buffer::{MemoryPool, MemoryReservation};
   use std::panic::{AssertUnwindSafe, catch_unwind};
   
   #[derive(Debug)]
   struct PanicReservation;
   
   impl MemoryReservation for PanicReservation {
       fn size(&self) -> usize {
           0
       }
   
       fn resize(&mut self, _new_size: usize) {
           panic!("Issue 1: panic in resize to poison the mutex");
       }
   }
   
   #[derive(Debug)]
   struct PanicPool;
   
   impl MemoryPool for PanicPool {
       fn reserve(&self, _size: usize) -> Box<dyn MemoryReservation> {
           Box::new(PanicReservation)
       }
   
       fn available(&self) -> isize {
           isize::MAX
       }
   
       fn used(&self) -> usize {
           0
       }
   
       fn capacity(&self) -> usize {
           usize::MAX
       }
   }
   
   fn main() {
       let pool = PanicPool;
       let mut buf = MutableBuffer::with_capacity(128);
       buf.resize(64, 0);
       buf.claim(&pool);
   
       let result = catch_unwind(AssertUnwindSafe(|| {
           buf.resize(100, 0);
       }));
   
       let _ = catch_unwind(AssertUnwindSafe(|| {
           let _b: arrow_buffer::Buffer = buf.into();
       }));
   }
   ```
   
   Run this code will trigger:
   
   ```plaintxt
   free(): double free detected in tcache 2
   [1]    1071684 IOT instruction (core dumped)
   ```
   
   # Issue 2
   
   ```rust
   use arrow_buffer::Buffer;
   use arrow_buffer::{MemoryPool, MemoryReservation};
   use std::panic::{AssertUnwindSafe, catch_unwind};
   
   #[derive(Debug)]
   struct PanicReservation {
       panicked: bool,
   }
   
   impl MemoryReservation for PanicReservation {
       fn size(&self) -> usize {
           0
       }
   
       fn resize(&mut self, _new_size: usize) {
           if !self.panicked {
               self.panicked = true;
               panic!("Issue 2: panic in resize during try_realloc → Bytes.ptr 
updated but Buffer.ptr not");
           }
       }
   }
   
   #[derive(Debug)]
   struct PanicPool;
   
   impl MemoryPool for PanicPool {
       fn reserve(&self, _size: usize) -> Box<dyn MemoryReservation> {
           Box::new(PanicReservation { panicked: false })
       }
   
       fn available(&self) -> isize {
           isize::MAX
       }
   
       fn used(&self) -> usize {
           0
       }
   
       fn capacity(&self) -> usize {
           usize::MAX
       }
   }
   
   fn main() {
       let pool = PanicPool;
   
       // Use from_slice_ref: capacity is rounded up to 64 (via 
MutableBuffer::with_capacity)
       // while length is the actual slice length. This gives us excess 
capacity to shrink.
       let data: &[u8] = &[0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22];
       let mut buf = Buffer::from_slice_ref(data);
       buf.claim(&pool);
   
       let result = catch_unwind(AssertUnwindSafe(|| {
           buf.shrink_to_fit();
       }));
   
       let slice = buf.as_slice();
       println!("as_slice() returned {} bytes", slice.len());
       if !slice.is_empty() {
           println!(
               "first bytes: {:02x?}",
               &slice[..slice.len().min(8)]
           );
       }
   }
   ```
   
   Running this code will print bytes that are not initialized by the code. In 
my local environment, it prints out `7d, 64, 23, b6, 05, 00, 00, 00`.
   
   # Issue 3
   
   ```rust
   use arrow_buffer::builder::BooleanBufferBuilder;
   use std::panic::{AssertUnwindSafe, catch_unwind};
   
   struct PanicIter {
       remaining: usize,
       yielded: usize,
       panic_after: usize,
   }
   
   impl Iterator for PanicIter {
       type Item = bool;
   
       fn next(&mut self) -> Option<Self::Item> {
           if self.remaining == 0 {
               return None;
           }
           self.remaining -= 1;
           self.yielded += 1;
           if self.yielded > self.panic_after {
               panic!("Issue 3: panic during iter.next() after set_len already 
ran");
           }
           Some(true)
       }
   
       fn size_hint(&self) -> (usize, Option<usize>) {
           (self.remaining, Some(self.remaining))
       }
   }
   
   fn main() {
       let mut builder = BooleanBufferBuilder::new(0);
   
       // Iterator: reports 64 items, yields 8, then panics on the 9th
       let iter = PanicIter {
           remaining: 64,
           yielded: 0,
           panic_after: 8,
       };
   
       let result = catch_unwind(AssertUnwindSafe(|| unsafe {
           builder.extend_trusted_len(iter);
       }));
   
       println!(
           "builder.len() = {} (logical len — NOT updated by 
extend_trusted_len)",
           builder.len()
       );
       println!(
           "builder.as_slice().len() = {} (internal MutableBuffer len — WAS 
expanded by set_len)",
           builder.as_slice().len()
       );
   
       let slice = builder.as_slice();
   
       println!(
           "raw bytes: {:02x?}",
           &slice[..slice.len().min(16)]
       );
   }
   ```
   
   This also prints uninitialized values.
   
   You can run `cargo +nightly miri run` to test both issue 2 and 3 (since 1 
directly crashed, which is obvious) to further check the issues.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to