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
- Replace expect() with graceful handling: if as_socket() returns None, return an io::Error (e.g. ErrorKind::InvalidData) instead of panicking
- Ensure only AF_INET/AF_INET6 UDP sockets are passed to this receive path
- 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
- Only pass AF_INET/AF_INET6 UDP sockets to the batch recv path
- Patch the library to convert None into an io::Error instead of panicking
- Watch for kernel quirks on the Linux versions you deploy
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
- AES-128 init
- Packet header length mismatch
- AES-256 init
- udp-redir
- not supported udp transparent proxy type
AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09).
Data as JSON: /api/errors/0545dff8c0dd9726.
Report an issue: GitHub.