bytecodealliance / bytecodealliance/rustix

net::recv with TRUNC flag is unsound

Open
#1,654 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
2.1k
Forks
294
Avg merge
4h 7m
Merged PRs (30d)
2

Description

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.

```rs
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)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.