-
Notifications
You must be signed in to change notification settings - Fork 367
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
Advisory for rio #293
Advisory for rio #293
Conversation
Thanks for reporting this! We are currently soliciting community feedback on how to handle such cases. If you have any particular stance on them, please chime in here. We will act on this PR after a rough consensus is reached by the community - at least sufficient for agreeing on a sane default. |
Oh, wow. The timing makes it seem my PR was prompted by that Reddit post... Anyway, my position is already represented in the discussion, so I will proceed to upvote accordingly. |
There was also a lot of prior discussion in #275 and some consensus for having a dedicated category/severity/whatever for soundness issues. |
I've been somewhat torn on what to do with this one. I think it might sense to track as an |
The issue linked in advisory has been deleted (https://github.com/spacejam/rio/issues/11), but it's available in caches. No idea why it was deleted, because it was a good civil discussion.
I have a feeling that I'm misunderstanding something in all this, so please correct me if I'm wrong. After reading the issue I can't see a problem in rio itself. If user of rio does something like
Documentation of
So rio is working as intended from If we consider this an worthy advisory I feel we probably open a can of worms and we need to make every possible crate work with users calling Maybe using My 2¢ is that this is not unsoundness issue nor vulnerability. |
It is not a matter of opinion whether it is unsound. By using only rio and safe code, one can induce undefined behavior. This is, by definition, unsound. |
|
I think the topic here is rio and the use of If every possible type implementing My opinion here is based on practicality and not theory. In theory the whole Rust as a language is unsound because Crates.io has 41472 published crates. Instead of harassing all the awesome authors of published crates because of a safe Rust standard library function |
They aren't; none of those make it possible to have undefined behavior in safe code. |
The root of the problem is not The definition of unsoundness laid down in the Nomicon and has not been arrived to lightly. Rust language designers have thoroughly investigated this exact memory unsafety pattern in relation to std scoped threads before. The issue is both real and fundamental; sadly there is no feasible technical solution outside rio to make its current pattern memory-safe. With the issue being technically impossible to fix outside rio and organizationally impossible to fix in rio, we're at a stalemate for getting a technical fix. What we're trying to do here is figure out how to inform interested parties about the issue while minimizing harassment. |
I've viewed rio as a piece of art to encourage some conversations. One of the conversations I've watched unfold is the controversial licensing I've used with it, which has been useful to encourage awareness of the virality of licenses and support of open source etc... I see this issue as another thing that has caused some back-and-forth to educate people about Rust. Never dropping would not be an issue. The issue is specifically what happens when the borrow checker is disabled. Consider this code: It does not compile when the I don't think it's correct to equate std::mem::forget with a leak, as illustrated with that code. In rio's case, immortality for a completion is totally fine. It's the edges where the borrow checker seems to be disabled that I hope people will talk about more. I'd also like to better understand if there are other cases where the borrow checker seems to be disabled. |
If you use |
@bjorn3 it does not seem to compile when using drop. Did you mean something else than the drop I added at the end? https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a088dbbc067538d824a2b7caefe9c8c8 |
I meant use std::rc::Rc;
use std::cell::RefCell;
struct Completion<'a> {
dst: &'a mut [u8],
}
impl<'a> Drop for Completion<'a> {
fn drop(&mut self) {
// the kernel does some write in the background
for item in self.dst.iter_mut() {
*item = 66;
}
panic!("this doesn't run because of the leak");
}
}
struct Cycle<'a> {
completion: Completion<'a>,
cycle: RefCell<Option<Rc<Cycle<'a>>>>,
}
fn main() {
let mut buf = [0; 1024];
let completion = Completion {
dst: &mut buf,
};
let cycle = Rc::new(Cycle { completion, cycle: RefCell::new(None)});
cycle.cycle.replace(Some(cycle.clone()));
std::mem::drop(cycle); // <---- drop cycle instead of forget
buf[4] = 42;
} Otherwise |
That's not true, and I was involved in formally proving this. It seems you are not entirely familiar with the involved terminology of "soundness" and/or "Undefined Behavior" here; a good starting point for this is the UCG glossary. Modulo the known and unknown soundness bugs, Rust is a perfectly sound language. This includes Also note that The "unsound" advisory category is meant for exactly those cases -- has it already been officially introduced by the WG? Once that category exists, rio ticks all the boxes to justify a corresponding advisory. Whether or not the developer treats a soundness violation as a bug is their choice, but has no impact on whether or not it is a soundness violation. |
Here's a way to re-implement use std::cell::Cell;
use std::rc::Rc;
fn my_forget<T>(x: T) {
#[allow(dead_code)]
struct Cycle<T> {
x: T,
cycle: Cell<Option<Rc<Cycle<T>>>>,
}
let cycle = Rc::new(Cycle { x, cycle: Cell::new(None)});
cycle.cycle.replace(Some(cycle.clone()));
}
fn main() {
// Just test that this works as expected.
struct Bomb;
impl Drop for Bomb {
fn drop(&mut self) {
println!("BOOM!");
}
}
let b = Bomb;
// Note how the bomb never goes off.
my_forget(b);
} This conclusively demonstrates that I also don't know what you mean by "disabling the borrow checker", nothing of the sort is happening in any of these examples. |
Thanks. @RalfJung ahh yeah I've hit the same thing before, still waking up. Was this one of the things that changed with NLL? Looking back at history to the flurry of activity around leakopalypse, just a few days remaining before 1.0, the proposal to make mem::forget safe, and frantic attempts to avoid unsafe drop, despite it being a decision that was made intentionally, I can't help but wonder what approaches simply didn't have time to be considered given that 1.0 was about to drop in a few hours. In that last issue, it was clear that the people making decisions didn't have enough time to pay attention to the people outside of the room due to the impending release. |
@spacejam Secondly, I think you are misrepresenting the discussion back then. As It is correct that no effort was made to spend 3 months on designing such an API. This is because the people involved were sure that the resulting API is bespoke and too limiting. That is still the consensus today, so I think those people were right. To prove them wrong, someone should present an
No, this works all the way back to Rust 1.0, as you can check on godbolt. |
I should also add that I appreciate this crate. :) It's always nice to see more of the design space explored, including the design space that could have been had Rust made different decisions around memory leaks. io_uring is an important API that Rust should have best-in-class safe+sound support for, and your crate certainly pushed that discussion forward. Our disagreement is on the treatment of the unsoundness, not on the publication of the crate in the first place. |
Well, as rio is art that I hope will encourage conversations, I hope that whatever decision you come to results in the loudest ruckus possible around it :) The unsoundness is currently a feature to that end. When that goal loses its lustre, the methods will just flip to unsafe fn's. Long-term, rio 2 is intended to be sound and may accept a IO topology structure that can be intelligently scheduled in prioritized ways, taking ideas from scoped threads and maybe tricks from typed arena. The benefits of io_uring really come about through chained concurrent operations in the kernel that can execute without userspace code stomping into the middle of the data plane, and that's the next space for exploration. |
Following the discussion above and a demonstration of memory unsafety in presence of an Rc cycle I'm inclined to merge the advisory as-is. Given that Rc/Arc cycles are not uncommon in async code the required conditions are not contrived, and the resulting memory unsafety is significant. That will make dependency on If there is any opposition to that, I'd like to hear it. I'm particularly interested in hearing from @spacejam |
The alternative would be to start with an This is also related to #313. |
No objections have been voiced in 10 days, so I'm going to go ahead and merge this. Thanks to everyone in this thread for the constructive, insightful and respectful discussion! |
No description provided.