Skip to content
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

Shouldn't the Storage trait require the implementer to be a singleton? #4

Open
japaric opened this issue Apr 9, 2020 · 0 comments
Open

Comments

@japaric
Copy link

japaric commented Apr 9, 2020

test/ui/sync-fail.rs shows how trying to close a file on a different filesystem fails at compile time. That compile time protection holds in that example because each filesystem instance has a different type but one can, in safe code, create multiple FS instances of the same type and then close a file on a different filesystem instance (that's still the same type). Variation of test/ui/sync-fail.rs that shows this:

use littlefs2::{
    consts, driver,
    fs::{File, Filesystem},
    io::{Result, Write},
    ram_storage,
};

ram_storage!(
    name=RamStorage,
    backend=Ram,
    trait=driver::Storage,
    erase_value=0xff,
    read_size=20*5,
    write_size=20*7,
    cache_size_ty=consts::U700,
    block_size=20*35,
    block_count=32,
    lookaheadwords_size_ty=consts::U1,
    filename_max_plus_one_ty=consts::U256,
    path_max_plus_one_ty=consts::U256,
    result=Result,
);

fn main() {
    let mut ram1 = Ram::default();
    let mut storage1 = RamStorage::new(&mut ram1);
    let mut alloc1 = Filesystem::allocate();
    Filesystem::format(&mut storage1).unwrap();
    let mut fs1 = Filesystem::mount(&mut alloc1, &mut storage1).unwrap();

    let mut ram2 = Ram::default();
    let mut storage2 = RamStorage::new(&mut ram2);
    let mut alloc2 = Filesystem::allocate();
    Filesystem::format(&mut storage2).unwrap();
    let mut fs2 = Filesystem::mount(&mut alloc2, &mut storage2).unwrap();

    let mut alloc = File::allocate();
    // open file in FS1
    let mut file = File::create("a.txt", &mut alloc, &mut fs1, &mut storage1).unwrap();
    file.write(&mut fs1, &mut storage1, b"Hello!").unwrap();
    // sync the file in FS2
    file.close(&mut fs2, &mut storage2).unwrap();
}

Here ram1 and ram2 point to different to different memory blocks. The above example doesn't appear to trigger UB but it's probably corrupting the second filesystem. A more complex variation of the above example may cause UB though.

A potential solution to this is to make trait Storage unsafe and require that's implemented on singleton types. This way all instances of Ram are handles to the same memory (but then you have be careful about Rust aliasing rules and will likely have to make Ram !Send and !Sync ...)

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

No branches or pull requests

1 participant