Skip to content

Commit ad87c3b

Browse files
geofftcarllerche
authored andcommitted
Fix handling of sockaddr_un lengths
The returned length of AF_UNIX sockaddrs is significant, and generally does not match the length of the entire structure. For filesystem sockets, this is ignorable because the path is also NUL-terminated, but for unbound sockets (e.g., a socketpair) or abstract-namespace sockets (a Linux extension where the address is an arbitrary bytestring), we need to keep track of the length. Fixes #177. Also add a UnixAddr::new_abstract function and some better handling of abstract-namespace socket addresses to fix #169.
1 parent 0cfa2a1 commit ad87c3b

File tree

3 files changed

+64
-21
lines changed

3 files changed

+64
-21
lines changed

src/sys/socket/addr.rs

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use {Result, Error, NixPath};
22
use super::{consts, sa_family_t};
33
use errno::Errno;
44
use libc;
5-
use std::{fmt, hash, mem, net};
6-
use std::ffi::{CStr, OsStr};
5+
use std::{fmt, hash, mem, net, ptr};
6+
use std::ffi::OsStr;
77
use std::path::Path;
88
use std::os::unix::ffi::OsStrExt;
99

@@ -329,45 +329,82 @@ impl fmt::Display for Ipv6Addr {
329329
*
330330
*/
331331

332+
/// A wrapper around sockaddr_un. We track the length of sun_path,
333+
/// because it may not be null-terminated (unconnected and abstract
334+
/// sockets). Note that the actual sockaddr length is greater by
335+
/// size_of::<sa_family_t>().
332336
#[derive(Copy)]
333-
pub struct UnixAddr(pub libc::sockaddr_un);
337+
pub struct UnixAddr(pub libc::sockaddr_un, pub usize);
334338

335339
impl UnixAddr {
340+
/// Create a new sockaddr_un representing a filesystem path.
336341
pub fn new<P: ?Sized + NixPath>(path: &P) -> Result<UnixAddr> {
337-
use libc::strcpy;
338-
339342
try!(path.with_nix_path(|cstr| {
340343
unsafe {
341344
let mut ret = libc::sockaddr_un {
342345
sun_family: AddressFamily::Unix as sa_family_t,
343346
.. mem::zeroed()
344347
};
345348

346-
// Must be smaller to account for the null byte
347-
if path.len() >= ret.sun_path.len() {
349+
let bytes = cstr.to_bytes_with_nul();
350+
351+
if bytes.len() > ret.sun_path.len() {
348352
return Err(Error::Sys(Errno::ENAMETOOLONG));
349353
}
350354

351-
strcpy(ret.sun_path.as_mut_ptr(), cstr.as_ptr());
355+
ptr::copy_nonoverlapping(bytes.as_ptr(),
356+
ret.sun_path.as_mut_ptr() as *mut u8,
357+
bytes.len());
352358

353-
Ok(UnixAddr(ret))
359+
Ok(UnixAddr(ret, bytes.len()))
354360
}
355361
}))
356362
}
357363

358-
pub fn path(&self) -> &Path {
364+
/// Create a new sockaddr_un representing an address in the
365+
/// "abstract namespace". This is a Linux-specific extension,
366+
/// primarily used to allow chrooted processes to communicate with
367+
/// specific daemons.
368+
pub fn new_abstract(path: &[u8]) -> Result<UnixAddr> {
359369
unsafe {
360-
let bytes = CStr::from_ptr(self.0.sun_path.as_ptr()).to_bytes();
361-
Path::new(<OsStr as OsStrExt>::from_bytes(bytes))
370+
let mut ret = libc::sockaddr_un {
371+
sun_family: AddressFamily::Unix as sa_family_t,
372+
.. mem::zeroed()
373+
};
374+
375+
if path.len() > ret.sun_path.len() {
376+
return Err(Error::Sys(Errno::ENAMETOOLONG));
377+
}
378+
379+
// Abstract addresses are represented by sun_path[0] ==
380+
// b'\0', so copy starting one byte in.
381+
ptr::copy_nonoverlapping(path.as_ptr(),
382+
ret.sun_path.as_mut_ptr().offset(1) as *mut u8,
383+
path.len());
384+
385+
Ok(UnixAddr(ret, path.len()))
386+
}
387+
}
388+
389+
fn sun_path(&self) -> &[u8] {
390+
unsafe { mem::transmute(&self.0.sun_path[..self.1]) }
391+
}
392+
393+
/// If this address represents a filesystem path, return that path.
394+
pub fn path(&self) -> Option<&Path> {
395+
if self.1 == 0 || self.0.sun_path[0] == 0 {
396+
// unbound or abstract
397+
None
398+
} else {
399+
let p = self.sun_path();
400+
Some(Path::new(<OsStr as OsStrExt>::from_bytes(&p[..p.len()-1])))
362401
}
363402
}
364403
}
365404

366405
impl PartialEq for UnixAddr {
367406
fn eq(&self, other: &UnixAddr) -> bool {
368-
unsafe {
369-
0 == libc::strcmp(self.0.sun_path.as_ptr(), other.0.sun_path.as_ptr())
370-
}
407+
self.sun_path() == other.sun_path()
371408
}
372409
}
373410

@@ -376,7 +413,7 @@ impl Eq for UnixAddr {
376413

377414
impl hash::Hash for UnixAddr {
378415
fn hash<H: hash::Hasher>(&self, s: &mut H) {
379-
( self.0.sun_family, self.path() ).hash(s)
416+
( self.0.sun_family, self.sun_path() ).hash(s)
380417
}
381418
}
382419

@@ -388,7 +425,14 @@ impl Clone for UnixAddr {
388425

389426
impl fmt::Display for UnixAddr {
390427
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
391-
self.path().display().fmt(f)
428+
if self.1 == 0 {
429+
f.write_str("<unbound UNIX socket>")
430+
} else if let Some(path) = self.path() {
431+
path.display().fmt(f)
432+
} else {
433+
let display = String::from_utf8_lossy(&self.sun_path()[1..]);
434+
write!(f, "@{}", display)
435+
}
392436
}
393437
}
394438

@@ -430,7 +474,7 @@ impl SockAddr {
430474
match *self {
431475
SockAddr::Inet(InetAddr::V4(ref addr)) => (mem::transmute(addr), mem::size_of::<libc::sockaddr_in>() as libc::socklen_t),
432476
SockAddr::Inet(InetAddr::V6(ref addr)) => (mem::transmute(addr), mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t),
433-
SockAddr::Unix(UnixAddr(ref addr)) => (mem::transmute(addr), mem::size_of::<libc::sockaddr_un>() as libc::socklen_t),
477+
SockAddr::Unix(UnixAddr(ref addr, len)) => (mem::transmute(addr), (len + mem::size_of::<libc::sa_family_t>()) as libc::socklen_t),
434478
}
435479
}
436480
}

src/sys/socket/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -687,8 +687,7 @@ pub unsafe fn sockaddr_storage_to_addr(
687687
Ok(SockAddr::Inet(InetAddr::V6((*(addr as *const _ as *const sockaddr_in6)))))
688688
}
689689
consts::AF_UNIX => {
690-
assert!(len as usize == mem::size_of::<sockaddr_un>());
691-
Ok(SockAddr::Unix(UnixAddr(*(addr as *const _ as *const sockaddr_un))))
690+
Ok(SockAddr::Unix(UnixAddr(*(addr as *const _ as *const sockaddr_un), len)))
692691
}
693692
af => panic!("unexpected address family {}", af),
694693
}

test/sys/test_socket.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub fn test_path_to_sock_addr() {
3535
let expect: &'static [i8] = unsafe { mem::transmute(&b"/foo/bar"[..]) };
3636
assert_eq!(&addr.0.sun_path[..8], expect);
3737

38-
assert_eq!(addr.path(), actual);
38+
assert_eq!(addr.path(), Some(actual));
3939
}
4040

4141
#[test]

0 commit comments

Comments
 (0)