From 3ad0420b85b509bb958e6dafdf42791a353d2c35 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 8 Nov 2025 17:30:34 +0100 Subject: [PATCH 1/3] Add derive macro to check types vs libc crate. --- src/header/sys_socket/mod.rs | 10 +++-- src/header/sys_uio/mod.rs | 2 +- src/macros.rs | 83 ++++++++++++++++++++++++++++++++++++ src/platform/redox/socket.rs | 5 +-- 4 files changed, 92 insertions(+), 8 deletions(-) diff --git a/src/header/sys_socket/mod.rs b/src/header/sys_socket/mod.rs index 169098d100..24578482d9 100644 --- a/src/header/sys_socket/mod.rs +++ b/src/header/sys_socket/mod.rs @@ -14,7 +14,7 @@ pub type sa_family_t = u16; pub type socklen_t = u32; #[repr(C)] -#[derive(Default)] +#[derive(Default, CheckVsLibcCrate)] pub struct linger { pub l_onoff: c_int, pub l_linger: c_int, @@ -24,7 +24,7 @@ pub struct linger { pub extern "C" fn _cbindgen_export_linger(linger: linger) {} #[repr(C)] -#[derive(Debug)] +#[derive(Debug, CheckVsLibcCrate)] pub struct msghdr { pub msg_name: *mut c_void, pub msg_namelen: socklen_t, @@ -36,7 +36,7 @@ pub struct msghdr { } #[repr(C)] -#[derive(Debug)] +#[derive(Debug, CheckVsLibcCrate)] pub struct cmsghdr { pub cmsg_len: size_t, pub cmsg_level: c_int, @@ -45,6 +45,7 @@ pub struct cmsghdr { #[repr(C)] #[derive(Clone, Copy, Debug)] +// FIXME: CheckVsLibcCrate pub struct ucred { pub pid: pid_t, pub uid: uid_t, @@ -55,7 +56,7 @@ pub struct ucred { pub extern "C" fn _cbindgen_export_cmsghdr(cmsghdr: cmsghdr) {} #[repr(C)] -#[derive(Default)] +#[derive(Default, CheckVsLibcCrate)] pub struct sockaddr { pub sa_family: sa_family_t, pub sa_data: [c_char; 14], @@ -75,6 +76,7 @@ const _SS_PADDING: usize = _SS_MAXSIZE - mem::size_of::() - mem::si /// * The order of the fields is important because the bytes in the padding will be cast to and /// from protocol structs in C #[repr(C)] +//#[derive(CheckVsLibcCrate)] FIXME: can't ignore private fields yet pub struct sockaddr_storage { pub ss_family: sa_family_t, __ss_pad2: [u8; _SS_PADDING], diff --git a/src/header/sys_uio/mod.rs b/src/header/sys_uio/mod.rs index d2beaede4a..ae3cabc155 100644 --- a/src/header/sys_uio/mod.rs +++ b/src/header/sys_uio/mod.rs @@ -11,7 +11,7 @@ use crate::{ pub const IOV_MAX: c_int = 1024; #[repr(C)] -#[derive(Debug)] +#[derive(Debug, CheckVsLibcCrate)] pub struct iovec { pub iov_base: *mut c_void, pub iov_len: size_t, diff --git a/src/macros.rs b/src/macros.rs index 4261077f65..3c00e823b8 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -410,3 +410,86 @@ macro_rules! OutProject { unsafe impl $crate::out::OutProject for $name {} } } +#[macro_export] +#[cfg(not(feature = "check_against_libc_crate"))] +macro_rules! CheckEquals { + derive() { $(#[$($attrs:meta),*])* $v:vis struct $name:ident { + $( + $(#[$($fa:meta),*])* $fv:vis $field:ident : $type:ty + ),*$(,)? + } } => { + } +} + +// TODO: probably exists nice nightly features that allow conflicting impls. Then we wouldn't need +// much of this redundant code just to say A == B -> B == A and say A == B -> *mut A == *mut B. +pub trait LibcTypeEquals {} +//impl LibcTypeEquals for () {} +impl LibcTypeEquals<*mut A, *mut B> for () where (): LibcTypeEquals {} +impl LibcTypeEquals<*const A, *const B> for () where (): LibcTypeEquals {} +impl LibcTypeEquals<[A; N], [B; N]> for () where (): LibcTypeEquals {} +macro_rules! for_primitive_int( + ($i:ident) => { + impl LibcTypeEquals<$i, $i> for () {} + } +); +for_primitive_int!(u8); +for_primitive_int!(u16); +for_primitive_int!(u32); +for_primitive_int!(u64); +for_primitive_int!(u128); +for_primitive_int!(usize); +for_primitive_int!(i8); +for_primitive_int!(i16); +for_primitive_int!(i32); +for_primitive_int!(i64); +for_primitive_int!(i128); +for_primitive_int!(isize); +impl LibcTypeEquals for () {} +impl LibcTypeEquals<__libc_only_for_layout_checks::c_void, crate::platform::types::c_void> for () {} +impl LibcTypeEquals for () {} + +//impl LibcTypeEquals<__libc_only_for_layout_checks::c_void> + +/// Derive macro which checks that structs here are defined the same as in the libc crate. Perhaps +/// not sufficiently rigorous to soundly cast between the types, but should catch most mistakes. +#[macro_export] +#[cfg(feature = "check_against_libc_crate")] +macro_rules! CheckVsLibcCrate { + // XXX: not sure we can have the name be different from libc::$name without parameters to the + // derive macro + derive() { $(#[$($attrs:meta),*])* $v:vis struct $name:ident { + $( + $(#[$($fa:meta),*])* $fv:vis $field:ident : $type:ty + ),*$(,)? + } } => { + // TODO: check repr(C)? probably possible to match on $attrs + #[allow(dead_code)] + const _: () = { + if ::core::mem::size_of::<$name>() != ::core::mem::size_of::<::__libc_only_for_layout_checks::$name>() { + panic!("struct size mismatch"); + } + if ::core::mem::align_of::<$name>() != ::core::mem::align_of::<::__libc_only_for_layout_checks::$name>() { + panic!("struct alignment mismatch"); + } + $( + if ::core::mem::offset_of!($name, $field) != ::core::mem::offset_of!(__libc_only_for_layout_checks::$name, $field) { + panic!("struct field offset mismatch"); + } + )* + }; + $( + // check all field types are equivalent + #[allow(dead_code)] + const _: () = { + fn ensure_ty(a: A, b: B) where (): $crate::macros::LibcTypeEquals:: {} + fn for_libc(a: $name, b: __libc_only_for_layout_checks::$name) { + let a: $type = panic!("never called"); + ensure_ty(a, b.$field); + } + }; + )* + impl $crate::macros::LibcTypeEquals<$name, __libc_only_for_layout_checks::$name> for () {} + impl $crate::macros::LibcTypeEquals<__libc_only_for_layout_checks::$name, $name> for () {} + } +} diff --git a/src/platform/redox/socket.rs b/src/platform/redox/socket.rs index 19db628284..efc3f7814d 100644 --- a/src/platform/redox/socket.rs +++ b/src/platform/redox/socket.rs @@ -716,8 +716,7 @@ impl PalSocket for Sys { } _ => { let metadata = [SocketCall::GetSockOpt as u64, option_name as u64]; - let payload = - slice::from_raw_parts_mut(option_value as *mut u8, option_len); + let payload = slice::from_raw_parts_mut(option_value as *mut u8, option_len); let call_flags = CallFlags::empty(); *option_len_ptr = redox_rt::sys::sys_call( socket as usize, @@ -810,7 +809,7 @@ impl PalSocket for Sys { + mem::size_of::() // payload_len + whole_iov_size // payload_data_buffer + mem::size_of::() // control_len - + mhdr.msg_controllen as usize // ancillary_stream_buffer + + mhdr.msg_controllen as usize // ancillary_stream_buffer }; msg_stream .try_reserve_exact(expected_stream_size) From 4855cf3875b2d44e2d0bb28acdd47da084819858 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 8 Nov 2025 17:31:57 +0100 Subject: [PATCH 2/3] Fix compilation on Linux. --- src/header/sys_syslog/linux.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/header/sys_syslog/linux.rs b/src/header/sys_syslog/linux.rs index 0ffb56fa8f..019b112168 100644 --- a/src/header/sys_syslog/linux.rs +++ b/src/header/sys_syslog/linux.rs @@ -55,7 +55,7 @@ impl LogSink for LogFile { connect( log_fd, &raw const log_addr as *const sockaddr, - size_of::(), + size_of::() as u32, ) < 0 } { // In case close sets ERRNO. From 513514a804533720bebdaf0fe2018fc526ee4d50 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 8 Nov 2025 17:35:51 +0100 Subject: [PATCH 3/3] fix typo when libc check feature not enabled --- src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 3c00e823b8..c04ca3bc26 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -412,7 +412,7 @@ macro_rules! OutProject { } #[macro_export] #[cfg(not(feature = "check_against_libc_crate"))] -macro_rules! CheckEquals { +macro_rules! CheckVsLibcCrate { derive() { $(#[$($attrs:meta),*])* $v:vis struct $name:ident { $( $(#[$($fa:meta),*])* $fv:vis $field:ident : $type:ty