From 0d0ba07a9dbaf1e2451e95c33f5e14be63b90755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= Date: Mon, 3 Mar 2025 18:48:44 +0100 Subject: [PATCH] Allocated `SocketAddrUnix::path()` with correct length Using `.to_owned()` + `.push()` will cause a reallocation, because the initially allocated array with be one byte too short. We can use `CString::from_vec_with_nul_unchecked()` because it is a known invariant that the input does not contain any `NUL`s, not even the terminating `NUL`. --- src/backend/libc/net/addr.rs | 21 +++++++++++++-------- src/backend/linux_raw/net/addr.rs | 21 +++++++++++++-------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/backend/libc/net/addr.rs b/src/backend/libc/net/addr.rs index 7679deac5..c730f3163 100644 --- a/src/backend/libc/net/addr.rs +++ b/src/backend/libc/net/addr.rs @@ -14,10 +14,7 @@ use { core::slice, }; #[cfg(all(unix, feature = "alloc"))] -use { - crate::ffi::CString, - alloc::borrow::{Cow, ToOwned}, -}; +use {crate::ffi::CString, alloc::borrow::Cow, alloc::vec::Vec}; /// `struct sockaddr_un` #[cfg(unix)] @@ -143,7 +140,8 @@ impl SocketAddrUnix { let bytes = self.bytes()?; if !bytes.is_empty() && bytes[0] != 0 { if self.unix.sun_path.len() == bytes.len() { - self.path_with_termination(bytes) + // SAFETY: no NULs are contained in bytes + unsafe { self.path_with_termination(bytes) } } else { // SAFETY: `from_bytes_with_nul_unchecked` since the string is // NUL-terminated. @@ -155,11 +153,18 @@ impl SocketAddrUnix { } /// If the `sun_path` field is not NUL-terminated, terminate it. + /// + /// SAFETY: the input `bytes` must not contain any NULs #[cfg(feature = "alloc")] - fn path_with_termination(&self, bytes: &[u8]) -> Option> { - let mut owned = bytes.to_owned(); + unsafe fn path_with_termination(&self, bytes: &[u8]) -> Option> { + let mut owned = Vec::with_capacity(bytes.len() + 1); + owned.extend_from_slice(bytes); owned.push(b'\0'); - Some(CString::from_vec_with_nul(owned).unwrap().into()) + // SAFETY: `from_vec_with_nul_unchecked` since the string is + // NUL-terminated and `bytes` does not conain any NULs. + Some(Cow::Owned( + CString::from_vec_with_nul_unchecked(owned).into(), + )) } /// For a filesystem path address, return the path as a byte sequence, diff --git a/src/backend/linux_raw/net/addr.rs b/src/backend/linux_raw/net/addr.rs index 11aa5d388..41b004b37 100644 --- a/src/backend/linux_raw/net/addr.rs +++ b/src/backend/linux_raw/net/addr.rs @@ -15,10 +15,7 @@ use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::{fmt, slice}; #[cfg(feature = "alloc")] -use { - crate::ffi::CString, - alloc::borrow::{Cow, ToOwned}, -}; +use {crate::ffi::CString, alloc::borrow::Cow, alloc::vec::Vec}; /// `struct sockaddr_un` #[derive(Clone)] @@ -105,7 +102,8 @@ impl SocketAddrUnix { let bytes = self.bytes()?; if !bytes.is_empty() && bytes[0] != 0 { if self.unix.sun_path.len() == bytes.len() { - self.path_with_termination(bytes) + // SAFETY: no NULs are contained in bytes + unsafe { self.path_with_termination(bytes) } } else { // SAFETY: `from_bytes_with_nul_unchecked` since the string is // NUL-terminated. @@ -117,11 +115,18 @@ impl SocketAddrUnix { } /// If the `sun_path` field is not NUL-terminated, terminate it. + /// + /// SAFETY: the input `bytes` must not contain any NULs #[cfg(feature = "alloc")] - fn path_with_termination(&self, bytes: &[u8]) -> Option> { - let mut owned = bytes.to_owned(); + unsafe fn path_with_termination(&self, bytes: &[u8]) -> Option> { + let mut owned = Vec::with_capacity(bytes.len() + 1); + owned.extend_from_slice(bytes); owned.push(b'\0'); - Some(CString::from_vec_with_nul(owned).unwrap().into()) + // SAFETY: `from_vec_with_nul_unchecked` since the string is + // NUL-terminated and `bytes` does not conain any NULs. + Some(Cow::Owned( + CString::from_vec_with_nul_unchecked(owned).into(), + )) } /// For a filesystem path address, return the path as a byte sequence,