diff --git a/src/platform/redox/socket.rs b/src/platform/redox/socket.rs index 62c59ac5e0..ea61d371b2 100644 --- a/src/platform/redox/socket.rs +++ b/src/platform/redox/socket.rs @@ -860,7 +860,14 @@ unsafe { *option_len_ptr = mem::size_of::() as socklen_t }; return Ok(()); } _ => { - let metadata = [SocketCall::GetSockOpt as u64, option_name as u64]; + // Wire format: [SocketCall::GetSockOpt, level, option] + // Level disambiguates option 4 (TCP_KEEPIDLE at SOL_SOCKET) + // from option 4 (IP_TOS at IPPROTO_IP) etc. + let metadata = [ + SocketCall::GetSockOpt as u64, + level as u64, + option_name as u64, + ]; let payload = // SAFETY: caller guarantees ptr is aligned for T and len is exact; no alias unsafe { slice::from_raw_parts_mut(option_value.cast::(), option_len) }; @@ -1206,18 +1213,26 @@ unsafe { &*option_value.cast::() }; Ok(()) }; - // TODO convert back to match when we support more levels + // Wire format: [SocketCall::SetSockOpt, level, option] + // The netstack dispatches on level + option together. Without + // level in the wire format, option 4 (TCP_KEEPIDLE at SOL_SOCKET) + // reads as IP_TOS (also option 4) at IPPROTO_IP. See + // NETWORKING-AND-DRIVERS-CODE-ASSESSMENT-2026-07-27.md ยง3.4 + // (Finding 1.7: option collision). if level == SOL_SOCKET { match option_name { SO_RCVTIMEO => return set_timeout(b"read_timeout"), SO_SNDTIMEO => return set_timeout(b"write_timeout"), _ => { - let metadata = [SocketCall::SetSockOpt as u64, option_name as u64]; + let metadata = [ + SocketCall::SetSockOpt as u64, + level as u64, + option_name as u64, + ]; let payload = // SAFETY: caller must verify the safety contract for this operation unsafe { slice::from_raw_parts_mut(option_value as *mut u8, option_len as usize) }; - let call_flags = CallFlags::empty(); redox_rt::sys::sys_call_rw( socket as usize, payload,