-
Notifications
You must be signed in to change notification settings - Fork 665
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
recvfrom
, recvmsg
and recvmmsg
might return uninitialized addresses when using connection-mode sockets
#2177
Comments
recvfrom
, recvmsg
and recvmmsg
might return unitialized addresses when using connection-mode socketsrecvfrom
, recvmsg
and recvmmsg
might return uninitialized addresses when using connection-mode sockets
This issue is related to #2054 |
Unless this can violate some other invariant of SockaddrIn, it isn't unsound. I assume you're referring to the unsoundness that results from reading uninitialized memory? In this case, though the kernel may not have written to the buffer, the compiler cannot possibly know that. So it must treat the memory as initialized. But we should probably add a check for |
I'm by no means an expert on this topic, but I think this meets the exact definition of "Undefined Behaviour" as the outcome of the family check is, well, undefined. Take a look what socket2 is doing, they zero out the entire storage before using it for this exact reason: https://github.com/rust-lang/socket2/blob/master/src/sockaddr.rs#L129 My suggestion is to introduce a new trait: pub unsafe trait SockaddrFromRaw {
// the libc type
type Storage;
unsafe fn from_raw(
addr: *const Self::Storage,
len: libc::socklen_t,
) -> Self
where
Self: Sized;
fn init_storage(buf: &mut MaybeUninit<Self::Storage>);
}
unsafe impl SockaddrFromRaw for Option<SockaddrIn> {
type Storage = libc::sockaddr_in;
unsafe fn from_raw(
addr: *const Self::Storage,
_: libc::socklen_t,
) -> Self {
// SAFETY: `sa_family` has been initialized by `Self::init_storage` or by the syscall.
unsafe {
if addr_of!((*addr).sin_family).read() as libc::c_int != libc::AF_INET {
return None;
}
}
// Now we can assume that `addr` has been fully initialized.
Some(SockaddrIn(ptr::read(addr)))
}
fn init_storage(buf: &mut MaybeUninit<Self::Storage>) {
// The family of `Self` is `AF_INET`, so setting the family to `AF_UNSPEC` is sufficient.
let ptr = buf.as_mut_ptr() as *mut libc::sockaddr;
unsafe { addr_of_mut!((*ptr).sa_family).write(libc::AF_UNSPEC as _) }
}
} |
We shouldn't need to zero it first. As far as the compiler is concerned, that FFI call always initializes the return argument. We should just check the sin_family field, the sin_len field (on OSes that have one), and the msg_namelen field of the msghdr. |
I'm still not sure if I agree with you, especially if we consider more complex types like
For example, |
Whether we want to check the lengths is an interesting question, too. I am honestly currently in favor of not checking it at all and blindly trusting the syscalls, because
Instead, I would add a |
Sure, different implementation of
It's not part of a standard, but it's guaranteed to be present on all of the BSDs. There's no possibility that it will simply vanish at runtime. |
Disregard my last comment about the lengths, these syscalls can return a length larger than our allocated storage, so a boundary check for the length is mandatory here. Sorry for the confusion. I was already thinking about the cases where the address storage is managed by the syscall, like |
When using connection-mode sockets like TCP, the addresses returned by
recvfrom
,recvmsg
andrecvmmsg
are not initialized by the syscall.This is how the receive operations create an IPV4-Socketaddr, with a comment which operation is unsound here:
The following test will catch this issue (but it is already based on the new API I'm working on):
The text was updated successfully, but these errors were encountered: