Skip to content

Commit cfbaa08

Browse files
authored
Exclude NUL characters from OSStrings returned by getsockopt (#2557)
* Exclude NUL characters from OSStrings returned by getsockopt On FreeBSD, getsockopt appends a single NUL to the returned string. On Linux, it pads the entire buffer with NULs. But both of those behaviors may be version-dependent. Adjust GetOsString to handle any number of NUL characters. * fixup: fix MSRV breakage * Fixup: expand the comment
1 parent e7e9809 commit cfbaa08

File tree

3 files changed

+26
-9
lines changed

3 files changed

+26
-9
lines changed

changelog/2557.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Properly exclude NUL characters from `OSString`s returned by `getsockopt`.

src/sys/socket/sockopt.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use crate::{errno::Errno, Result};
77
use cfg_if::cfg_if;
88
use libc::{self, c_int, c_void, socklen_t};
99
#[cfg(apple_targets)]
10-
use std::ffi::{CStr, CString};
11-
use std::ffi::{OsStr, OsString};
10+
use std::ffi::CString;
11+
use std::ffi::{CStr, OsStr, OsString};
1212
use std::mem::{self, MaybeUninit};
1313
use std::os::unix::ffi::OsStrExt;
1414
#[cfg(linux_android)]
@@ -1745,7 +1745,15 @@ impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
17451745
unsafe fn assume_init(self) -> OsString {
17461746
let len = self.len as usize;
17471747
let mut v = unsafe { self.val.assume_init() };
1748-
OsStr::from_bytes(&v.as_mut()[0..len]).to_owned()
1748+
if let Ok(cs) = CStr::from_bytes_until_nul(&v.as_mut()[0..len]) {
1749+
// It's legal for the kernel to return any number of NULs at the
1750+
// end of the string. C applications don't care, after all.
1751+
OsStr::from_bytes(cs.to_bytes())
1752+
} else {
1753+
// Even zero NULs is possible.
1754+
OsStr::from_bytes(&v.as_mut()[0..len])
1755+
}
1756+
.to_owned()
17491757
}
17501758
}
17511759

test/sys/test_sockopt.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,16 +249,16 @@ fn test_so_type_unknown() {
249249
}
250250

251251
// The CI doesn't supported getsockopt and setsockopt on emulated processors.
252-
// It's believed that a QEMU issue, the tests run ok on a fully emulated system.
253-
// Current CI just run the binary with QEMU but the Kernel remains the same as the host.
252+
// It's believed to be a QEMU issue; the tests run ok on a fully emulated
253+
// system. Current CI just runs the binary with QEMU but the kernel remains the
254+
// same as the host.
254255
// So the syscall doesn't work properly unless the kernel is also emulated.
255256
#[test]
256-
#[cfg(all(
257-
any(target_arch = "x86", target_arch = "x86_64"),
258-
any(target_os = "freebsd", target_os = "linux")
259-
))]
257+
#[cfg(any(target_os = "freebsd", target_os = "linux"))]
258+
#[cfg_attr(qemu, ignore)]
260259
fn test_tcp_congestion() {
261260
use std::ffi::OsString;
261+
use std::os::unix::ffi::OsStrExt;
262262

263263
let fd = socket(
264264
AddressFamily::Inet,
@@ -269,6 +269,14 @@ fn test_tcp_congestion() {
269269
.unwrap();
270270

271271
let val = getsockopt(&fd, sockopt::TcpCongestion).unwrap();
272+
let bytes = val.as_os_str().as_bytes();
273+
for b in bytes.iter() {
274+
assert_ne!(
275+
*b, 0,
276+
"OsString should contain no embedded NULs: {:?}",
277+
val
278+
);
279+
}
272280
setsockopt(&fd, sockopt::TcpCongestion, &val).unwrap();
273281

274282
setsockopt(

0 commit comments

Comments
 (0)