gnzlbg / slice_deque

A contiguous-in-memory double-ended queue that derefs into a slice
https://docs.rs/crate/slice-deque/
Other
154 stars 21 forks source link

Address of zero length buffer is outside of mapped address space #50

Closed FrancisRussell closed 5 years ago

FrancisRussell commented 6 years ago

I've been using SliceDeque with code that uses the write system call on Linux (via the standard Write trait on std::net::TcpStream). If I pass this code a SliceDeque::new(), the write will fail with EFAULT (buf is outside the accessible address space), because SliceDeque returns a start pointer that is not mapped in the address space.

I've not been able to find any documentation on the guarantees intended to be provided by slice, but it would seem to be good practice to have the zero length SliceDeque return a pointer inside the address space, such as null, for a zero-length slice since Linux system calls will check the pointer validity, even if the start address will not be deferenced.

gnzlbg commented 6 years ago

@FrancisRussell sorry for the delay. Do you have a minimal working example that reproduces this ?

Without having looked at what the current implementation does, an empty SliceDeque is guaranteed to:

This is pretty much how all Rust's allocation mechanisms (GlobalAlloc, Alloc, Vec, Box, ...) work when passed a ZST.

If SliceDeque does not currently work like this, then this is a bug that should be fixed.

If I pass this code a SliceDeque::new(), the write will fail with EFAULT (buf is outside the accessible address space), because SliceDeque returns a start pointer that is not mapped in the address space.

I suppose that you are calling write with a length of zero. Is that correct? EDIT: that is, can you assert that the buffer being passed to <TcpStream as Write>::write(tcp_stream, &buf) has a length of zero ?

FrancisRussell commented 5 years ago

I've written this to replicate (the issue also manifests under standard file IO):

extern crate slice_deque;
use slice_deque::SliceDeque;
use std::fs::File;
use std::io::Write;

fn main() {
    let out_buffer = SliceDeque::new();
    let mut out_file = File::create("/tmp/slice_deque_test").unwrap();
    let res = out_file.write(&out_buffer[..]);
    println!("Result was {:?}", res);
    println!("Buffer size: {}", out_buffer.len());
    println!("Address of buffer was: {:?}", out_buffer.as_ptr());
}

Running on x86-64 Debian testing under a 4.6.12 kernel and slice-deque 0.1.13 I get the following:

Result was Err(Os { code: 14, kind: Other, message: "Bad address" })
Buffer size: 0
Address of buffer was: 0xffffffffffffffff

I traced the issue to this line. I note that drop() specifically avoids doing a deallocation when the buffer is empty, however, it looks like removing this check will break things since deallocate_mirrored() expects a non-zero size.

gnzlbg commented 5 years ago

Could you confirm that v0.1.14 fixes this?