shadowsocks/shadowsocks-rust · error

SockAddr.as_socket

Error message

SockAddr.as_socket

What it means

In the Linux fast-path UDP recvmsg wrapper, the socket address filled by the kernel is converted with SockAddr::as_socket() and unwrapped with expect(). as_socket returns None if the stored address family is not AF_INET/AF_INET6. The library assumes recvmsg on a UDP socket always yields a valid internet socket address, so a foreign family (e.g. AF_UNSPEC/AF_UNIX) is an invariant violation and panics.

Source

Thrown at crates/shadowsocks/src/net/sys/unix/linux/mod.rs:457

fn recvmsg_fallback<S: AsRawFd>(sock: &S, msg: &mut BatchRecvMessage<'_>) -> io::Result<()> {
    let mut hdr: libc::msghdr = unsafe { mem::zeroed() };

    let addr_storage = SockAddrStorage::zeroed();
    let addr_len = addr_storage.size_of() as libc::socklen_t;

    let sock_addr = unsafe { SockAddr::new(addr_storage, addr_len) };
    hdr.msg_name = sock_addr.as_ptr() as *mut _;
    hdr.msg_namelen = sock_addr.len() as _;

    hdr.msg_iov = msg.data.as_ptr() as *mut _;
    hdr.msg_iovlen = msg.data.len() as _;

    let ret = unsafe { libc::recvmsg(sock.as_raw_fd(), &mut hdr as *mut _, 0) };
    if ret < 0 {
        return Err(io::Error::last_os_error());
    }

    msg.addr = sock_addr.as_socket().expect("SockAddr.as_socket");
    msg.data_len = ret as usize;

    Ok(())
}

pub fn batch_recvmsg<S: AsRawFd>(sock: &S, msgs: &mut [BatchRecvMessage<'_>]) -> io::Result<usize> {
    if msgs.is_empty() {
        return Ok(0);
    }

    if !SUPPORT_BATCH_SEND_RECV_MSG.load(Ordering::Relaxed) {
        recvmsg_fallback(sock, &mut msgs[0])?;
        return Ok(1);
    }

    let mut vec_msg_name = Vec::with_capacity(msgs.len());
    let mut vec_msg_hdr = Vec::with_capacity(msgs.len());

View on GitHub (pinned to 8eb0f0a65b)

Solutions

  1. Replace expect() with graceful handling: if as_socket() returns None, return an io::Error (e.g. ErrorKind::InvalidData) instead of panicking
  2. Ensure only AF_INET/AF_INET6 UDP sockets are passed to this receive path
  3. Check for invalid/zero-length msg_name before conversion and skip/normalize the address

Example fix

// before
msg.addr = sock_addr.as_socket().expect("SockAddr.as_socket");
// after
msg.addr = sock_addr.as_socket().ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "recvmsg returned non-internet address"))?;
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure socket family before use
assert!(matches!(socket.local_addr()?.family(), AF_INET | AF_INET6));

Type guard

fn is_inet_sockaddr(sa: &SockAddr) -> bool { matches!(sa.family(), AF_INET | AF_INET6) }

Try / catch

match sock_addr.as_socket() {
    Some(a) => msg.addr = a,
    None => return Err(io::Error::new(io::ErrorKind::InvalidData, "non-internet sender address")),
}

Prevention

When it happens

Trigger: Calling batch_recvmsg/recvmsg_fallback on a UDP socket when the kernel returns a sender address whose family is not AF_INET or AF_INET6 — e.g. recvmsg returning a truncated/zeroed sockaddr, or the socket somehow being bound to a non-internet family.

Common situations: Rare kernel/edge cases on Linux where msg_name is not populated as AF_INET/6; misuse of the internal net::sys API with a non-UDP socket; unusual socket setups (e.g. raw or unix sockets passed into the batch-recv path).

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09). Data as JSON: /api/errors/0545dff8c0dd9726. Report an issue: GitHub.