Skip to content

net::recv with TRUNC flag is unsound #1654

Description

@funsafemath

For absolutely no reason I was reading the tcp(7) manpage, and it turns out tcp sockets support the MSG_TRUNC flag, which makes the kernel discard any received data instead of writing it, while still returning the number of bytes received. recv() assumes that the kernel writes min(buffer len, recv return value) bytes, so you can pass an uninit array and get a mut slice of MaybeUninit::uninit().assume_init() u8 (probably not too bad, as the syscall should prevent the compiler from making any assumptions, but it's still technically ub).

https://man7.org/linux/man-pages/man7/tcp.7.html

Since Linux 2.4, Linux supports the use of MSG_TRUNC in the flags argument of recv(2) (and recvmsg(2)). This flag causes the received bytes of data to be discarded, rather than passed back in a caller-supplied buffer. Since Linux 2.4.4, MSG_TRUNC also has this effect when used in conjunction with MSG_OOB to receive out-of-band data.

use std::{
    io::Write,
    mem::MaybeUninit,
    net::{TcpListener, TcpStream},
    thread,
    time::Duration,
};

use rustix::{self, net::RecvFlags};

fn main() -> eyre::Result<()> {
    thread::spawn(|| -> eyre::Result<()> {
        let l = TcpListener::bind("127.0.0.1:1234")?;
        let mut c = l.accept()?.0;
        c.write_all(&[0; 128])?;
        Ok(())
    });
    thread::sleep(Duration::from_millis(100));

    let s = TcpStream::connect("127.0.0.1:1234")?;
    let mut stack = [MaybeUninit::uninit(); 128];
    let stack = rustix::net::recv(s, &mut stack, RecvFlags::TRUNC)?.0.0;
    println!("{stack:?}");
    assert!(stack != [0; 128] && stack.len() == 128);
    Ok(())
}

(rustix::net is configured out on the playground)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions