Skip to content
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

Closed
wants to merge 5 commits into from

Conversation

de-vri-es
Copy link
Contributor

@de-vri-es de-vri-es commented Jun 18, 2021

This PR tweaks the API of the unix-specific SocketAncillary struct.

It does four things:

  • Add data(), buffer(), buffer_mut() and set_len() to allow integration with manual syscalls, for example when implementing unsupported socket types outside of std.
  • Switch to &mut [MaybeUnit<u8>] internally and add new_uninit().
  • Have add_fds() and add_creds() return a Result to indicate failure.
  • Mark 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.

@rustbot rustbot added A-io Area: `std::io`, `std::fs`, `std::net` and `std::path` C-enhancement Category: An issue proposing an enhancement or a PR with one. O-unix Operating system: Unix-like T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. labels Jun 18, 2021
@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jun 18, 2021
///
/// 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>] {
Copy link
Contributor Author

@de-vri-es de-vri-es Jun 18, 2021

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.

Copy link
Member

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 {
Copy link
Contributor Author

@de-vri-es de-vri-es Jun 18, 2021

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.

Copy link
Member

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 of Other.

@de-vri-es de-vri-es force-pushed the socket-ancillary-tweaks branch from fb99d92 to 327cb39 Compare June 18, 2021 10:27
/// - 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) {
Copy link
Contributor Author

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.

@de-vri-es de-vri-es changed the title Tweaks SocketAncillary API Tweak SocketAncillary API Jun 18, 2021
#[unstable(feature = "unix_socket_ancillary_data", issue = "76915")]
pub unsafe fn buffer_mut(&mut self) -> &mut [MaybeUninit<u8>] {
self.buffer
}
Copy link
Member

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.

Copy link
Contributor Author

@de-vri-es de-vri-es Jun 18, 2021

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).

Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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.

Copy link
Contributor Author

@de-vri-es de-vri-es Jun 18, 2021

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.

@de-vri-es de-vri-es force-pushed the socket-ancillary-tweaks branch from caab6f4 to bf85a1a Compare June 18, 2021 11:39
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.
@de-vri-es
Copy link
Contributor Author

Pushed a commit to make add_fds() unsafe. I also updated the PR description with the reasoning why.

/// 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 {
Copy link
Contributor

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.

Copy link
Contributor Author

@de-vri-es de-vri-es Jun 23, 2021

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.

Copy link
Contributor

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.

@LinkTed
Copy link
Contributor

LinkTed commented Jun 19, 2021

@de-vri-es There is a PR #82858, to extend the SocketAncillary. If you have time, you could review it :).

@de-vri-es
Copy link
Contributor Author

@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 :)

@crlf0710 crlf0710 added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 9, 2021
@JohnCSimon JohnCSimon added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 25, 2021
@JohnCSimon JohnCSimon added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Aug 15, 2021
@JohnCSimon JohnCSimon removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 6, 2021
@JohnCSimon JohnCSimon added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 6, 2021
@JohnCSimon JohnCSimon added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 27, 2021
@JohnCSimon JohnCSimon added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 19, 2021
@JohnCSimon
Copy link
Member

JohnCSimon commented Nov 8, 2021

Triage:
@m-ou-se This has been in review state since July.

Copy link
Member

@m-ou-se m-ou-se left a 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 {
Copy link
Member

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 of Other.

///
/// 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>] {
Copy link
Member

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.

@JohnCSimon
Copy link
Member

ping from triage:
@de-vri-es
Returning to author to address comments.

Can the new BorrowedFd or OwnedFd types be of use here?

When it's ready for review send a message containing
@rustbot ready to switch to S-waiting-on-review

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 13, 2022
@JohnCSimon
Copy link
Member

@de-vri-es
Ping from triage: I'm closing this due to inactivity, Please reopen when you are ready to continue with this. Thanks for your contribution.

@rustbot label: +S-inactive

@JohnCSimon JohnCSimon closed this Mar 20, 2022
@rustbot rustbot added the S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs. label Mar 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-io Area: `std::io`, `std::fs`, `std::net` and `std::path` C-enhancement Category: An issue proposing an enhancement or a PR with one. O-unix Operating system: Unix-like S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants