Skip to content

Replace MemCase's unsound impl of Deref/AsMut with a borrow() method #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

progval
Copy link
Contributor

@progval progval commented Aug 8, 2025

This code compiled fine, but raised a segfault on the last println:

use epserde::deser::Deserialize;
use epserde::prelude::Flags;
use epserde::prelude::MemCase;
use epserde::ser::Serialize;
use std::fs::File;
use std::io::BufWriter;

fn main() {
    let v = vec![0u64, 10, 20, 30, 40];

    let path = std::path::PathBuf::from("/tmp/test.vector");

    println!("Writing vector...");
    let mut writer = BufWriter::new(File::create(&path).expect("Could not create temp file"));
    unsafe { v.serialize(&mut writer) }.expect("Could not write vector");
    drop(v);

    println!("mmapping vector...");
    let v =
        unsafe { <Vec<u64>>::mmap(&path, Flags::RANDOM_ACCESS) }.expect("Could not mmap vector");
    let v2: &[u64] = *v;

    println!("Vector value:");
    println!("{:?}", v2);

    println!("Dropping vector...");
    drop(v);

    println!("Vector value:");
    println!("{:?}", v2);
}

This is because the Deref implementation of MemCase<&[u64]> effectively yields a &&'static [u64] which can itself be dereferenced to a &'static [u64], which outlives the MemCase.

There does not seem to be a way to implement Deref or AsRef (or std::borrow::Borrow) without yielding some sort of 'static here. So as far as I can tell, we have to take this ergonomic blow in order to avoid segfaults that are very easy for users to trigger unknowingly.

Resolves #54

progval added 2 commits August 8, 2025 10:15
This code compiled fine, but raised a segfault on the last println:

```rust
use epserde::deser::Deserialize;
use epserde::prelude::Flags;
use epserde::prelude::MemCase;
use epserde::ser::Serialize;
use std::fs::File;
use std::io::BufWriter;

fn main() {
    let v = vec![0u64, 10, 20, 30, 40];

    let path = std::path::PathBuf::from("/tmp/test.vector");

    println!("Writing vector...");
    let mut writer = BufWriter::new(File::create(&path).expect("Could not create temp file"));
    unsafe { v.serialize(&mut writer) }.expect("Could not write vector");
    drop(v);

    println!("mmapping vector...");
    let v =
        unsafe { <Vec<u64>>::mmap(&path, Flags::RANDOM_ACCESS) }.expect("Could not mmap vector");
    let v2: &[u64] = *v;

    println!("Vector value:");
    println!("{:?}", v2);

    println!("Dropping vector...");
    drop(v);

    println!("Vector value:");
    println!("{:?}", v2);
}
```

This is because the `Deref` implementation of `MemCase<&[u64]>`
effectively yields a `&&'static [u64]` which can itself be dereferenced
to a `&'static [u64]`, which outlives the `MemCase`.

There does not seem to be a way to implement `Deref` or `AsRef` (or
`std::borrow::Borrow`) without yielding some sort of `'static` here.
So as far as I can tell, we have to take this ergonomic blow in order to
avoid segfaults that are very easy for users to trigger unknowingly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Dropping a cloned mmapped value makes access to the clone raise a segfault
1 participant