-
Notifications
You must be signed in to change notification settings - Fork 674
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
Set the length of a socket address when calling recvmsg
on Linux
#2041
Set the length of a socket address when calling recvmsg
on Linux
#2041
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unfortunate that this change results in an extra data copy, in S::from_raw. Instead, what about adding a SockaddrLike::set_len method? recvmsg would call it after libc::recv_msg. For most socket types, it would be a noop, but for UnixAddr on Linux it would set the sun_len field.
test/sys/test_socket.rs
Outdated
print!("Couldn't send ({e:?}), so skipping test"); | ||
return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
print!("Couldn't send ({e:?}), so skipping test"); | |
return; | |
skip!("Couldn't send ({e:?}), so skipping test"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in 72a2f56
src/sys/socket/mod.rs
Outdated
@@ -2048,20 +2047,23 @@ pub fn recvmsg<'a, 'outer, 'inner, S>(fd: RawFd, iov: &'outer mut [IoSliceMut<'i | |||
where S: SockaddrLike + 'a, | |||
'inner: 'outer | |||
{ | |||
let mut address = mem::MaybeUninit::uninit(); | |||
let mut address: libc::sockaddr_storage = unsafe { mem::MaybeUninit::zeroed().assume_init() }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use sockaddr_storage instead of S?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It didn't pass tests when I used S
, I think because ()
is shorter than the actual socket address type so it ran out of room (though I didn't check for the cause, so I might be wrong about that). Though the point is moot as I put it back to how it was before for implementing your suggestion about the different approach.
I implemented that change, though I wasn't sure if you wanted it to be publicly-visible or not. I can hide it from the docs and mark it an internal implementation detail, if you prefer. Also, I made it return an error if you try to set the length on any socket address type whose length is static (anything other that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this version a lot better. Just a few inline comments, and:
- Please do add
#[doc(hidden)]
toset_length
, as you suggested, and - Please add a CHANGELOG entry.
src/sys/socket/addr.rs
Outdated
|
||
/// Set the length of this socket address | ||
/// | ||
/// This method may only be called on socket addresses whose lenghts are dynamic, and it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// This method may only be called on socket addresses whose lenghts are dynamic, and it | |
/// This method may only be called on socket addresses whose lengths are dynamic, and it |
src/sys/socket/addr.rs
Outdated
cfg_if! { | ||
if #[cfg(any(target_os = "android", | ||
target_os = "fuchsia", | ||
target_os = "illumos", | ||
target_os = "linux", | ||
target_os = "redox", | ||
))] { | ||
addr.sun_len = new_length as u8; | ||
} else { | ||
addr.sun.sun_len = new_length as u8; | ||
} | ||
} | ||
Ok(()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cfg_if! { | |
if #[cfg(any(target_os = "android", | |
target_os = "fuchsia", | |
target_os = "illumos", | |
target_os = "linux", | |
target_os = "redox", | |
))] { | |
addr.sun_len = new_length as u8; | |
} else { | |
addr.sun.sun_len = new_length as u8; | |
} | |
} | |
Ok(()) | |
addr.set_length(new_length) |
src/sys/socket/addr.rs
Outdated
/// | ||
/// `new_length` must be a valid length for this type of address. Specifically, reads of that | ||
/// length from `self` must be valid. | ||
unsafe fn set_length(&mut self, new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you add a default implementation here, then you can avoid a lot of boilerplate elsewhere.
unsafe fn set_length(&mut self, new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic>; | |
unsafe fn set_length(&mut self, new_length: usize) -> std::result::Result<(), SocketAddressLengthNotDynamic> { | |
Err(SocketAddressLengthNotDynamic) | |
} |
src/sys/socket/mod.rs
Outdated
@@ -2048,7 +2053,7 @@ pub fn recvmsg<'a, 'outer, 'inner, S>(fd: RawFd, iov: &'outer mut [IoSliceMut<'i | |||
where S: SockaddrLike + 'a, | |||
'inner: 'outer | |||
{ | |||
let mut address = mem::MaybeUninit::uninit(); | |||
let mut address = mem::MaybeUninit::zeroed(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you think it's necessary to zero the address here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally thought it might be necessary when I first started working on this (since the length was being left uninitialized initially), but looking back at it now I think everything is initialized now so I put it back to uninit()
I've done those changes, please let me know if there's anything else you want to see. |
Co-authored-by: Alan Somers <asomers@gmail.com>
The linter isn't happy with that change. |
Fixed! I had to make an allowance because the variable was only used on some platforms and was left unused on others. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bors r+
Build succeeded! The publicly hosted instance of bors-ng is deprecated and will go away soon. If you want to self-host your own instance, instructions are here. If you want to switch to GitHub's built-in merge queue, visit their help page.
|
2041: Set the length of a socket address when calling `recvmsg` on Linux r=asomers a=JarredAllen # Background I think I've found a bug with `recvmsg` on Linux where it doesn't set the length of a received socket address. I found this while working with unix datagram sockets with path `struct sockaddr_un` addresses, but I think this applies to any variable-length socket address type. At present, the `sun_len` field of `UnixAddr` on Linux shows up as 0 whenever I call `recvmsg`. I think it's reading uninitialized memory (afaict the `recvmsg` function never initializes it before we call `MaybeUninit::assume_init`), though it's possible that it's being zero-initialized somewhere that I missed. Either way, this isn't the correct behavior, which should set it to the length of the `struct sockaddr_un` contents (or whatever other type of socket). # Changes I changed the `recvmsg` function to construct the returned socket address from the `struct sockaddr` and length returned by the libc `recvmsg` call (using the `from_raw` function the trait provides). Since the trait is sealed so downstream crates can't implement it, I believe this shouldn't be a breaking change. # Validation I've tested that my code (which failed due to this bug) now works. I also added a new test case which tests that we construct `UnixAddr`s correctly in the `recvmsg` function, which passes locally for me and fails if I cherry-pick it onto the current `master`. I've also checked that `cargo test` and `cargo clippy` both still pass on `aarch64-apple-darwin` and on `aarch64-unknown-linux-musl` targets (the two targets I develop for/have access to). Hopefully your CI will confirm that everything else still works. Co-authored-by: Jarred Allen <jarred@moveparallel.com> Co-authored-by: Jarred Allen <jarredallen73@gmail.com>
2041: Set the length of a socket address when calling `recvmsg` on Linux r=asomers a=JarredAllen # Background I think I've found a bug with `recvmsg` on Linux where it doesn't set the length of a received socket address. I found this while working with unix datagram sockets with path `struct sockaddr_un` addresses, but I think this applies to any variable-length socket address type. At present, the `sun_len` field of `UnixAddr` on Linux shows up as 0 whenever I call `recvmsg`. I think it's reading uninitialized memory (afaict the `recvmsg` function never initializes it before we call `MaybeUninit::assume_init`), though it's possible that it's being zero-initialized somewhere that I missed. Either way, this isn't the correct behavior, which should set it to the length of the `struct sockaddr_un` contents (or whatever other type of socket). # Changes I changed the `recvmsg` function to construct the returned socket address from the `struct sockaddr` and length returned by the libc `recvmsg` call (using the `from_raw` function the trait provides). Since the trait is sealed so downstream crates can't implement it, I believe this shouldn't be a breaking change. # Validation I've tested that my code (which failed due to this bug) now works. I also added a new test case which tests that we construct `UnixAddr`s correctly in the `recvmsg` function, which passes locally for me and fails if I cherry-pick it onto the current `master`. I've also checked that `cargo test` and `cargo clippy` both still pass on `aarch64-apple-darwin` and on `aarch64-unknown-linux-musl` targets (the two targets I develop for/have access to). Hopefully your CI will confirm that everything else still works. Co-authored-by: Jarred Allen <jarred@moveparallel.com> Co-authored-by: Jarred Allen <jarredallen73@gmail.com>
2041: Set the length of a socket address when calling `recvmsg` on Linux r=asomers a=JarredAllen # Background I think I've found a bug with `recvmsg` on Linux where it doesn't set the length of a received socket address. I found this while working with unix datagram sockets with path `struct sockaddr_un` addresses, but I think this applies to any variable-length socket address type. At present, the `sun_len` field of `UnixAddr` on Linux shows up as 0 whenever I call `recvmsg`. I think it's reading uninitialized memory (afaict the `recvmsg` function never initializes it before we call `MaybeUninit::assume_init`), though it's possible that it's being zero-initialized somewhere that I missed. Either way, this isn't the correct behavior, which should set it to the length of the `struct sockaddr_un` contents (or whatever other type of socket). # Changes I changed the `recvmsg` function to construct the returned socket address from the `struct sockaddr` and length returned by the libc `recvmsg` call (using the `from_raw` function the trait provides). Since the trait is sealed so downstream crates can't implement it, I believe this shouldn't be a breaking change. # Validation I've tested that my code (which failed due to this bug) now works. I also added a new test case which tests that we construct `UnixAddr`s correctly in the `recvmsg` function, which passes locally for me and fails if I cherry-pick it onto the current `master`. I've also checked that `cargo test` and `cargo clippy` both still pass on `aarch64-apple-darwin` and on `aarch64-unknown-linux-musl` targets (the two targets I develop for/have access to). Hopefully your CI will confirm that everything else still works. Co-authored-by: Jarred Allen <jarred@moveparallel.com> Co-authored-by: Jarred Allen <jarredallen73@gmail.com>
Background
I think I've found a bug with
recvmsg
on Linux where it doesn't set the length of a received socket address. I found this while working with unix datagram sockets with pathstruct sockaddr_un
addresses, but I think this applies to any variable-length socket address type.At present, the
sun_len
field ofUnixAddr
on Linux shows up as 0 whenever I callrecvmsg
. I think it's reading uninitialized memory (afaict therecvmsg
function never initializes it before we callMaybeUninit::assume_init
), though it's possible that it's being zero-initialized somewhere that I missed. Either way, this isn't the correct behavior, which should set it to the length of thestruct sockaddr_un
contents (or whatever other type of socket).Changes
I changed the
recvmsg
function to construct the returned socket address from thestruct sockaddr
and length returned by the libcrecvmsg
call (using thefrom_raw
function the trait provides). Since the trait is sealed so downstream crates can't implement it, I believe this shouldn't be a breaking change.Validation
I've tested that my code (which failed due to this bug) now works. I also added a new test case which tests that we construct
UnixAddr
s correctly in therecvmsg
function, which passes locally for me and fails if I cherry-pick it onto the currentmaster
.I've also checked that
cargo test
andcargo clippy
both still pass onaarch64-apple-darwin
and onaarch64-unknown-linux-musl
targets (the two targets I develop for/have access to). Hopefully your CI will confirm that everything else still works.