Skip to content

SliceVec::split_off Can Cause Double Free When T Implements Drop #6

@GeorgeAndrou

Description

@GeorgeAndrou

Hello,

We are @purseclab, and we are fuzzing Rust crates to identify memory violation bugs. While analyzing this crate, we discovered that SliceVec::split_off can duplicate ownership of elements of type T, potentially leading to a double-free memory violation when the generic type implements Drop.

The PoC below causes a double-free memory violation.

PoC:

#![forbid(unsafe_code)]

use arenavec::*;

#[derive(Debug)]
struct CustomType0(String);

impl Drop for CustomType0{
    fn drop(&mut self){
        println!("Attempting to free: {:?}", self.0.as_ptr());
    }
}

fn main() {
    let arena0 = arenavec::rc::Arena::init_capacity(arenavec::common::ArenaBacking::MemoryMap, 300).unwrap();
    let handle0 = arena0.inner();
    let mut sv1 = arenavec::common::SliceVec::with_capacity(handle0.clone(), 4);
    sv1.push(CustomType0(String::from("BUG")));
    sv1.push(CustomType0(String::from("BUG")));
    let _ = sv1.split_off(0);
    println!("exiting...")
}

Bug Description:

The method SliceVec::split_off in lines 407-412 copies the elements from the self object to the ret object.

arenavec/src/common.rs

Lines 400 to 415 in f931efb

pub fn split_off(&mut self, at: usize) -> Self
where
H: Clone,
{
let mut ret = Self::with_capacity(self.slice.handle.clone(), self.slice.len - at);
ret.slice.len = self.slice.len - at;
unsafe {
ptr::copy_nonoverlapping(
self.slice.ptr.as_ptr().add(at),
ret.slice.ptr.as_ptr(),
ret.len());
}
ret
}

If these elements are heap-allocated and implement the Drop trait, this copy duplicates their ownership. In the specific case where at == 0, split_off creates a new SliceVec (ret) with the same length and elements as self.

In the provided PoC, when split_off returns, ret is dropped, and its elements are deallocated. Later, when the original SliceVec (sv1) is dropped at the end of main, it attempts to deallocate the same elements again. This results in a double-free violation.

Output:

Attempting to free: 0x502000000010
Attempting to free: 0x502000000030
exiting...
Attempting to free: 0x502000000010
=================================================================
==4170119==ERROR: AddressSanitizer: attempting double-free on 0x502000000010 in thread T0:
    #0 0x55a232e8f116  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xca116) (BuildId: 69e067aa5f1d3584)
    #1 0x55a232eba556  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf5556) (BuildId: 69e067aa5f1d3584)
    #2 0x55a232ebe55a  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf955a) (BuildId: 69e067aa5f1d3584)
    #3 0x55a232ebdec9  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8ec9) (BuildId: 69e067aa5f1d3584)
    #4 0x55a232ebde9a  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8e9a) (BuildId: 69e067aa5f1d3584)
    #5 0x55a232ebde49  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8e49) (BuildId: 69e067aa5f1d3584)
    #6 0x55a232ebdf0a  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8f0a) (BuildId: 69e067aa5f1d3584)
    #7 0x55a232ebdf89  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8f89) (BuildId: 69e067aa5f1d3584)
    #8 0x55a232ebe8aa  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf98aa) (BuildId: 69e067aa5f1d3584)
    #9 0x55a232ebdd82  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8d82) (BuildId: 69e067aa5f1d3584)
    #10 0x55a232ebddd9  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8dd9) (BuildId: 69e067aa5f1d3584)
    #11 0x55a232ebda4e  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8a4e) (BuildId: 69e067aa5f1d3584)
    #12 0x55a232ebdd6a  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8d6a) (BuildId: 69e067aa5f1d3584)
    #13 0x55a232eba81d  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf581d) (BuildId: 69e067aa5f1d3584)
    #14 0x55a232ebf074  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xfa074) (BuildId: 69e067aa5f1d3584)
    #15 0x55a232ed8381  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0x113381) (BuildId: 69e067aa5f1d3584)
    #16 0x55a232ebef18  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf9f18) (BuildId: 69e067aa5f1d3584)
    #17 0x55a232ebdb9d  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8b9d) (BuildId: 69e067aa5f1d3584)
    #18 0x7fe0d2967d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)
    #19 0x7fe0d2967e3f  (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)
    #20 0x55a232e0bfe4  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0x46fe4) (BuildId: 69e067aa5f1d3584)

0x502000000010 is located 0 bytes inside of 3-byte region [0x502000000010,0x502000000013)
freed by thread T0 here:
    #0 0x55a232e8f116  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xca116) (BuildId: 69e067aa5f1d3584)
    #1 0x55a232eba556  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf5556) (BuildId: 69e067aa5f1d3584)
    #2 0x55a232ebe55a  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf955a) (BuildId: 69e067aa5f1d3584)
    #3 0x55a232ebdec9  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8ec9) (BuildId: 69e067aa5f1d3584)
    #4 0x55a232ebdd82  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8d82) (BuildId: 69e067aa5f1d3584)
    #5 0x55a232ebdd6a  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8d6a) (BuildId: 69e067aa5f1d3584)
    #6 0x55a232ed8381  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0x113381) (BuildId: 69e067aa5f1d3584)
    #7 0x55a232ebef18  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf9f18) (BuildId: 69e067aa5f1d3584)
    #8 0x55a232ebdb9d  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8b9d) (BuildId: 69e067aa5f1d3584)

previously allocated by thread T0 here:
    #0 0x55a232e8f3af  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xca3af) (BuildId: 69e067aa5f1d3584)
    #1 0x55a232eb9fbf  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf4fbf) (BuildId: 69e067aa5f1d3584)
    #2 0x55a232eba250  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf5250) (BuildId: 69e067aa5f1d3584)
    #3 0x55a232eba5b8  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf55b8) (BuildId: 69e067aa5f1d3584)
    #4 0x55a232ebfc8c  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xfac8c) (BuildId: 69e067aa5f1d3584)
    #5 0x55a232eba784  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf5784) (BuildId: 69e067aa5f1d3584)
    #6 0x55a232ebd8fa  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf88fa) (BuildId: 69e067aa5f1d3584)
    #7 0x55a232ebdd6a  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8d6a) (BuildId: 69e067aa5f1d3584)
    #8 0x55a232ed8381  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0x113381) (BuildId: 69e067aa5f1d3584)
    #9 0x55a232ebef18  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf9f18) (BuildId: 69e067aa5f1d3584)
    #10 0x55a232ebdb9d  (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xf8b9d) (BuildId: 69e067aa5f1d3584)

SUMMARY: AddressSanitizer: double-free (/home/user/arenavec_split_off_df/target/debug/arenavec_split_off_df+0xca116) (BuildId: 69e067aa5f1d3584) 
==4170119==ABORTING

How to Build and Run the PoC:

RUSTFLAGS="-Zsanitizer=address" cargo run

Details:

  • Compiler Version: rustc 1.81.0-nightly (8337ba918 2024-06-12)
  • Library Version: arenavec-0.1.1
  • OS: Ubuntu 20.04.6 LTS

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions