Skip to content

Commit fbb0609

Browse files
authored
Fix recv_uninit to handle the TRUNC flag. (#1159)
* Fix `recv_uninit` to handle the `TRUNC` flag. With the `TRUNC` flag, `recv` returns the untruncated length, which may be longer than the provided buffer. Fix `recv_uninit` and `recvfrom_uninit` to handle this case. This mean that `recv_uninit` and `recvfrom_uninit` are unable to return the full untruncated length returned by `recv` with the `TRUNC` flag, however it appears that fixing that may require API changes. Fixes #1153. * Add comments mentioning alternatives.
1 parent 23369bb commit fbb0609

File tree

3 files changed

+50
-3
lines changed

3 files changed

+50
-3
lines changed

src/net/send_recv/mod.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::net::SocketAddrUnix;
1010
use crate::net::{SocketAddr, SocketAddrAny, SocketAddrV4, SocketAddrV6};
1111
use crate::{backend, io};
1212
use backend::fd::{AsFd, BorrowedFd};
13+
use core::cmp::min;
1314
use core::mem::MaybeUninit;
1415

1516
pub use backend::net::send_recv::{RecvFlags, SendFlags};
@@ -71,17 +72,24 @@ pub fn recv<Fd: AsFd>(fd: Fd, buf: &mut [u8], flags: RecvFlags) -> io::Result<us
7172
/// This is equivalent to [`recv`], except that it can read into uninitialized
7273
/// memory. It returns the slice that was initialized by this function and the
7374
/// slice that remains uninitialized.
75+
///
76+
/// Because this interface returns the length via the returned slice, it's
77+
/// unsable to return the untruncated length that would be returned when the
78+
/// `RecvFlags::TRUNC` flag is used. If you need the untruncated length, use
79+
/// [`recv`].
7480
#[inline]
7581
pub fn recv_uninit<Fd: AsFd>(
7682
fd: Fd,
7783
buf: &mut [MaybeUninit<u8>],
7884
flags: RecvFlags,
7985
) -> io::Result<(&mut [u8], &mut [MaybeUninit<u8>])> {
8086
let length = unsafe {
81-
backend::net::syscalls::recv(fd.as_fd(), buf.as_mut_ptr().cast::<u8>(), buf.len(), flags)
87+
backend::net::syscalls::recv(fd.as_fd(), buf.as_mut_ptr().cast::<u8>(), buf.len(), flags)?
8288
};
8389

84-
Ok(unsafe { split_init(buf, length?) })
90+
// If the `TRUNC` flag is set, the returned `length` may be longer than the
91+
// buffer length.
92+
Ok(unsafe { split_init(buf, min(length, buf.len())) })
8593
}
8694

8795
/// `send(fd, buf, flags)`—Writes data to a socket.
@@ -160,6 +168,11 @@ pub fn recvfrom<Fd: AsFd>(
160168
/// This is equivalent to [`recvfrom`], except that it can read into
161169
/// uninitialized memory. It returns the slice that was initialized by this
162170
/// function and the slice that remains uninitialized.
171+
///
172+
/// Because this interface returns the length via the returned slice, it's
173+
/// unsable to return the untruncated length that would be returned when the
174+
/// `RecvFlags::TRUNC` flag is used. If you need the untruncated length, use
175+
/// [`recvfrom`].
163176
#[allow(clippy::type_complexity)]
164177
#[inline]
165178
pub fn recvfrom_uninit<Fd: AsFd>(
@@ -175,7 +188,10 @@ pub fn recvfrom_uninit<Fd: AsFd>(
175188
flags,
176189
)?
177190
};
178-
let (init, uninit) = unsafe { split_init(buf, length) };
191+
192+
// If the `TRUNC` flag is set, the returned `length` may be longer than the
193+
// buffer length.
194+
let (init, uninit) = unsafe { split_init(buf, min(length, buf.len())) };
179195
Ok((init, uninit, addr))
180196
}
181197

tests/net/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ mod connect_bind_send;
1212
mod dgram;
1313
#[cfg(feature = "event")]
1414
mod poll;
15+
#[cfg(unix)]
16+
mod recv_trunc;
1517
mod sockopt;
1618
#[cfg(unix)]
1719
mod unix;

tests/net/recv_trunc.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use rustix::net::{AddressFamily, RecvFlags, SendFlags, SocketAddrUnix, SocketType};
2+
use std::mem::MaybeUninit;
3+
4+
/// Test `recv_uninit` with the `RecvFlags::Trunc` flag.
5+
#[test]
6+
fn net_recv_uninit_trunc() {
7+
crate::init();
8+
9+
let tmpdir = tempfile::tempdir().unwrap();
10+
let path = tmpdir.path().join("recv_uninit_trunc");
11+
let name = SocketAddrUnix::new(&path).unwrap();
12+
13+
let receiver = rustix::net::socket(AddressFamily::UNIX, SocketType::DGRAM, None).unwrap();
14+
rustix::net::bind_unix(&receiver, &name).expect("bind");
15+
16+
let sender = rustix::net::socket(AddressFamily::UNIX, SocketType::DGRAM, None).unwrap();
17+
let request = b"Hello, World!!!";
18+
let n = rustix::net::sendto_unix(&sender, request, SendFlags::empty(), &name).expect("send");
19+
assert_eq!(n, request.len());
20+
drop(sender);
21+
22+
let mut response = [MaybeUninit::<u8>::zeroed(); 5];
23+
let (init, uninit) =
24+
rustix::net::recv_uninit(&receiver, &mut response, RecvFlags::TRUNC).expect("recv_uninit");
25+
26+
// We used the `TRUNC` flag, so we should have only gotten 5 bytes.
27+
assert_eq!(init, b"Hello");
28+
assert!(uninit.is_empty());
29+
}

0 commit comments

Comments
 (0)