Skip to content

Commit ff9c7fb

Browse files
authored
Make cmsg_space! usable in const contexts. (#889)
Make `cmsg_space!` usable in const contexts, so that it can be used as a buffer size argument, and add a version of tests/net/unix.rs that uses stack-allocated buffers instead of `Vec`s. This exposes an alignment sublety, that buffers must be aligned to the needed alignment of `cmsghdr`; handle this by auto-aligning the provided buffer to the needed boundary.
1 parent dd5dc44 commit ff9c7fb

4 files changed

Lines changed: 691 additions & 31 deletions

File tree

src/net/send_recv/msg.rs

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::net::UCred;
1010

1111
use core::iter::FusedIterator;
1212
use core::marker::PhantomData;
13-
use core::mem::{size_of, size_of_val, take};
13+
use core::mem::{align_of, size_of, size_of_val, take};
1414
use core::{ptr, slice};
1515

1616
use super::{RecvFlags, SendFlags, SocketAddrAny, SocketAddrV4, SocketAddrV6};
@@ -40,8 +40,19 @@ macro_rules! cmsg_space {
4040
}
4141

4242
#[doc(hidden)]
43-
pub fn __cmsg_space(len: usize) -> usize {
44-
unsafe { c::CMSG_SPACE(len.try_into().expect("CMSG_SPACE size overflow")) as usize }
43+
pub const fn __cmsg_space(len: usize) -> usize {
44+
// Add `align_of::<c::cmsghdr>()` so that we can align the user-provided
45+
// `&[u8]` to the required alignment boundary.
46+
let len = len + align_of::<c::cmsghdr>();
47+
48+
// Convert `len` to `u32` for `CMSG_SPACE`. This would be `try_into()` if
49+
// we could call that in a `const fn`.
50+
let converted_len = len as u32;
51+
if converted_len as usize != len {
52+
unreachable!(); // `CMSG_SPACE` size overflow
53+
}
54+
55+
unsafe { c::CMSG_SPACE(converted_len) as usize }
4556
}
4657

4758
/// Ancillary message for [`sendmsg`], [`sendmsg_v4`], [`sendmsg_v6`],
@@ -59,19 +70,11 @@ impl SendAncillaryMessage<'_, '_> {
5970
/// Get the maximum size of an ancillary message.
6071
///
6172
/// This can be helpful in determining the size of the buffer you allocate.
62-
pub fn size(&self) -> usize {
63-
let total_bytes = match self {
64-
Self::ScmRights(slice) => size_of_val(*slice),
73+
pub const fn size(&self) -> usize {
74+
match self {
75+
Self::ScmRights(slice) => cmsg_space!(ScmRights(slice.len())),
6576
#[cfg(linux_kernel)]
66-
Self::ScmCredentials(ucred) => size_of_val(ucred),
67-
};
68-
69-
unsafe {
70-
c::CMSG_SPACE(
71-
total_bytes
72-
.try_into()
73-
.expect("size too large for CMSG_SPACE"),
74-
) as usize
77+
Self::ScmCredentials(_) => cmsg_space!(ScmCredentials(1)),
7578
}
7679
}
7780
}
@@ -107,15 +110,20 @@ impl<'buf> From<&'buf mut [u8]> for SendAncillaryBuffer<'buf, '_, '_> {
107110

108111
impl Default for SendAncillaryBuffer<'_, '_, '_> {
109112
fn default() -> Self {
110-
Self::new(&mut [])
113+
Self {
114+
buffer: &mut [],
115+
length: 0,
116+
_phantom: PhantomData,
117+
}
111118
}
112119
}
113120

114121
impl<'buf, 'slice, 'fd> SendAncillaryBuffer<'buf, 'slice, 'fd> {
115122
/// Create a new, empty `SendAncillaryBuffer` from a raw byte buffer.
123+
#[inline]
116124
pub fn new(buffer: &'buf mut [u8]) -> Self {
117125
Self {
118-
buffer,
126+
buffer: align_for_cmsghdr(buffer),
119127
length: 0,
120128
_phantom: PhantomData,
121129
}
@@ -234,15 +242,20 @@ impl<'buf> From<&'buf mut [u8]> for RecvAncillaryBuffer<'buf> {
234242

235243
impl Default for RecvAncillaryBuffer<'_> {
236244
fn default() -> Self {
237-
Self::new(&mut [])
245+
Self {
246+
buffer: &mut [],
247+
read: 0,
248+
length: 0,
249+
}
238250
}
239251
}
240252

241253
impl<'buf> RecvAncillaryBuffer<'buf> {
242254
/// Create a new, empty `RecvAncillaryBuffer` from a raw byte buffer.
255+
#[inline]
243256
pub fn new(buffer: &'buf mut [u8]) -> Self {
244257
Self {
245-
buffer,
258+
buffer: align_for_cmsghdr(buffer),
246259
read: 0,
247260
length: 0,
248261
}
@@ -297,6 +310,16 @@ impl Drop for RecvAncillaryBuffer<'_> {
297310
}
298311
}
299312

313+
/// Return a slice of `buffer` starting at the first `cmsghdr` alignment
314+
/// boundary.
315+
#[inline]
316+
fn align_for_cmsghdr(buffer: &mut [u8]) -> &mut [u8] {
317+
let align = align_of::<c::cmsghdr>();
318+
let addr = buffer.as_ptr() as usize;
319+
let adjusted = (addr + (align - 1)) & align.wrapping_neg();
320+
&mut buffer[adjusted - addr..]
321+
}
322+
300323
/// An iterator that drains messages from a [`RecvAncillaryBuffer`].
301324
pub struct AncillaryDrain<'buf> {
302325
/// Inner iterator over messages.

tests/net/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ mod poll;
1212
mod sockopt;
1313
#[cfg(unix)]
1414
mod unix;
15+
#[cfg(unix)]
16+
mod unix_alloc;
1517
mod v4;
1618
mod v6;
1719

tests/net/unix.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ fn server(ready: Arc<(Mutex<bool>, Condvar)>, path: &Path) {
3535
cvar.notify_all();
3636
}
3737

38-
let mut buffer = vec![0; BUFFER_SIZE];
38+
let mut buffer = [0; BUFFER_SIZE];
3939
'exit: loop {
4040
let data_socket = accept(&connection_socket).unwrap();
4141
let mut sum = 0;
@@ -68,7 +68,7 @@ fn client(ready: Arc<(Mutex<bool>, Condvar)>, path: &Path, runs: &[(&[&str], i32
6868
}
6969

7070
let addr = SocketAddrUnix::new(path).unwrap();
71-
let mut buffer = vec![0; BUFFER_SIZE];
71+
let mut buffer = [0; BUFFER_SIZE];
7272

7373
for (args, sum) in runs {
7474
let data_socket = socket(AddressFamily::UNIX, SocketType::SEQPACKET, None).unwrap();
@@ -136,7 +136,7 @@ fn do_test_unix_msg(addr: SocketAddrUnix) {
136136
listen(&connection_socket, 1).unwrap();
137137

138138
move || {
139-
let mut buffer = vec![0; BUFFER_SIZE];
139+
let mut buffer = [0; BUFFER_SIZE];
140140
'exit: loop {
141141
let data_socket = accept(&connection_socket).unwrap();
142142
let mut sum = 0;
@@ -173,7 +173,7 @@ fn do_test_unix_msg(addr: SocketAddrUnix) {
173173
};
174174

175175
let client = move || {
176-
let mut buffer = vec![0; BUFFER_SIZE];
176+
let mut buffer = [0; BUFFER_SIZE];
177177
let runs: &[(&[&str], i32)] = &[
178178
(&["1", "2"], 3),
179179
(&["4", "77", "103"], 184),
@@ -266,7 +266,7 @@ fn do_test_unix_msg_unconnected(addr: SocketAddrUnix) {
266266
bind_unix(&data_socket, &addr).unwrap();
267267

268268
move || {
269-
let mut buffer = vec![0; BUFFER_SIZE];
269+
let mut buffer = [0; BUFFER_SIZE];
270270
for expected_sum in runs {
271271
let mut sum = 0;
272272
loop {
@@ -434,8 +434,8 @@ fn test_unix_msg_with_scm_rights() {
434434
move || {
435435
let mut pipe_end = None;
436436

437-
let mut buffer = vec![0; BUFFER_SIZE];
438-
let mut cmsg_space = vec![0; rustix::cmsg_space!(ScmRights(1))];
437+
let mut buffer = [0; BUFFER_SIZE];
438+
let mut cmsg_space = [0; rustix::cmsg_space!(ScmRights(1))];
439439

440440
'exit: loop {
441441
let data_socket = accept(&connection_socket).unwrap();
@@ -495,7 +495,7 @@ fn test_unix_msg_with_scm_rights() {
495495
let client = move || {
496496
let addr = SocketAddrUnix::new(path).unwrap();
497497
let (read_end, write_end) = pipe().unwrap();
498-
let mut buffer = vec![0; BUFFER_SIZE];
498+
let mut buffer = [0; BUFFER_SIZE];
499499
let runs: &[(&[&str], i32)] = &[
500500
(&["1", "2"], 3),
501501
(&["4", "77", "103"], 184),
@@ -543,7 +543,7 @@ fn test_unix_msg_with_scm_rights() {
543543
// Format the CMSG.
544544
let we = [write_end.as_fd()];
545545
let msg = SendAncillaryMessage::ScmRights(&we);
546-
let mut space = vec![0; msg.size()];
546+
let mut space = [0; rustix::cmsg_space!(ScmRights(1))];
547547
let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space);
548548
assert!(cmsg_buffer.push(msg));
549549

@@ -606,7 +606,7 @@ fn test_unix_peercred() {
606606
assert_eq!(ucred.gid, getgid());
607607

608608
let msg = SendAncillaryMessage::ScmCredentials(ucred);
609-
let mut space = vec![0; msg.size()];
609+
let mut space = [0; rustix::cmsg_space!(ScmCredentials(1))];
610610
let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space);
611611
assert!(cmsg_buffer.push(msg));
612612

@@ -618,10 +618,10 @@ fn test_unix_peercred() {
618618
)
619619
.unwrap();
620620

621-
let mut cmsg_space = vec![0; rustix::cmsg_space!(ScmCredentials(1))];
621+
let mut cmsg_space = [0; rustix::cmsg_space!(ScmCredentials(1))];
622622
let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut cmsg_space);
623623

624-
let mut buffer = vec![0; BUFFER_SIZE];
624+
let mut buffer = [0; BUFFER_SIZE];
625625
recvmsg(
626626
&recv_sock,
627627
&mut [IoSliceMut::new(&mut buffer)],

0 commit comments

Comments
 (0)