-
Notifications
You must be signed in to change notification settings - Fork 11
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
Make views require dedicated unsafe
marker traits.
#32
Conversation
Requiring `Into` and `From` conversions is not sufficient for `FilelikeView` and `SocketlikeView`, becuase there's no guarantee that the `Into` implementation will return the same handle as the `From` implementation. If a type allows its handle to be reassigned, that can lead to the old handle being freed twice, and the new handle being leaked. To fix this, introduce unsafe marker traits `FilelikeViewType` and `SocketlikeViewType`, and have `FilelikeView` and `SocketlikeView` require these traits.
This PR as it stands is not enough. Here's another example: use io_lifetimes::AsFilelike;
use std::fs::File;
fn main() {
let a = File::open("Cargo.toml").unwrap();
let b = File::open("Cargo.toml").unwrap();
let mut v = a.as_filelike_view::<std::fs::File>();
*v = b;
} is enough to get a double- Maybe we also need to remove the |
It's not that restrictive due to |
Oh wow, so this works: let v = f.as_filelike_view::<std::fs::File>();
(&*v).read(&mut buf) I would not have guessed that. |
And similar for `dup3`. The idea behind using `&OwnedFd` is that `dup2`'s second operand isn't like a normal borrow. It effectively closes the old file descriptor, and creates a new one with the same index. This could break assumptions of classes that have an `AsFd` to allow users to do special I/O operations, but which don't expect users can close and reopen their file descriptor as some completely unrelated resource. However, the existence of things like [`FilelikeView`], as well as the `ManuallyDrop` pattern, mean that `&OwnedFd` doesn't actually prevent users from using `dup2` on a `BorrowedFd`. With sunfishcode/io-lifetimes#32 though, `&mut OwnedFd` would be sufficient, because it removes the `DerefMut` implementation. So change `rustix` stance to be that `dup2` requires `&mut OwnedFd`. This means that it's no longer possible to pass the same file descriptor to both operands of `dup2` or `dup3` with safe Rust, which means it's not possible to observe the difference in behavior in that case, so remove the `dup3.rs` test. [`FilelikeView`]: https://docs.rs/io-lifetimes/latest/io_lifetimes/views/struct.FilelikeView.html
The removal of This potentially means that the answer to "what's the practical difference between |
The current PR here plus bytecodealliance/rustix#332 suggest a rule: there can only be at most one mutable An alternative would be to say "there can only be at most one Another alternative would be to say you can have many |
That seems like it would take us back to |
It would still be
|
This, of sorts. Because Any of those in isolation would be unsafe. It's the constraints imposed by their combination that makes it safe. E.g. an owned |
I'm having difficulty figuring out how to describe this rule. I think it's something like "types implementing |
How about this?
I think &OwnedFd is ok? That's very similar to a |
Should
Such an |
Ah, yes. I think it would still make a good example on
Other than |
I am trying to avoid getting into the business of documenting how to use
Same here. I don't have comprehensive knowledge of Windows, but I'm not aware of anything like this. As far as I can tell, Windows doesn't even have anything corresponding to And I know see use cases that need protection from And something I was missing above is that we don't need to define the abstraction boundary for a view type. Just saying " So it might be sufficient to just say this:
What do you think? |
One outcome of this approach is that |
A |
Yes, that's what I'm calling ergonomics here. You could also use In other words, these two questions:
have the same answer. Also, both |
You couldn't because you wouldn't have a place to store the |
You'd define a |
Ah, I it does collapse the concepts of |
I think it makes sense to keep |
How about |
Hm, interesting. For a general-purpose view type, we'd want a type parameter, so we can have |
Hmm, no that seems too limiting. And |
`ManuallyDrop::take` allows the view types to properly consume their temporary objects without needing an `.unwrap()`.
I just noticed that This seems like an improvement on the current PR here, but it also suggests that it might be feasible to eliminate |
I've now tried to implement this, and encountered the following problems:
Making |
And similar for `dup3`. The idea behind using `&OwnedFd` is that `dup2`'s second operand isn't like a normal borrow. It effectively closes the old file descriptor, and creates a new one with the same index. This could break assumptions of classes that have an `AsFd` to allow users to do special I/O operations, but which don't expect users can close and reopen their file descriptor as some completely unrelated resource. However, the existence of things like [`FilelikeView`], as well as the `ManuallyDrop` pattern, mean that `&OwnedFd` doesn't actually prevent users from using `dup2` on a `BorrowedFd`. With sunfishcode/io-lifetimes#32 though, `&mut OwnedFd` would be sufficient, because it removes the `DerefMut` implementation. So change `rustix` stance to be that `dup2` requires `&mut OwnedFd`. This means that it's no longer possible to pass the same file descriptor to both operands of `dup2` or `dup3` with safe Rust, which means it's not possible to observe the difference in behavior in that case, so remove the `dup3.rs` test. [`FilelikeView`]: https://docs.rs/io-lifetimes/latest/io_lifetimes/views/struct.FilelikeView.html
Thinking about this more, there wouldn't be as much of a need for |
Thanks for the discussion, @the8472! I found it very valuable to talk through this here. I'm going to merge this PR, because I think we have a clear enough view (hah) of how this form of view needs to work. But if you have any further observations, please post about them, here or in the I/O safety tracking issue. |
The main change here is that view types no longer implement `DerefMut`, which is needed by `Read` and `Write`. Fortunately, there's a way to get a `DerefMut` implementation: sunfishcode/io-lifetimes#32 (comment)
* Update to io-lifetimes 0.7.0-beta.0. The main change here is that view types no longer implement `DerefMut`, which is needed by `Read` and `Write`. Fortunately, there's a way to get a `DerefMut` implementation: sunfishcode/io-lifetimes#32 (comment)
Requiring
Into
andFrom
conversions is not sufficient forFilelikeView
andSocketlikeView
, becuase there's no guaranteethat the
Into
implementation will return the same handle as theFrom
implementation. If a type allows its handle to be reassigned, that
can lead to the old handle being freed twice, and the new handle being
leaked.
To fix this, introduce unsafe marker traits
FilelikeViewType
andSocketlikeViewType
, and haveFilelikeView
andSocketlikeView
require these traits.