nix
nix copied to clipboard
`assume_init` on partially initizalized socket addresses
There are a few places in the code that call assume_init() on a MaybeUninit that may only be partially initialized, which is UB.
https://github.com/nix-rust/nix/blob/89b4976fddf713ca54834612e351ae35d86c430a/src/sys/socket/mod.rs#L2217-L2242
https://github.com/nix-rust/nix/blob/89b4976fddf713ca54834612e351ae35d86c430a/src/sys/socket/mod.rs#L2340-L2369
https://github.com/nix-rust/nix/blob/89b4976fddf713ca54834612e351ae35d86c430a/src/sys/socket/mod.rs#L2045-L2065
The calling code might call these functions with a S: SockaddrLike larger than the number of bytes the kernel will write during the syscall. For example if a SockaddrStorage is provided to a getsockname() call for an inet socket, the syscall will write a sockaddr_in which is much smaller than the SockaddrStorage, leaving some of the SockaddrStorage bytes uninitialized. Those uninitialized bytes will then be "assumed init" by the assume_init() call, which is UB.
The simplest solution would be to use mem::MaybeUninit::<T>::zeroed() instead since I think all socket address types are POD, so zero values should be allowed. You could also instead use as_ptr() directly on the MaybeUninit without using assume_init() first, but this will give you a *const S instead of a *const sockaddr, but there's no guarantee that a *const S can be cast to a *const sockaddr.
I thought that uninitialized memory would only trigger UB when the compiler could prove that the memory is uninitialized. But in this case, since the structure is initialized via the C library, that can't happen. Or to put it another way, why isn't it UB to use assume_init after libc::getsockname, even if the OS did initialize the entire structure?