Skip to content

Commit f7b62f6

Browse files
committed
Optimize UnixAddr for the BSDs
On BSD-derived operating systems, struct sockaddr has a sa_len field that holds the length of the structure. UnixAddr's path_len field is redundant. Remove path_len on BSD-derived OSes, retaining it only for Illumos and Linux-based OSes. Also, ensure that two UnixAddrs compare equal if they differ only by the presence of a trailing NUL. On Linux, syscalls like getsockname add a trailing NUL to the sockaddr they return, even if no NUL was present on the sockaddr originally passed to the kernel via a syscall like bind, and even though the docs explicitly say that any NUL passed to bind is not considered to be part of the address. Work around this bug by stripping it in UnixAddrKind::get(), so that at least two UnixAddrs will compare identical even if they differ in the presence of a trailing NUL.
1 parent 6d07477 commit f7b62f6

File tree

1 file changed

+54
-21
lines changed

1 file changed

+54
-21
lines changed

src/sys/socket/addr.rs

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::sa_family_t;
2+
use cfg_if::cfg_if;
23
use crate::{Result, NixPath};
34
use crate::errno::Errno;
45
use memoffset::offset_of;
@@ -580,7 +581,13 @@ pub struct UnixAddr {
580581
sun: libc::sockaddr_un,
581582
/// The length of the valid part of `sun`, including the sun_family field
582583
/// but excluding any trailing nul.
583-
sun_len: u8,
584+
// On the BSDs, this field is built into sun
585+
#[cfg(any(target_os = "android",
586+
target_os = "fuchsia",
587+
target_os = "illumos",
588+
target_os = "linux"
589+
))]
590+
sun_len: u8
584591
}
585592

586593
// linux man page unix(7) says there are 3 kinds of unix socket:
@@ -611,7 +618,18 @@ impl<'a> UnixAddrKind<'a> {
611618
return Self::Abstract(name);
612619
}
613620
let pathname = slice::from_raw_parts(sun.sun_path.as_ptr() as *const u8, path_len);
614-
Self::Pathname(Path::new(OsStr::from_bytes(pathname)))
621+
if pathname.last() == Some(&0) {
622+
// A trailing NUL is not considered part of the path, and it does
623+
// not need to be included in the addrlen passed to functions like
624+
// bind(). However, Linux adds a trailing NUL, even if one was not
625+
// originally present, when returning addrs from functions like
626+
// getsockname() (the BSDs do not do that). So we need to filter
627+
// out any trailing NUL here, so sockaddrs can round-trip through
628+
// the kernel and still compare equal.
629+
Self::Pathname(Path::new(OsStr::from_bytes(&pathname[0..pathname.len() - 1])))
630+
} else {
631+
Self::Pathname(Path::new(OsStr::from_bytes(pathname)))
632+
}
615633
}
616634
}
617635

@@ -640,7 +658,6 @@ impl UnixAddr {
640658
target_os = "ios",
641659
target_os = "macos",
642660
target_os = "netbsd",
643-
target_os = "illumos",
644661
target_os = "openbsd"))]
645662
{
646663
ret.sun_len = sun_len;
@@ -657,7 +674,7 @@ impl UnixAddr {
657674
/// Create a new `sockaddr_un` representing an address in the "abstract namespace".
658675
///
659676
/// The leading nul byte for the abstract namespace is automatically added;
660-
/// thus the input `path` is expected to be the bare name, not null-prefixed.
677+
/// thus the input `path` is expected to be the bare name, not NUL-prefixed.
661678
/// This is a Linux-specific extension, primarily used to allow chrooted
662679
/// processes to communicate with processes having a different filesystem view.
663680
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -699,23 +716,24 @@ impl UnixAddr {
699716
/// - if this is a unix addr with a pathname, sun.sun_path is a
700717
/// fs path, not necessarily nul-terminated.
701718
pub(crate) unsafe fn from_raw_parts(sun: libc::sockaddr_un, sun_len: u8) -> UnixAddr {
702-
#[cfg(any(target_os = "dragonfly",
703-
target_os = "freebsd",
704-
target_os = "ios",
705-
target_os = "macos",
706-
target_os = "netbsd",
707-
target_os = "illumos",
708-
target_os = "openbsd"))]
709-
{
710-
assert_eq!(sun_len, sun.sun_len);
719+
cfg_if!{
720+
if #[cfg(any(target_os = "android",
721+
target_os = "fuchsia",
722+
target_os = "illumos",
723+
target_os = "linux"
724+
))]
725+
{
726+
UnixAddr { sun, sun_len }
727+
} else {
728+
assert_eq!(sun_len, sun.sun_len);
729+
UnixAddr {sun}
730+
}
711731
}
712-
713-
UnixAddr { sun, sun_len }
714732
}
715733

716734
fn kind(&self) -> UnixAddrKind<'_> {
717735
// SAFETY: our sockaddr is always valid because of the invariant on the struct
718-
unsafe { UnixAddrKind::get(&self.sun, self.sun_len) }
736+
unsafe { UnixAddrKind::get(&self.sun, self.sun_len()) }
719737
}
720738

721739
/// If this address represents a filesystem path, return that path.
@@ -729,7 +747,7 @@ impl UnixAddr {
729747
/// If this address represents an abstract socket, return its name.
730748
///
731749
/// For abstract sockets only the bare name is returned, without the
732-
/// leading null byte. `None` is returned for unnamed or path-backed sockets.
750+
/// leading NUL byte. `None` is returned for unnamed or path-backed sockets.
733751
#[cfg(any(target_os = "android", target_os = "linux"))]
734752
#[cfg_attr(docsrs, doc(cfg(all())))]
735753
pub fn as_abstract(&self) -> Option<&[u8]> {
@@ -742,7 +760,7 @@ impl UnixAddr {
742760
/// Returns the addrlen of this socket - `offsetof(struct sockaddr_un, sun_path)`
743761
#[inline]
744762
pub fn path_len(&self) -> usize {
745-
self.sun_len as usize - offset_of!(libc::sockaddr_un, sun_path)
763+
self.sun_len() as usize - offset_of!(libc::sockaddr_un, sun_path)
746764
}
747765
/// Returns a pointer to the raw `sockaddr_un` struct
748766
#[inline]
@@ -754,6 +772,21 @@ impl UnixAddr {
754772
pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_un {
755773
&mut self.sun
756774
}
775+
776+
fn sun_len(&self)-> u8 {
777+
cfg_if!{
778+
if #[cfg(any(target_os = "android",
779+
target_os = "fuchsia",
780+
target_os = "illumos",
781+
target_os = "linux"
782+
))]
783+
{
784+
self.sun_len
785+
} else {
786+
self.sun.sun_len
787+
}
788+
}
789+
}
757790
}
758791

759792
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -987,12 +1020,12 @@ impl SockAddr {
9871020
},
9881021
mem::size_of_val(addr) as libc::socklen_t
9891022
),
990-
SockAddr::Unix(UnixAddr { ref sun, sun_len }) => (
1023+
SockAddr::Unix(ref unix_addr) => (
9911024
// This cast is always allowed in C
9921025
unsafe {
993-
&*(sun as *const libc::sockaddr_un as *const libc::sockaddr)
1026+
&*(&unix_addr.sun as *const libc::sockaddr_un as *const libc::sockaddr)
9941027
},
995-
sun_len as libc::socklen_t
1028+
unix_addr.sun_len() as libc::socklen_t
9961029
),
9971030
#[cfg(any(target_os = "android", target_os = "linux"))]
9981031
SockAddr::Netlink(NetlinkAddr(ref sa)) => (

0 commit comments

Comments
 (0)