Skip to content

Commit b5a23c7

Browse files
bors[bot]asomers
andauthored
Merge #1618
1618: Refactor UnixAddr r=asomers a=asomers * Within UnixAddr, replace the path_len variable (length of the sun_path field) with sun_len (length of the whole structure). This is more similar to how other sockaddr types work, and it's the same way that the BSDs use the sun_len field. Also, don't require that sun_path be nul-terminated. The OS doesn't require it. * 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. Co-authored-by: Alan Somers <[email protected]>
2 parents b9eb197 + f7b62f6 commit b5a23c7

File tree

2 files changed

+95
-32
lines changed

2 files changed

+95
-32
lines changed

src/sys/socket/addr.rs

Lines changed: 92 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
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;
56
use std::{fmt, mem, net, ptr, slice};
7+
use std::convert::TryInto;
68
use std::ffi::OsStr;
79
use std::hash::{Hash, Hasher};
810
use std::path::Path;
@@ -575,9 +577,17 @@ impl fmt::Display for Ipv6Addr {
575577
/// A wrapper around `sockaddr_un`.
576578
#[derive(Clone, Copy, Debug)]
577579
pub struct UnixAddr {
578-
// INVARIANT: sun & path_len are valid as defined by docs for from_raw_parts
580+
// INVARIANT: sun & sun_len are valid as defined by docs for from_raw_parts
579581
sun: libc::sockaddr_un,
580-
path_len: usize,
582+
/// The length of the valid part of `sun`, including the sun_family field
583+
/// but excluding any trailing nul.
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
581591
}
582592

583593
// linux man page unix(7) says there are 3 kinds of unix socket:
@@ -594,8 +604,10 @@ enum UnixAddrKind<'a> {
594604
Abstract(&'a [u8]),
595605
}
596606
impl<'a> UnixAddrKind<'a> {
597-
/// Safety: sun & path_len must be valid
598-
unsafe fn get(sun: &'a libc::sockaddr_un, path_len: usize) -> Self {
607+
/// Safety: sun & sun_len must be valid
608+
unsafe fn get(sun: &'a libc::sockaddr_un, sun_len: u8) -> Self {
609+
assert!(sun_len as usize >= offset_of!(libc::sockaddr_un, sun_path));
610+
let path_len = sun_len as usize - offset_of!(libc::sockaddr_un, sun_path);
599611
if path_len == 0 {
600612
return Self::Unnamed;
601613
}
@@ -605,8 +617,19 @@ impl<'a> UnixAddrKind<'a> {
605617
slice::from_raw_parts(sun.sun_path.as_ptr().add(1) as *const u8, path_len - 1);
606618
return Self::Abstract(name);
607619
}
608-
let pathname = slice::from_raw_parts(sun.sun_path.as_ptr() as *const u8, path_len - 1);
609-
Self::Pathname(Path::new(OsStr::from_bytes(pathname)))
620+
let pathname = slice::from_raw_parts(sun.sun_path.as_ptr() as *const u8, path_len);
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+
}
610633
}
611634
}
612635

@@ -626,19 +649,32 @@ impl UnixAddr {
626649
return Err(Errno::ENAMETOOLONG);
627650
}
628651

652+
let sun_len = (bytes.len() +
653+
offset_of!(libc::sockaddr_un, sun_path)).try_into()
654+
.unwrap();
655+
656+
#[cfg(any(target_os = "dragonfly",
657+
target_os = "freebsd",
658+
target_os = "ios",
659+
target_os = "macos",
660+
target_os = "netbsd",
661+
target_os = "openbsd"))]
662+
{
663+
ret.sun_len = sun_len;
664+
}
629665
ptr::copy_nonoverlapping(bytes.as_ptr(),
630666
ret.sun_path.as_mut_ptr() as *mut u8,
631667
bytes.len());
632668

633-
Ok(UnixAddr::from_raw_parts(ret, bytes.len() + 1))
669+
Ok(UnixAddr::from_raw_parts(ret, sun_len))
634670
}
635671
})?
636672
}
637673

638674
/// Create a new `sockaddr_un` representing an address in the "abstract namespace".
639675
///
640-
/// The leading null byte for the abstract namespace is automatically added;
641-
/// thus the input `path` is expected to be the bare name, not null-prefixed.
676+
/// The leading nul byte for the abstract namespace is automatically added;
677+
/// thus the input `path` is expected to be the bare name, not NUL-prefixed.
642678
/// This is a Linux-specific extension, primarily used to allow chrooted
643679
/// processes to communicate with processes having a different filesystem view.
644680
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -653,39 +689,51 @@ impl UnixAddr {
653689
if path.len() >= ret.sun_path.len() {
654690
return Err(Errno::ENAMETOOLONG);
655691
}
692+
let sun_len = (path.len() +
693+
1 +
694+
offset_of!(libc::sockaddr_un, sun_path)).try_into()
695+
.unwrap();
656696

657697
// Abstract addresses are represented by sun_path[0] ==
658698
// b'\0', so copy starting one byte in.
659699
ptr::copy_nonoverlapping(path.as_ptr(),
660700
ret.sun_path.as_mut_ptr().offset(1) as *mut u8,
661701
path.len());
662702

663-
Ok(UnixAddr::from_raw_parts(ret, path.len() + 1))
703+
Ok(UnixAddr::from_raw_parts(ret, sun_len))
664704
}
665705
}
666706

667-
/// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `path_len` is the "addrlen"
668-
/// of this address, but minus `offsetof(struct sockaddr_un, sun_path)`. Basically the length
669-
/// of the data in `sun_path`.
707+
/// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `sun_len`
708+
/// is the size of the valid portion of the struct, excluding any trailing
709+
/// NUL.
670710
///
671711
/// # Safety
672-
/// This pair of sockaddr_un & path_len must be a valid unix addr, which means:
673-
/// - path_len <= sockaddr_un.sun_path.len()
674-
/// - if this is a unix addr with a pathname, sun.sun_path is a nul-terminated fs path and
675-
/// sun.sun_path[path_len - 1] == 0 || sun.sun_path[path_len] == 0
676-
pub(crate) unsafe fn from_raw_parts(sun: libc::sockaddr_un, mut path_len: usize) -> UnixAddr {
677-
if let UnixAddrKind::Pathname(_) = UnixAddrKind::get(&sun, path_len) {
678-
if sun.sun_path[path_len - 1] != 0 {
679-
assert_eq!(sun.sun_path[path_len], 0);
680-
path_len += 1
712+
/// This pair of sockaddr_un & sun_len must be a valid unix addr, which
713+
/// means:
714+
/// - sun_len >= offset_of(sockaddr_un, sun_path)
715+
/// - sun_len <= sockaddr_un.sun_path.len() - offset_of(sockaddr_un, sun_path)
716+
/// - if this is a unix addr with a pathname, sun.sun_path is a
717+
/// fs path, not necessarily nul-terminated.
718+
pub(crate) unsafe fn from_raw_parts(sun: libc::sockaddr_un, sun_len: u8) -> UnixAddr {
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}
681730
}
682731
}
683-
UnixAddr { sun, path_len }
684732
}
685733

686734
fn kind(&self) -> UnixAddrKind<'_> {
687735
// SAFETY: our sockaddr is always valid because of the invariant on the struct
688-
unsafe { UnixAddrKind::get(&self.sun, self.path_len) }
736+
unsafe { UnixAddrKind::get(&self.sun, self.sun_len()) }
689737
}
690738

691739
/// If this address represents a filesystem path, return that path.
@@ -699,7 +747,7 @@ impl UnixAddr {
699747
/// If this address represents an abstract socket, return its name.
700748
///
701749
/// For abstract sockets only the bare name is returned, without the
702-
/// 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.
703751
#[cfg(any(target_os = "android", target_os = "linux"))]
704752
#[cfg_attr(docsrs, doc(cfg(all())))]
705753
pub fn as_abstract(&self) -> Option<&[u8]> {
@@ -712,7 +760,7 @@ impl UnixAddr {
712760
/// Returns the addrlen of this socket - `offsetof(struct sockaddr_un, sun_path)`
713761
#[inline]
714762
pub fn path_len(&self) -> usize {
715-
self.path_len
763+
self.sun_len() as usize - offset_of!(libc::sockaddr_un, sun_path)
716764
}
717765
/// Returns a pointer to the raw `sockaddr_un` struct
718766
#[inline]
@@ -724,6 +772,21 @@ impl UnixAddr {
724772
pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_un {
725773
&mut self.sun
726774
}
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+
}
727790
}
728791

729792
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -957,12 +1020,12 @@ impl SockAddr {
9571020
},
9581021
mem::size_of_val(addr) as libc::socklen_t
9591022
),
960-
SockAddr::Unix(UnixAddr { ref sun, path_len }) => (
1023+
SockAddr::Unix(ref unix_addr) => (
9611024
// This cast is always allowed in C
9621025
unsafe {
963-
&*(sun as *const libc::sockaddr_un as *const libc::sockaddr)
1026+
&*(&unix_addr.sun as *const libc::sockaddr_un as *const libc::sockaddr)
9641027
},
965-
(path_len + offset_of!(libc::sockaddr_un, sun_path)) as libc::socklen_t
1028+
unix_addr.sun_len() as libc::socklen_t
9661029
),
9671030
#[cfg(any(target_os = "android", target_os = "linux"))]
9681031
SockAddr::Netlink(NetlinkAddr(ref sa)) => (

src/sys/socket/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use cfg_if::cfg_if;
55
use crate::{Result, errno::Errno};
66
use libc::{self, c_void, c_int, iovec, socklen_t, size_t,
77
CMSG_FIRSTHDR, CMSG_NXTHDR, CMSG_DATA, CMSG_LEN};
8-
use memoffset::offset_of;
8+
use std::convert::TryInto;
99
use std::{mem, ptr, slice};
1010
use std::os::unix::io::RawFd;
1111
#[cfg(target_os = "linux")]
@@ -2007,10 +2007,10 @@ pub fn sockaddr_storage_to_addr(
20072007
Ok(SockAddr::Inet(InetAddr::V6(sin6)))
20082008
}
20092009
libc::AF_UNIX => {
2010-
let pathlen = len - offset_of!(sockaddr_un, sun_path);
20112010
unsafe {
20122011
let sun = *(addr as *const _ as *const sockaddr_un);
2013-
Ok(SockAddr::Unix(UnixAddr::from_raw_parts(sun, pathlen)))
2012+
let sun_len = len.try_into().unwrap();
2013+
Ok(SockAddr::Unix(UnixAddr::from_raw_parts(sun, sun_len)))
20142014
}
20152015
}
20162016
#[cfg(any(target_os = "android", target_os = "linux"))]

0 commit comments

Comments
 (0)