about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <dylan.dpc@gmail.com>2021-03-30 00:32:21 +0200
committerGitHub <noreply@github.com>2021-03-30 00:32:21 +0200
commit772582e19ef366e4be686bfcfd65667dc033fe3a (patch)
tree0a485e095e1e3e5adc5285392055d2ae13fe6063
parent68964d1fc24888b04be492c3f3ba0dae7d6219da (diff)
parent3d6bd87b24a2fbccb6c1e81863874789eb046c17 (diff)
downloadrust-772582e19ef366e4be686bfcfd65667dc033fe3a.tar.gz
rust-772582e19ef366e4be686bfcfd65667dc033fe3a.zip
Rollup merge of #83374 - reyk:fix/bsd-ancillary, r=joshtriplett
unix: Fix feature(unix_socket_ancillary_data) on macos and other BSDs

This adds support for CMSG handling on macOS and fixes it on OpenBSD and possibly other BSDs.

When traversing the CMSG list, the previous code had an exception for Android where the next element after the last pointer could point to the first pointer instead of NULL.  This is actually not specific to Android: the `libc::CMSG_NXTHDR` implementation for Linux and emscripten have a special case to return NULL when the length of the previous element is zero; most other implementations simply return the previous element plus a zero offset in this case.

This MR makes the check non-optional which fixes CMSG handling and a possible endless loop on such systems; tested with file descriptor passing on OpenBSD, Linux, and macOS.

This MR additionally adds `SocketAncillary::is_empty` because clippy is right that it should be added.

This belongs to the `feature(unix_socket_ancillary_data)` tracking issue:  https://github.com/rust-lang/rust/issues/76915

r? `@joshtriplett`
-rw-r--r--library/std/src/sys/unix/ext/net/ancillary.rs57
1 files changed, 34 insertions, 23 deletions
diff --git a/library/std/src/sys/unix/ext/net/ancillary.rs b/library/std/src/sys/unix/ext/net/ancillary.rs
index 33d6a39af07..011ae643f87 100644
--- a/library/std/src/sys/unix/ext/net/ancillary.rs
+++ b/library/std/src/sys/unix/ext/net/ancillary.rs
@@ -5,9 +5,7 @@ use crate::marker::PhantomData;
 use crate::mem::{size_of, zeroed};
 use crate::os::unix::io::RawFd;
 use crate::path::Path;
-#[cfg(target_os = "android")]
-use crate::ptr::eq;
-use crate::ptr::read_unaligned;
+use crate::ptr::{eq, read_unaligned};
 use crate::slice::from_raw_parts;
 use crate::sys::net::Socket;
 
@@ -30,12 +28,10 @@ pub(super) fn recv_vectored_with_ancillary_from(
 ) -> io::Result<(usize, bool, io::Result<SocketAddr>)> {
     unsafe {
         let mut msg_name: libc::sockaddr_un = zeroed();
-
         let mut msg: libc::msghdr = zeroed();
         msg.msg_name = &mut msg_name as *mut _ as *mut _;
         msg.msg_namelen = size_of::<libc::sockaddr_un>() as libc::socklen_t;
         msg.msg_iov = bufs.as_mut_ptr().cast();
-        msg.msg_control = ancillary.buffer.as_mut_ptr().cast();
         cfg_if::cfg_if! {
             if #[cfg(any(target_os = "android", all(target_os = "linux", target_env = "gnu")))] {
                 msg.msg_iovlen = bufs.len() as libc::size_t;
@@ -45,6 +41,7 @@ pub(super) fn recv_vectored_with_ancillary_from(
                           target_os = "emscripten",
                           target_os = "freebsd",
                           all(target_os = "linux", target_env = "musl",),
+                          target_os = "macos",
                           target_os = "netbsd",
                           target_os = "openbsd",
                       ))] {
@@ -52,6 +49,10 @@ pub(super) fn recv_vectored_with_ancillary_from(
                 msg.msg_controllen = ancillary.buffer.len() as libc::socklen_t;
             }
         }
+        // macos requires that the control pointer is NULL when the len is 0.
+        if msg.msg_controllen > 0 {
+            msg.msg_control = ancillary.buffer.as_mut_ptr().cast();
+        }
 
         let count = socket.recv_msg(&mut msg)?;
 
@@ -79,7 +80,6 @@ pub(super) fn send_vectored_with_ancillary_to(
         msg.msg_name = &mut msg_name as *mut _ as *mut _;
         msg.msg_namelen = msg_namelen;
         msg.msg_iov = bufs.as_ptr() as *mut _;
-        msg.msg_control = ancillary.buffer.as_mut_ptr().cast();
         cfg_if::cfg_if! {
             if #[cfg(any(target_os = "android", all(target_os = "linux", target_env = "gnu")))] {
                 msg.msg_iovlen = bufs.len() as libc::size_t;
@@ -89,6 +89,7 @@ pub(super) fn send_vectored_with_ancillary_to(
                           target_os = "emscripten",
                           target_os = "freebsd",
                           all(target_os = "linux", target_env = "musl",),
+                          target_os = "macos",
                           target_os = "netbsd",
                           target_os = "openbsd",
                       ))] {
@@ -96,6 +97,10 @@ pub(super) fn send_vectored_with_ancillary_to(
                 msg.msg_controllen = ancillary.length as libc::socklen_t;
             }
         }
+        // macos requires that the control pointer is NULL when the len is 0.
+        if msg.msg_controllen > 0 {
+            msg.msg_control = ancillary.buffer.as_mut_ptr().cast();
+        }
 
         ancillary.truncated = false;
 
@@ -147,6 +152,7 @@ fn add_to_ancillary_data<T>(
                           target_os = "emscripten",
                           target_os = "freebsd",
                           all(target_os = "linux", target_env = "musl",),
+                          target_os = "macos",
                           target_os = "netbsd",
                           target_os = "openbsd",
                       ))] {
@@ -159,14 +165,12 @@ fn add_to_ancillary_data<T>(
         while !cmsg.is_null() {
             previous_cmsg = cmsg;
             cmsg = libc::CMSG_NXTHDR(&msg, cmsg);
-            cfg_if::cfg_if! {
-                // Android return the same pointer if it is the last cmsg.
-                // Therefore, check it if the previous pointer is the same as the current one.
-                if #[cfg(target_os = "android")] {
-                    if cmsg == previous_cmsg {
-                        break;
-                    }
-                }
+
+            // Most operating systems, but not Linux or emscripten, return the previous pointer
+            // when its length is zero. Therefore, check if the previous pointer is the same as
+            // the current one.
+            if eq(cmsg, previous_cmsg) {
+                break;
             }
         }
 
@@ -184,6 +188,7 @@ fn add_to_ancillary_data<T>(
                           target_os = "emscripten",
                           target_os = "freebsd",
                           all(target_os = "linux", target_env = "musl",),
+                          target_os = "macos",
                           target_os = "netbsd",
                           target_os = "openbsd",
                       ))] {
@@ -371,6 +376,7 @@ impl<'a> AncillaryData<'a> {
                               target_os = "emscripten",
                               target_os = "freebsd",
                               all(target_os = "linux", target_env = "musl",),
+                              target_os = "macos",
                               target_os = "netbsd",
                               target_os = "openbsd",
                           ))] {
@@ -421,6 +427,7 @@ impl<'a> Iterator for Messages<'a> {
                               target_os = "emscripten",
                               target_os = "freebsd",
                               all(target_os = "linux", target_env = "musl",),
+                              target_os = "macos",
                               target_os = "netbsd",
                               target_os = "openbsd",
                           ))] {
@@ -435,15 +442,13 @@ impl<'a> Iterator for Messages<'a> {
             };
 
             let cmsg = cmsg.as_ref()?;
-            cfg_if::cfg_if! {
-                // Android return the same pointer if it is the last cmsg.
-                // Therefore, check it if the previous pointer is the same as the current one.
-                if #[cfg(target_os = "android")] {
-                    if let Some(current) = self.current {
-                        if eq(current, cmsg) {
-                            return None;
-                        }
-                    }
+
+            // Most operating systems, but not Linux or emscripten, return the previous pointer
+            // when its length is zero. Therefore, check if the previous pointer is the same as
+            // the current one.
+            if let Some(current) = self.current {
+                if eq(current, cmsg) {
+                    return None;
                 }
             }
 
@@ -514,6 +519,12 @@ impl<'a> SocketAncillary<'a> {
         self.buffer.len()
     }
 
+    /// Returns `true` if the ancillary data is empty.
+    #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
+    pub fn is_empty(&self) -> bool {
+        self.length == 0
+    }
+
     /// Returns the number of used bytes.
     #[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
     pub fn len(&self) -> usize {