Skip to content

Commit d4836e9

Browse files
committed
fixes UB for recvmmsg, simplifies function signature of recvmmsg and sendmmsg
1 parent 8ad064d commit d4836e9

File tree

3 files changed

+46
-23
lines changed

3 files changed

+46
-23
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ This project adheres to [Semantic Versioning](https://semver.org/).
55

66
## [Unreleased] - ReleaseDate
77

8+
### Fixed
9+
10+
- Fixed the function signature of `recvmmsg`, potentially causing UB
11+
([#2119](https://github.com/nix-rust/nix/issues/2119))
12+
813
### Changed
914

1015
- The following APIs now take an implementation of `AsFd` rather than a
@@ -17,6 +22,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
1722
- `unistd::getpeereid`
1823

1924
([#2137](https://github.com/nix-rust/nix/pull/2137))
25+
- Simplified the function signatures of `recvmmsg` and `sendmmsg`
2026

2127
## [0.27.1] - 2023-08-28
2228

src/sys/socket/mod.rs

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1552,23 +1552,23 @@ pub fn sendmmsg<'a, XS, AS, C, I, S>(
15521552
flags: MsgFlags
15531553
) -> crate::Result<MultiResults<'a, S>>
15541554
where
1555-
XS: IntoIterator<Item = &'a I>,
1555+
XS: IntoIterator<Item = I>,
15561556
AS: AsRef<[Option<S>]>,
1557-
I: AsRef<[IoSlice<'a>]> + 'a,
1558-
C: AsRef<[ControlMessage<'a>]> + 'a,
1559-
S: SockaddrLike + 'a
1557+
I: AsRef<[IoSlice<'a>]>,
1558+
C: AsRef<[ControlMessage<'a>]>,
1559+
S: SockaddrLike,
15601560
{
15611561

15621562
let mut count = 0;
15631563

15641564

15651565
for (i, ((slice, addr), mmsghdr)) in slices.into_iter().zip(addrs.as_ref()).zip(data.items.iter_mut() ).enumerate() {
15661566
let p = &mut mmsghdr.msg_hdr;
1567-
p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec;
1567+
p.msg_iov = slice.as_ref().as_ptr().cast_mut().cast();
15681568
p.msg_iovlen = slice.as_ref().len() as _;
15691569

15701570
p.msg_namelen = addr.as_ref().map_or(0, S::len);
1571-
p.msg_name = addr.as_ref().map_or(ptr::null(), S::as_ptr) as _;
1571+
p.msg_name = addr.as_ref().map_or(ptr::null(), S::as_ptr).cast_mut().cast();
15721572

15731573
// Encode each cmsg. This must happen after initializing the header because
15741574
// CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields.
@@ -1583,9 +1583,16 @@ pub fn sendmmsg<'a, XS, AS, C, I, S>(
15831583
pmhdr = unsafe { CMSG_NXTHDR(p, pmhdr) };
15841584
}
15851585

1586-
count = i+1;
1586+
// Doing an unchecked addition is alright here, as the only way to obtain an instance of `MultiHeaders`
1587+
// is through the `preallocate` function, which takes an `usize` as an argument to define its size,
1588+
// which also provides an upper bound for the size of this zipped iterator. Thus, `i < usize::MAX` or in
1589+
// other words: `count` doesn't overflow
1590+
count = i + 1;
15871591
}
15881592

1593+
// SAFETY: all pointers are guaranteed to be valid for the scope of this function. `count` does represent the
1594+
// maximum number of messages that can be sent safely (i.e. `count` is the minimum of the sizes of `slices`,
1595+
// `data.items` and `addrs`)
15891596
let sent = Errno::result(unsafe {
15901597
libc::sendmmsg(
15911598
fd,
@@ -1711,21 +1718,28 @@ pub fn recvmmsg<'a, XS, S, I>(
17111718
mut timeout: Option<crate::sys::time::TimeSpec>,
17121719
) -> crate::Result<MultiResults<'a, S>>
17131720
where
1714-
XS: IntoIterator<Item = &'a I>,
1715-
I: AsRef<[IoSliceMut<'a>]> + 'a,
1721+
XS: IntoIterator<Item = I>,
1722+
I: AsMut<[IoSliceMut<'a>]>,
17161723
{
17171724
let mut count = 0;
1718-
for (i, (slice, mmsghdr)) in slices.into_iter().zip(data.items.iter_mut()).enumerate() {
1725+
for (i, (mut slice, mmsghdr)) in slices.into_iter().zip(data.items.iter_mut()).enumerate() {
17191726
let p = &mut mmsghdr.msg_hdr;
1720-
p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec;
1721-
p.msg_iovlen = slice.as_ref().len() as _;
1727+
p.msg_iov = slice.as_mut().as_mut_ptr().cast();
1728+
p.msg_iovlen = slice.as_mut().len() as _;
1729+
1730+
// Doing an unchecked addition is alright here, as the only way to obtain an instance of `MultiHeaders`
1731+
// is through the `preallocate` function, which takes an `usize` as an argument to define its size,
1732+
// which also provides an upper bound for the size of this zipped iterator. Thus, `i < usize::MAX` or in
1733+
// other words: `count` doesn't overflow
17221734
count = i + 1;
17231735
}
17241736

17251737
let timeout_ptr = timeout
17261738
.as_mut()
17271739
.map_or_else(std::ptr::null_mut, |t| t as *mut _ as *mut libc::timespec);
17281740

1741+
// SAFETY: all pointers are guaranteed to be valid for the scope of this function. `count` does represent the
1742+
// maximum number of messages that can be received safely (i.e. `count` is the minimum of the sizes of `slices` and `data.items`)
17291743
let received = Errno::result(unsafe {
17301744
libc::recvmmsg(
17311745
fd,
@@ -1743,16 +1757,14 @@ where
17431757
})
17441758
}
17451759

1760+
/// Iterator over results of [`recvmmsg`]/[`sendmmsg`]
17461761
#[cfg(any(
17471762
target_os = "linux",
17481763
target_os = "android",
17491764
target_os = "freebsd",
17501765
target_os = "netbsd",
17511766
))]
17521767
#[derive(Debug)]
1753-
/// Iterator over results of [`recvmmsg`]/[`sendmmsg`]
1754-
///
1755-
///
17561768
pub struct MultiResults<'a, S> {
17571769
// preallocated structures
17581770
rmm: &'a MultiHeaders<S>,
@@ -1903,7 +1915,7 @@ mod test {
19031915

19041916
let t = sys::time::TimeSpec::from_duration(std::time::Duration::from_secs(10));
19051917

1906-
let recv = super::recvmmsg(rsock.as_raw_fd(), &mut data, recv_iovs.iter(), flags, Some(t))?;
1918+
let recv = super::recvmmsg(rsock.as_raw_fd(), &mut data, recv_iovs.iter_mut(), flags, Some(t))?;
19071919

19081920
for rmsg in recv {
19091921
#[cfg(not(any(qemu, target_arch = "aarch64")))]

test/sys/test_socket.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ mod recvfrom {
565565
let res: Vec<RecvMsg<SockaddrIn>> = recvmmsg(
566566
rsock.as_raw_fd(),
567567
&mut data,
568-
msgs.iter(),
568+
msgs.iter_mut(),
569569
MsgFlags::empty(),
570570
None,
571571
)
@@ -653,7 +653,7 @@ mod recvfrom {
653653
let res: Vec<RecvMsg<SockaddrIn>> = recvmmsg(
654654
rsock.as_raw_fd(),
655655
&mut data,
656-
msgs.iter(),
656+
msgs.iter_mut(),
657657
MsgFlags::MSG_DONTWAIT,
658658
None,
659659
)
@@ -2325,12 +2325,17 @@ fn test_recvmmsg_timestampns() {
23252325
// Receive the message
23262326
let mut buffer = vec![0u8; message.len()];
23272327
let cmsgspace = nix::cmsg_space!(TimeSpec);
2328-
let iov = vec![[IoSliceMut::new(&mut buffer)]];
2328+
let mut iov = vec![[IoSliceMut::new(&mut buffer)]];
23292329
let mut data = MultiHeaders::preallocate(1, Some(cmsgspace));
2330-
let r: Vec<RecvMsg<()>> =
2331-
recvmmsg(in_socket.as_raw_fd(), &mut data, iov.iter(), flags, None)
2332-
.unwrap()
2333-
.collect();
2330+
let r: Vec<RecvMsg<()>> = recvmmsg(
2331+
in_socket.as_raw_fd(),
2332+
&mut data,
2333+
iov.iter_mut(),
2334+
flags,
2335+
None,
2336+
)
2337+
.unwrap()
2338+
.collect();
23342339
let rtime = match r[0].cmsgs().next() {
23352340
Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime,
23362341
Some(_) => panic!("Unexpected control message"),

0 commit comments

Comments
 (0)