Skip to content

Commit 31dfad1

Browse files
authored
Fixes for empty and multiple-entry control message buffers. (#915)
* Fixes for empty and multiple-entry control message buffers. - Fix the handling of empty buffers passed to `SendAncillaryBuffer::new` and `RecvAncillaryBuffer::new`. - Fix the buffer size computation for multiple messages to include only one copy of the padding for alignment. * Update to qemu 8.1.2. * Update to linux-raw-sys 0.4.11 In particular, this brings in sunfishcode/linux-raw-sys#92, a fix for the `CMSG_NXTHDR` macro which fixes test failures in this PR.
1 parent 1edcdfe commit 31dfad1

File tree

7 files changed

+617
-10
lines changed

7 files changed

+617
-10
lines changed

.github/workflows/main.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ jobs:
226226
name: Test
227227
runs-on: ${{ matrix.os }}
228228
env:
229-
QEMU_BUILD_VERSION: 8.1.0
229+
QEMU_BUILD_VERSION: 8.1.2
230230
# Enabling testing of experimental features.
231231
RUSTFLAGS: --cfg rustix_use_experimental_features
232232
strategy:
@@ -547,7 +547,7 @@ jobs:
547547
env:
548548
# -D warnings is commented out in our install-rust action; re-add it here.
549549
RUSTFLAGS: -D warnings -D elided-lifetimes-in-paths
550-
QEMU_BUILD_VERSION: 8.1.0
550+
QEMU_BUILD_VERSION: 8.1.2
551551
steps:
552552
- uses: actions/checkout@v3
553553
with:
@@ -639,7 +639,7 @@ jobs:
639639
RUSTFLAGS: --cfg rustix_use_experimental_asm -D warnings -D elided-lifetimes-in-paths
640640
RUSTDOCFLAGS: --cfg rustix_use_experimental_asm
641641
CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_RUSTFLAGS: --cfg rustix_use_experimental_asm
642-
QEMU_BUILD_VERSION: 8.1.0
642+
QEMU_BUILD_VERSION: 8.1.2
643643
steps:
644644
- uses: actions/checkout@v3
645645
with:

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ once_cell = { version = "1.5.2", optional = true }
3636
# libc backend can be selected via adding `--cfg=rustix_use_libc` to
3737
# `RUSTFLAGS` or enabling the `use-libc` cargo feature.
3838
[target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies]
39-
linux-raw-sys = { version = "0.4.8", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] }
39+
linux-raw-sys = { version = "0.4.11", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] }
4040
libc_errno = { package = "errno", version = "0.3.1", default-features = false, optional = true }
4141
libc = { version = "0.2.150", default-features = false, features = ["extra_traits"], optional = true }
4242

@@ -53,7 +53,7 @@ libc = { version = "0.2.150", default-features = false, features = ["extra_trait
5353
# Some syscalls do not have libc wrappers, such as in `io_uring`. For these,
5454
# the libc backend uses the linux-raw-sys ABI and `libc::syscall`.
5555
[target.'cfg(all(any(target_os = "android", target_os = "linux"), any(rustix_use_libc, miri, not(all(target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies]
56-
linux-raw-sys = { version = "0.4.8", default-features = false, features = ["general", "ioctl", "no_std"] }
56+
linux-raw-sys = { version = "0.4.11", default-features = false, features = ["general", "ioctl", "no_std"] }
5757

5858
# For the libc backend on Windows, use the Winsock2 API in windows-sys.
5959
[target.'cfg(windows)'.dependencies.windows-sys]

src/net/send_recv/msg.rs

Lines changed: 156 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,34 @@ use core::{ptr, slice};
1717

1818
use super::{RecvFlags, SendFlags, SocketAddrAny, SocketAddrV4, SocketAddrV6};
1919

20-
/// Macro for defining the amount of space used by CMSGs.
20+
/// Macro for defining the amount of space to allocate in a buffer for use with
21+
/// [`RecvAncillaryBuffer::new`] and [`SendAncillaryBuffer::new`].
22+
///
23+
/// # Examples
24+
///
25+
/// Allocate a buffer for a single file descriptor:
26+
/// ```
27+
/// # use rustix::cmsg_space;
28+
/// let mut space = [0; rustix::cmsg_space!(ScmRights(1))];
29+
/// ```
30+
///
31+
/// Allocate a buffer for credentials:
32+
/// ```
33+
/// # #[cfg(linux_kernel)]
34+
/// # {
35+
/// # use rustix::cmsg_space;
36+
/// let mut space = [0; rustix::cmsg_space!(ScmCredentials(1))];
37+
/// # }
38+
/// ```
39+
///
40+
/// Allocate a buffer for two file descriptors and credentials:
41+
/// ```
42+
/// # #[cfg(linux_kernel)]
43+
/// # {
44+
/// # use rustix::cmsg_space;
45+
/// let mut space = [0; rustix::cmsg_space!(ScmRights(2), ScmCredentials(1))];
46+
/// # }
47+
/// ```
2148
#[macro_export]
2249
macro_rules! cmsg_space {
2350
// Base Rules
@@ -33,12 +60,41 @@ macro_rules! cmsg_space {
3360
};
3461

3562
// Combo Rules
36-
(($($($x:tt)*),+)) => {
63+
($firstid:ident($firstex:expr), $($restid:ident($restex:expr)),*) => {{
64+
// We only have to add `cmsghdr` alignment once; all other times we can
65+
// use `cmsg_aligned_space`.
66+
let sum = $crate::cmsg_space!($firstid($firstex));
3767
$(
38-
cmsg_space!($($x)*) +
39-
)+
40-
0
68+
let sum = sum + $crate::cmsg_aligned_space!($restid($restex));
69+
)*
70+
sum
71+
}};
72+
}
73+
74+
/// Like `cmsg_space`, but doesn't add padding for `cmsghdr` alignment.
75+
#[doc(hidden)]
76+
#[macro_export]
77+
macro_rules! cmsg_aligned_space {
78+
// Base Rules
79+
(ScmRights($len:expr)) => {
80+
$crate::net::__cmsg_aligned_space(
81+
$len * ::core::mem::size_of::<$crate::fd::BorrowedFd<'static>>(),
82+
)
83+
};
84+
(ScmCredentials($len:expr)) => {
85+
$crate::net::__cmsg_aligned_space(
86+
$len * ::core::mem::size_of::<$crate::net::UCred>(),
87+
)
4188
};
89+
90+
// Combo Rules
91+
($firstid:ident($firstex:expr), $($restid:ident($restex:expr)),*) => {{
92+
let sum = cmsg_aligned_space!($firstid($firstex));
93+
$(
94+
let sum = sum + cmsg_aligned_space!($restid($restex));
95+
)*
96+
sum
97+
}};
4298
}
4399

44100
#[doc(hidden)]
@@ -47,6 +103,11 @@ pub const fn __cmsg_space(len: usize) -> usize {
47103
// `&[u8]` to the required alignment boundary.
48104
let len = len + align_of::<c::cmsghdr>();
49105

106+
__cmsg_aligned_space(len)
107+
}
108+
109+
#[doc(hidden)]
110+
pub const fn __cmsg_aligned_space(len: usize) -> usize {
50111
// Convert `len` to `u32` for `CMSG_SPACE`. This would be `try_into()` if
51112
// we could call that in a `const fn`.
52113
let converted_len = len as u32;
@@ -97,6 +158,10 @@ pub enum RecvAncillaryMessage<'a> {
97158

98159
/// Buffer for sending ancillary messages with [`sendmsg`], [`sendmsg_v4`],
99160
/// [`sendmsg_v6`], [`sendmsg_unix`], and [`sendmsg_any`].
161+
///
162+
/// Use the [`push`] function to add messages to send.
163+
///
164+
/// [`push`]: SendAncillaryBuffer::push
100165
pub struct SendAncillaryBuffer<'buf, 'slice, 'fd> {
101166
/// Raw byte buffer for messages.
102167
buffer: &'buf mut [u8],
@@ -126,6 +191,44 @@ impl Default for SendAncillaryBuffer<'_, '_, '_> {
126191

127192
impl<'buf, 'slice, 'fd> SendAncillaryBuffer<'buf, 'slice, 'fd> {
128193
/// Create a new, empty `SendAncillaryBuffer` from a raw byte buffer.
194+
///
195+
/// The buffer size may be computed with [`cmsg_space`], or it may be
196+
/// zero for an empty buffer, however in that case, consider `default()`
197+
/// instead, or even using [`send`] instead of `sendmsg`.
198+
///
199+
/// # Examples
200+
///
201+
/// Allocate a buffer for a single file descriptor:
202+
/// ```
203+
/// # use rustix::cmsg_space;
204+
/// # use rustix::net::SendAncillaryBuffer;
205+
/// let mut space = [0; rustix::cmsg_space!(ScmRights(1))];
206+
/// let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space);
207+
/// ```
208+
///
209+
/// Allocate a buffer for credentials:
210+
/// ```
211+
/// # #[cfg(linux_kernel)]
212+
/// # {
213+
/// # use rustix::cmsg_space;
214+
/// # use rustix::net::SendAncillaryBuffer;
215+
/// let mut space = [0; rustix::cmsg_space!(ScmCredentials(1))];
216+
/// let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space);
217+
/// # }
218+
/// ```
219+
///
220+
/// Allocate a buffer for two file descriptors and credentials:
221+
/// ```
222+
/// # #[cfg(linux_kernel)]
223+
/// # {
224+
/// # use rustix::cmsg_space;
225+
/// # use rustix::net::SendAncillaryBuffer;
226+
/// let mut space = [0; rustix::cmsg_space!(ScmRights(2), ScmCredentials(1))];
227+
/// let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space);
228+
/// # }
229+
/// ```
230+
///
231+
/// [`send`]: crate::net::send
129232
#[inline]
130233
pub fn new(buffer: &'buf mut [u8]) -> Self {
131234
Self {
@@ -229,6 +332,10 @@ impl<'slice, 'fd> Extend<SendAncillaryMessage<'slice, 'fd>>
229332
}
230333

231334
/// Buffer for receiving ancillary messages with [`recvmsg`].
335+
///
336+
/// Use the [`drain`] function to iterate over the received messages.
337+
///
338+
/// [`drain`]: RecvAncillaryBuffer::drain
232339
#[derive(Default)]
233340
pub struct RecvAncillaryBuffer<'buf> {
234341
/// Raw byte buffer for messages.
@@ -249,6 +356,44 @@ impl<'buf> From<&'buf mut [u8]> for RecvAncillaryBuffer<'buf> {
249356

250357
impl<'buf> RecvAncillaryBuffer<'buf> {
251358
/// Create a new, empty `RecvAncillaryBuffer` from a raw byte buffer.
359+
///
360+
/// The buffer size may be computed with [`cmsg_space`], or it may be
361+
/// zero for an empty buffer, however in that case, consider `default()`
362+
/// instead, or even using [`recv`] instead of `recvmsg`.
363+
///
364+
/// # Examples
365+
///
366+
/// Allocate a buffer for a single file descriptor:
367+
/// ```
368+
/// # use rustix::cmsg_space;
369+
/// # use rustix::net::RecvAncillaryBuffer;
370+
/// let mut space = [0; rustix::cmsg_space!(ScmRights(1))];
371+
/// let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut space);
372+
/// ```
373+
///
374+
/// Allocate a buffer for credentials:
375+
/// ```
376+
/// # #[cfg(linux_kernel)]
377+
/// # {
378+
/// # use rustix::cmsg_space;
379+
/// # use rustix::net::RecvAncillaryBuffer;
380+
/// let mut space = [0; rustix::cmsg_space!(ScmCredentials(1))];
381+
/// let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut space);
382+
/// # }
383+
/// ```
384+
///
385+
/// Allocate a buffer for two file descriptors and credentials:
386+
/// ```
387+
/// # #[cfg(linux_kernel)]
388+
/// # {
389+
/// # use rustix::cmsg_space;
390+
/// # use rustix::net::RecvAncillaryBuffer;
391+
/// let mut space = [0; rustix::cmsg_space!(ScmRights(2), ScmCredentials(1))];
392+
/// let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut space);
393+
/// # }
394+
/// ```
395+
///
396+
/// [`recv`]: crate::net::recv
252397
#[inline]
253398
pub fn new(buffer: &'buf mut [u8]) -> Self {
254399
Self {
@@ -311,6 +456,12 @@ impl Drop for RecvAncillaryBuffer<'_> {
311456
/// boundary.
312457
#[inline]
313458
fn align_for_cmsghdr(buffer: &mut [u8]) -> &mut [u8] {
459+
// If the buffer is empty, we won't be writing anything into it, so it
460+
// doesn't need to be aligned.
461+
if buffer.is_empty() {
462+
return buffer;
463+
}
464+
314465
let align = align_of::<c::cmsghdr>();
315466
let addr = buffer.as_ptr() as usize;
316467
let adjusted = (addr + (align - 1)) & align.wrapping_neg();

tests/net/cmsg.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#[test]
2+
fn test_empty_buffers() {
3+
use rustix::fd::AsFd;
4+
use rustix::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SendAncillaryMessage};
5+
use rustix::pipe::pipe;
6+
7+
let (_read_end, write_end) = pipe().unwrap();
8+
let we = [write_end.as_fd()];
9+
10+
let mut cmsg_buffer = SendAncillaryBuffer::new(&mut []);
11+
let msg = SendAncillaryMessage::ScmRights(&we);
12+
assert!(!cmsg_buffer.push(msg));
13+
14+
let mut cmsg_buffer = SendAncillaryBuffer::default();
15+
let msg = SendAncillaryMessage::ScmRights(&we);
16+
assert!(!cmsg_buffer.push(msg));
17+
18+
let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut []);
19+
assert!(cmsg_buffer.drain().next().is_none());
20+
21+
let mut cmsg_buffer = RecvAncillaryBuffer::default();
22+
assert!(cmsg_buffer.drain().next().is_none());
23+
}
24+
25+
#[test]
26+
fn test_buffer_sizes() {
27+
use rustix::cmsg_space;
28+
29+
assert!(cmsg_space!(ScmRights(0)) > 0);
30+
assert!(cmsg_space!(ScmRights(1)) >= cmsg_space!(ScmRights(0)));
31+
assert!(cmsg_space!(ScmRights(2)) < cmsg_space!(ScmRights(1), ScmRights(1)));
32+
assert!(cmsg_space!(ScmRights(1)) * 2 >= cmsg_space!(ScmRights(1), ScmRights(1)));
33+
assert!(cmsg_space!(ScmRights(1), ScmRights(0)) >= cmsg_space!(ScmRights(1)));
34+
}

tests/net/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#![cfg_attr(core_c_str, feature(core_c_str))]
77

88
mod addr;
9+
#[cfg(unix)]
10+
mod cmsg;
911
mod connect_bind_send;
1012
#[cfg(feature = "event")]
1113
mod poll;

0 commit comments

Comments
 (0)