From 00ac34d716f8f4cf834f61e03fba25a82d0ae240 Mon Sep 17 00:00:00 2001 From: Stefan Holderbach Date: Mon, 28 Jul 2025 08:42:23 +0200 Subject: [PATCH] Port `unsafe_op_in_unsafe_fn` fix to FreeBSD (#16275) Same general idea as https://github.com/nushell/nushell/pull/16266 Fixes the 2024 edition [`unsafe_op_in_unsafe_fn`](https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html) lint for FreeBSD as well Add safety comments to both implementations and an assertion before `MaybeUninit::assume_init` --- crates/nu-system/src/freebsd.rs | 40 +++++++++++++++++++++-------- crates/nu-system/src/netbsd.rs | 45 +++++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/crates/nu-system/src/freebsd.rs b/crates/nu-system/src/freebsd.rs index 6c91f66ba2..a6f0fa257f 100644 --- a/crates/nu-system/src/freebsd.rs +++ b/crates/nu-system/src/freebsd.rs @@ -213,19 +213,39 @@ fn get_proc_args(pid: i32) -> io::Result> { } } -// For getting simple values from the sysctl interface +/// For getting simple values from the sysctl interface +/// +/// # Safety +/// `T` needs to be of the structure that is expected to be returned by `sysctl` for the given +/// `ctl_name` sequence and will then be assumed to be of correct layout. +/// Thus only use it for primitive types or well defined fixed size types. For variable length +/// arrays that can be returned from `sysctl` use it directly (or write a proper wrapper handling +/// capacity management) +/// +/// # Panics +/// If the size of the returned data diverges from the size of the expected `T` unsafe fn get_ctl(ctl_name: &[i32]) -> io::Result { let mut value: MaybeUninit = MaybeUninit::uninit(); let mut value_len = mem::size_of_val(&value); - check(sysctl( - ctl_name.as_ptr(), - ctl_name.len() as u32, - value.as_mut_ptr() as *mut libc::c_void, - &mut value_len, - ptr::null(), - 0, - ))?; - Ok(value.assume_init()) + // SAFETY: lengths to the pointers is provided, uninitialized data with checked length provided + // Only assume initialized when the written data doesn't diverge in length, layout is the + // safety responsibility of the caller. + check(unsafe { + sysctl( + ctl_name.as_ptr(), + ctl_name.len() as u32, + value.as_mut_ptr() as *mut libc::c_void, + &mut value_len, + ptr::null(), + 0, + ) + })?; + assert_eq!( + value_len, + mem::size_of_val(&value), + "Data requested from from `sysctl` diverged in size from the expected return type. For variable length data you need to manually truncate the data to the valid returned size!" + ); + Ok(unsafe { value.assume_init() }) } fn get_pagesize() -> io::Result { diff --git a/crates/nu-system/src/netbsd.rs b/crates/nu-system/src/netbsd.rs index b8c29756d4..bf9fb67c04 100644 --- a/crates/nu-system/src/netbsd.rs +++ b/crates/nu-system/src/netbsd.rs @@ -93,7 +93,15 @@ fn check(err: libc::c_int) -> std::io::Result<()> { } } -/// Call `sysctl()` in read mode (i.e. the last two arguments are NULL and zero) +/// Call `sysctl()` in read mode (i.e. the last two arguments to set new values are NULL and zero) +/// +/// `name` is a flag array. +/// +/// # Safety +/// `data` needs to be writable for `data_len` or be a `ptr::null()` paired with `data_len = 0` to +/// poll for the expected length in the `data_len` out parameter. +/// +/// For more details see: https://man.netbsd.org/sysctl.3 unsafe fn sysctl_get( name: *const i32, name_len: u32, @@ -250,20 +258,37 @@ fn get_proc_args(pid: i32, what: i32) -> io::Result> { } } -// For getting simple values from the sysctl interface +/// For getting simple values from the sysctl interface +/// +/// # Safety +/// `T` needs to be of the structure that is expected to be returned by `sysctl` for the given +/// `ctl_name` sequence and will then be assumed to be of correct layout. +/// Thus only use it for primitive types or well defined fixed size types. For variable length +/// arrays that can be returned from `sysctl` use it directly (or write a proper wrapper handling +/// capacity management) +/// +/// # Panics +/// If the size of the returned data diverges from the size of the expected `T` unsafe fn get_ctl(ctl_name: &[i32]) -> io::Result { - // Safety: Call to unsafe function `netbsd::sysctl_get` - unsafe { - let mut value: MaybeUninit = MaybeUninit::uninit(); - let mut value_len = mem::size_of_val(&value); - check(sysctl_get( + let mut value: MaybeUninit = MaybeUninit::uninit(); + let mut value_len = mem::size_of_val(&value); + // SAFETY: lengths to the pointers is provided, uninitialized data with checked length provided + // Only assume initialized when the written data doesn't diverge in length, layout is the + // safety responsibility of the caller. + check(unsafe { + sysctl_get( ctl_name.as_ptr(), ctl_name.len() as u32, value.as_mut_ptr() as *mut libc::c_void, &mut value_len, - ))?; - Ok(value.assume_init()) - } + ) + })?; + assert_eq!( + value_len, + mem::size_of_val(&value), + "Data requested from from `sysctl` diverged in size from the expected return type. For variable length data you need to manually truncate the data to the valid returned size!" + ); + Ok(unsafe { value.assume_init() }) } fn get_pagesize() -> io::Result {