-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Tweak SocketAncillary API #86432
Tweak SocketAncillary API #86432
Conversation
/// | ||
/// Use [`data()`] if you are only interested in the used portion of the buffer. | ||
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] | ||
pub fn buffer(&self) -> &[MaybeUninit<u8>] { |
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.
Note that we don't really need buffer()
for integrating with manual syscalls. data()
, buffer_mut()
and set_len()
are enough. However, it seemed weird to have buffer_mut()
and not buffer()
.
We could also rename buffer_mut()
to just buffer()
. I don't have any strong feelings either way.
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.
We could also rename buffer_mut() to just buffer().
Sounds good to me.
impl crate::error::Error for AddAncillaryError {} | ||
|
||
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] | ||
impl From<AddAncillaryError> for io::Error { |
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 added this conversion because it is likely that add_fds()
and add_creds()
are going to be used from functions that already return an io::Result
. So this allows you to just use the ?
operator.
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.
- io::Error::new does a lot of allocations. This should probably use io::Error::new_const.
- This should now be
ErrorKind::Uncategorized
instead ofOther
.
fb99d92
to
327cb39
Compare
/// - The length may not exceed [`capacity()`](Self::capacity). | ||
/// - The data in the buffer at `0..length` must be valid ancillary data. | ||
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] | ||
pub unsafe fn set_len(&mut self, length: usize, truncated: bool) { |
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 could not think of situations where you want to change length
and the truncated
flag independently of each-other, so I figured it makes sense to ask for both at the same time. That way you can't accidentally forget to update the truncated flag.
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] | ||
pub unsafe fn buffer_mut(&mut self) -> &mut [MaybeUninit<u8>] { | ||
self.buffer | ||
} |
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.
Is the use-case appending additional messages to the buffer? In that case it might be more ergonomic to only return the tail beyond the current length.
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.
The use-case I have in mind is to pass the entire buffer to a syscall to let the kernel write into it. I wasn't really thinking of manually extending it with hand-crafted messages.
If we would want to support that, I would lean towards making a public version of add_to_ancillary_data
(also unsafe of course).
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 see, I thought this was about handling message types not supported by std rather than handling ancilliary data in conjunction with other syscalls. Are there syscalls other than recvmsg
that would write ancillary data? io_uring I guess?
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.
Possibly, although I'm not familiar with io_uring
myself. It also helps when implementing unsupported socket types on crates.io. I'd love to use this in tokio-seqpacket
for example.
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'd love to use this in tokio-seqpacket for example.
At least for that particular usecase you probably wouldn't need it. You could temporarily create a ManuallyDrop<UnixDatagram>
socket and then call send_with_ancillary
since that boils down to the same syscall. At least once a way to pass flags is provided. That way a lot of code duplication can be avoided.
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 don't think that would be a good solution. That would mean using knowledge about implementation details of std
. The UnixDatagram API does not guarantee any mapping to specific syscalls or that from_raw_fd
is pretty much a simple cast.
caab6f4
to
bf85a1a
Compare
The `SocketAncillary` object has no lifetime that could guarantee that the file descriptor remains open long enough. That means it could end up duplicating arbitrary file descriptors and send them to a remote process. Additionally, even if the fd lifetime was tracked, duplicating an FD and sending it to a remote process could violate the safety requirements of the object managing the file descriptor. This matters, because `as_raw_fd()` is not unsafe.
Pushed a commit to make |
/// An error returned when trying to add anciallary data that exceeds the buffer capacity. | ||
#[cfg(any(doc, target_os = "android", target_os = "linux",))] | ||
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] | ||
pub struct AddAncillaryError { |
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.
Should we really create an Error for on special failure? Maybe, create an enum AncillaryError
for all errors, which can occur with Ancillary, or use io:Error:other
.
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.
We could add an inner enum to change the error message, but I would be hestitent to expose the enum directly to the user. That would limit what we can do with it in the future, because we would have to stay backwards compatible.
I wouldn't directly return an io::Error
, since it's a bit overkill to have a Box<dyn Error>
. I kinda like a opaque cheap error type that can be converted into io::Error
.
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.
Yes, io::Error
would be okay, or we can use #[non_exhaustive]
for the enum.
@de-vri-es There is a PR #82858, to extend the SocketAncillary. If you have time, you could review it :). |
I've looked it over and left some comments :) |
Triage: |
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.
Mark add_fds() unsafe.
Can the new BorrowedFd or OwnedFd types be of use here?
impl crate::error::Error for AddAncillaryError {} | ||
|
||
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] | ||
impl From<AddAncillaryError> for io::Error { |
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.
- io::Error::new does a lot of allocations. This should probably use io::Error::new_const.
- This should now be
ErrorKind::Uncategorized
instead ofOther
.
/// | ||
/// Use [`data()`] if you are only interested in the used portion of the buffer. | ||
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")] | ||
pub fn buffer(&self) -> &[MaybeUninit<u8>] { |
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.
We could also rename buffer_mut() to just buffer().
Sounds good to me.
ping from triage:
When it's ready for review send a message containing @rustbot author |
@de-vri-es @rustbot label: +S-inactive |
This PR tweaks the API of the unix-specific
SocketAncillary
struct.It does four things:
data()
,buffer()
,buffer_mut()
andset_len()
to allow integration with manual syscalls, for example when implementing unsupported socket types outside ofstd
.&mut [MaybeUnit<u8>]
internally and addnew_uninit()
.add_fds()
andadd_creds()
return aResult
to indicate failure.add_fds()
unsafe.The reason for making
add_fds()
unsafe is that no lifetimes are tracked, so the FD could be closed and re-assigned before the ancillary data is actually used. This would result in sending the wrong file descriptor which may end up messing with an otherwise safe memory mapping.