Skip to content

Commit 05eee29

Browse files
bors[bot]JarredAllen
authored andcommitted
Merge #2041
2041: Set the length of a socket address when calling `recvmsg` on Linux r=asomers a=JarredAllen # Background I think I've found a bug with `recvmsg` on Linux where it doesn't set the length of a received socket address. I found this while working with unix datagram sockets with path `struct sockaddr_un` addresses, but I think this applies to any variable-length socket address type. At present, the `sun_len` field of `UnixAddr` on Linux shows up as 0 whenever I call `recvmsg`. I think it's reading uninitialized memory (afaict the `recvmsg` function never initializes it before we call `MaybeUninit::assume_init`), though it's possible that it's being zero-initialized somewhere that I missed. Either way, this isn't the correct behavior, which should set it to the length of the `struct sockaddr_un` contents (or whatever other type of socket). # Changes I changed the `recvmsg` function to construct the returned socket address from the `struct sockaddr` and length returned by the libc `recvmsg` call (using the `from_raw` function the trait provides). Since the trait is sealed so downstream crates can't implement it, I believe this shouldn't be a breaking change. # Validation I've tested that my code (which failed due to this bug) now works. I also added a new test case which tests that we construct `UnixAddr`s correctly in the `recvmsg` function, which passes locally for me and fails if I cherry-pick it onto the current `master`. I've also checked that `cargo test` and `cargo clippy` both still pass on `aarch64-apple-darwin` and on `aarch64-unknown-linux-musl` targets (the two targets I develop for/have access to). Hopefully your CI will confirm that everything else still works. Co-authored-by: Jarred Allen <[email protected]> Co-authored-by: Jarred Allen <[email protected]>
1 parent 4f80a18 commit 05eee29

File tree

4 files changed

+113
-5
lines changed

4 files changed

+113
-5
lines changed

CHANGELOG.md

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

88
### Fixed
9+
- Fix: send `ETH_P_ALL` in htons format
10+
([#1925](https://github.com/nix-rust/nix/pull/1925))
11+
- Fix: `recvmsg` now sets the length of the received `sockaddr_un` field
12+
correctly on Linux platforms. ([#2041](https://github.com/nix-rust/nix/pull/2041))
13+
- Fix potentially invalid conversions in
14+
`SockaddrIn::from<std::net::SocketAddrV4>`,
15+
`SockaddrIn6::from<std::net::SockaddrV6>`, `IpMembershipRequest::new`, and
16+
`Ipv6MembershipRequest::new` with future Rust versions.
17+
([#2061](https://github.com/nix-rust/nix/pull/2061))
18+
- Fixed an incorrect lifetime returned from `recvmsg`.
19+
([#2095](https://github.com/nix-rust/nix/pull/2095))
920

1021
## [0.26.2] - 2023-01-18
1122
### Fixed

src/sys/socket/addr.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,22 @@ impl SockaddrLike for UnixAddr {
10491049
{
10501050
mem::size_of::<libc::sockaddr_un>() as libc::socklen_t
10511051
}
1052+
1053+
unsafe fn set_length(&mut self, new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic> {
1054+
// `new_length` is only used on some platforms, so it must be provided even when not used
1055+
#![allow(unused_variables)]
1056+
cfg_if! {
1057+
if #[cfg(any(target_os = "android",
1058+
target_os = "fuchsia",
1059+
target_os = "illumos",
1060+
target_os = "linux",
1061+
target_os = "redox",
1062+
))] {
1063+
self.sun_len = new_length as u8;
1064+
}
1065+
};
1066+
Ok(())
1067+
}
10521068
}
10531069

10541070
impl AsRef<libc::sockaddr_un> for UnixAddr {
@@ -1198,7 +1214,32 @@ pub trait SockaddrLike: private::SockaddrLikePriv {
11981214
{
11991215
mem::size_of::<Self>() as libc::socklen_t
12001216
}
1217+
1218+
/// Set the length of this socket address
1219+
///
1220+
/// This method may only be called on socket addresses whose lengths are dynamic, and it
1221+
/// returns an error if called on a type whose length is static.
1222+
///
1223+
/// # Safety
1224+
///
1225+
/// `new_length` must be a valid length for this type of address. Specifically, reads of that
1226+
/// length from `self` must be valid.
1227+
#[doc(hidden)]
1228+
unsafe fn set_length(&mut self, _new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic> {
1229+
Err(SocketAddressLengthNotDynamic)
1230+
}
1231+
}
1232+
1233+
/// The error returned by [`SockaddrLike::set_length`] on an address whose length is statically
1234+
/// fixed.
1235+
#[derive(Copy, Clone, Debug)]
1236+
pub struct SocketAddressLengthNotDynamic;
1237+
impl fmt::Display for SocketAddressLengthNotDynamic {
1238+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1239+
f.write_str("Attempted to set length on socket whose length is statically fixed")
1240+
}
12011241
}
1242+
impl std::error::Error for SocketAddressLengthNotDynamic {}
12021243

12031244
impl private::SockaddrLikePriv for () {
12041245
fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
@@ -1645,6 +1686,15 @@ impl SockaddrLike for SockaddrStorage {
16451686
None => mem::size_of_val(self) as libc::socklen_t
16461687
}
16471688
}
1689+
1690+
unsafe fn set_length(&mut self, new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic> {
1691+
match self.as_unix_addr_mut() {
1692+
Some(addr) => {
1693+
addr.set_length(new_length)
1694+
},
1695+
None => Err(SocketAddressLengthNotDynamic),
1696+
}
1697+
}
16481698
}
16491699

16501700
macro_rules! accessors {
@@ -1961,7 +2011,7 @@ impl PartialEq for SockaddrStorage {
19612011
}
19622012
}
19632013

1964-
mod private {
2014+
pub(super) mod private {
19652015
pub trait SockaddrLikePriv {
19662016
/// Returns a mutable raw pointer to the inner structure.
19672017
///
@@ -2850,7 +2900,6 @@ mod datalink {
28502900
&self.0
28512901
}
28522902
}
2853-
28542903
}
28552904
}
28562905

src/sys/socket/mod.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,7 +1611,7 @@ impl<S> MultiHeaders<S> {
16111611
{
16121612
// we will be storing pointers to addresses inside mhdr - convert it into boxed
16131613
// slice so it can'be changed later by pushing anything into self.addresses
1614-
let mut addresses = vec![std::mem::MaybeUninit::uninit(); num_slices].into_boxed_slice();
1614+
let mut addresses = vec![std::mem::MaybeUninit::<S>::uninit(); num_slices].into_boxed_slice();
16151615

16161616
let msg_controllen = cmsg_buffer.as_ref().map_or(0, |v| v.capacity());
16171617

@@ -1916,7 +1916,7 @@ unsafe fn read_mhdr<'a, 'i, S>(
19161916
mhdr: msghdr,
19171917
r: isize,
19181918
msg_controllen: usize,
1919-
address: S,
1919+
mut address: S,
19201920
) -> RecvMsg<'a, 'i, S>
19211921
where S: SockaddrLike
19221922
{
@@ -1932,6 +1932,11 @@ unsafe fn read_mhdr<'a, 'i, S>(
19321932
}.as_ref()
19331933
};
19341934

1935+
// Ignore errors if this socket address has statically-known length
1936+
//
1937+
// This is to ensure that unix socket addresses have their length set appropriately.
1938+
let _ = address.set_length(mhdr.msg_namelen as usize);
1939+
19351940
RecvMsg {
19361941
bytes: r as usize,
19371942
cmsghdr,
@@ -1967,7 +1972,7 @@ unsafe fn pack_mhdr_to_receive<S>(
19671972
// initialize it.
19681973
let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
19691974
let p = mhdr.as_mut_ptr();
1970-
(*p).msg_name = (*address).as_mut_ptr() as *mut c_void;
1975+
(*p).msg_name = address as *mut c_void;
19711976
(*p).msg_namelen = S::size();
19721977
(*p).msg_iov = iov_buffer as *mut iovec;
19731978
(*p).msg_iovlen = iov_buffer_len as _;

test/sys/test_socket.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,49 @@ pub fn test_socketpair() {
298298
assert_eq!(&buf[..], b"hello");
299299
}
300300

301+
#[test]
302+
pub fn test_recvmsg_sockaddr_un() {
303+
use nix::sys::socket::{
304+
self, bind, socket, AddressFamily, MsgFlags, SockFlag, SockType,
305+
};
306+
307+
let tempdir = tempfile::tempdir().unwrap();
308+
let sockname = tempdir.path().join("sock");
309+
let sock = socket(
310+
AddressFamily::Unix,
311+
SockType::Datagram,
312+
SockFlag::empty(),
313+
None,
314+
)
315+
.expect("socket failed");
316+
let sockaddr = UnixAddr::new(&sockname).unwrap();
317+
bind(sock, &sockaddr).expect("bind failed");
318+
319+
// Send a message
320+
let send_buffer = "hello".as_bytes();
321+
if let Err(e) = socket::sendmsg(
322+
sock,
323+
&[std::io::IoSlice::new(send_buffer)],
324+
&[],
325+
MsgFlags::empty(),
326+
Some(&sockaddr),
327+
) {
328+
crate::skip!("Couldn't send ({:?}), so skipping test", e);
329+
}
330+
331+
// Receive the message
332+
let mut recv_buffer = [0u8; 32];
333+
let received = socket::recvmsg(
334+
sock,
335+
&mut [std::io::IoSliceMut::new(&mut recv_buffer)],
336+
None,
337+
MsgFlags::empty(),
338+
)
339+
.unwrap();
340+
// Check the address in the received message
341+
assert_eq!(sockaddr, received.address.unwrap());
342+
}
343+
301344
#[test]
302345
pub fn test_std_conversions() {
303346
use nix::sys::socket::*;

0 commit comments

Comments
 (0)