Skip to content

Pointer dereference after free with allocator containing shared state #95269

Closed
@jhorstmann

Description

@jhorstmann

I'm trying out the allocator api with the goal of being able to track memory usage for parts of a program:

#![feature(allocator_api)]
#![feature(core_intrinsics)]

use std::alloc::{AllocError, Allocator, Layout, System};
use std::intrinsics::black_box;
use std::ptr::NonNull;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;

#[derive(Default, Clone)]
struct TrackingAllocator {
    memory_usage: Arc<AtomicUsize>,
}

unsafe impl Allocator for TrackingAllocator {
    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        self.memory_usage
            .fetch_add(layout.size(), Ordering::Relaxed);
        System.allocate(layout)
    }

    fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        self.memory_usage
            .fetch_add(layout.size(), Ordering::Relaxed);
        System.allocate_zeroed(layout)
    }

    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
        self.memory_usage
            .fetch_sub(layout.size(), Ordering::Relaxed);
        System.deallocate(ptr, layout)
    }
}

fn bool_to_int(input: Vec<bool, TrackingAllocator>) -> Vec<i32, TrackingAllocator> {
    let allocator = input.allocator().clone();
    let mut output = Vec::with_capacity_in(input.len(), allocator);
    for b in input {
        output.push(b as i32);
    }
    output
}

fn main() {
    let tracking_alloc = TrackingAllocator::default();
    {
        let mut input = Vec::new_in(tracking_alloc.clone());
        input.push(true);
        input.push(false);
        let output = bool_to_int(input);
        black_box(output);
    }
    {
        let mut input = Vec::new_in(tracking_alloc.clone());
        input.push(true);
        input.push(true);
        let output = bool_to_int(input);
        black_box(output);
    }
    eprintln!("{}", tracking_alloc.memory_usage.load(Ordering::Relaxed));
}

I expected this to work (and print 0 at the end), but instead it fails by freeing an invalid pointer:

$ ./target/debug/memory_tracking
free(): invalid pointer
Aborted

Running with miri gives a bit more context:

error: Undefined Behavior: pointer to alloc984 was dereferenced after this allocation got freed
   --> /home/user/.rustup/toolchains/nightly-2022-03-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/non_null.rs:328:18
    |
328 |         unsafe { &*self.as_ptr() }
    |                  ^^^^^^^^^^^^^^^ pointer to alloc984 was dereferenced after this allocation got freed
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
            
    = note: inside `std::ptr::NonNull::<alloc::sync::ArcInner<std::sync::atomic::AtomicUsize>>::as_ref` at /home/user/.rustup/toolchains/nightly-2022-03-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/non_null.rs:328:18
    = note: inside `std::sync::Arc::<std::sync::atomic::AtomicUsize>::inner` at /home/user/.rustup/toolchains/nightly-2022-03-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/sync.rs:1084:18
    = note: inside `<std::sync::Arc<std::sync::atomic::AtomicUsize> as std::clone::Clone>::clone` at /home/user/.rustup/toolchains/nightly-2022-03-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/sync.rs:1342:24
note: inside `<TrackingAllocator as std::clone::Clone>::clone` at src/main.rs:12:5
   --> src/main.rs:12:5
    |
10  | #[derive(Default, Clone)]
    |                   ----- in this derive macro expansion
11  | struct TrackingAllocator {
12  |     memory_usage: Arc<AtomicUsize>,
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: inside `main` at src/main.rs:54:37
   --> src/main.rs:54:37
    |
54  |         let mut input = Vec::new_in(tracking_alloc.clone());
    |                                     ^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

Meta

$ rustc --version --verbose
rustc 1.61.0-nightly (5f3700105 2022-03-22)
binary: rustc
commit-hash: 5f37001055c29982f4c27ee9edd90449c8e07774
commit-date: 2022-03-22
host: x86_64-unknown-linux-gnu
release: 1.61.0-nightly
LLVM version: 14.0.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-mediumMedium priorityT-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions