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`
This commit is contained in:
Stefan Holderbach
2025-07-28 08:42:23 +02:00
committed by GitHub
parent 28a796d5cb
commit 00ac34d716
2 changed files with 65 additions and 20 deletions

View File

@ -213,19 +213,39 @@ fn get_proc_args(pid: i32) -> io::Result<Vec<u8>> {
} }
} }
// 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<T>(ctl_name: &[i32]) -> io::Result<T> { unsafe fn get_ctl<T>(ctl_name: &[i32]) -> io::Result<T> {
let mut value: MaybeUninit<T> = MaybeUninit::uninit(); let mut value: MaybeUninit<T> = MaybeUninit::uninit();
let mut value_len = mem::size_of_val(&value); let mut value_len = mem::size_of_val(&value);
check(sysctl( // SAFETY: lengths to the pointers is provided, uninitialized data with checked length provided
ctl_name.as_ptr(), // Only assume initialized when the written data doesn't diverge in length, layout is the
ctl_name.len() as u32, // safety responsibility of the caller.
value.as_mut_ptr() as *mut libc::c_void, check(unsafe {
&mut value_len, sysctl(
ptr::null(), ctl_name.as_ptr(),
0, ctl_name.len() as u32,
))?; value.as_mut_ptr() as *mut libc::c_void,
Ok(value.assume_init()) &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<libc::c_int> { fn get_pagesize() -> io::Result<libc::c_int> {

View File

@ -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( unsafe fn sysctl_get(
name: *const i32, name: *const i32,
name_len: u32, name_len: u32,
@ -250,20 +258,37 @@ fn get_proc_args(pid: i32, what: i32) -> io::Result<Vec<u8>> {
} }
} }
// 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<T>(ctl_name: &[i32]) -> io::Result<T> { unsafe fn get_ctl<T>(ctl_name: &[i32]) -> io::Result<T> {
// Safety: Call to unsafe function `netbsd::sysctl_get` let mut value: MaybeUninit<T> = MaybeUninit::uninit();
unsafe { let mut value_len = mem::size_of_val(&value);
let mut value: MaybeUninit<T> = MaybeUninit::uninit(); // SAFETY: lengths to the pointers is provided, uninitialized data with checked length provided
let mut value_len = mem::size_of_val(&value); // Only assume initialized when the written data doesn't diverge in length, layout is the
check(sysctl_get( // safety responsibility of the caller.
check(unsafe {
sysctl_get(
ctl_name.as_ptr(), ctl_name.as_ptr(),
ctl_name.len() as u32, ctl_name.len() as u32,
value.as_mut_ptr() as *mut libc::c_void, value.as_mut_ptr() as *mut libc::c_void,
&mut value_len, &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<libc::c_int> { fn get_pagesize() -> io::Result<libc::c_int> {