ouroboros: Fundamental Soundness Problem
UPDATE: A fix has been implemented. 0.16.0+ is no longer unsound in the described way, but support for template parameters had to be cut in the process.
TL;DR: Ouroboros has a soundness problem. A fix has been attempted but it was also unsound. Currently the compiler does not actually produce unsound machine code, but it may begin doing so at any point in the future. Migrate code to use self_cell instead.
More details:
Currently, Ouroboros works internally by creating a struct where all uses of 'this
are replaced by 'static
. However, a recent addition to Miri checks that references passed to functions are valid during the entire execution of the function, even when those references are passed inside a struct. This poses an issue for dropping self-referencing values, as the reference becomes invalid during the dropping process. Effectively, since self-referencing structs are not allowed in vanilla Rust, there is no allowance for dropping data during a function that has also been given a reference to that data. There’s usually no way to pass a
and &a
to the same function.
A fix was attempted, where the struct would be turned in to a raw byte array and only transformed back into the underlying data type inside each function. This is allowable as a reference can be created and die over the body of a function, as long as the reference was created inside the function. However, this is also not sound if the original struct contains padding. There is no way to initialize padding bytes to a known value that Miri will accept, so when it is cast to an array some of the bytes contain uninitialized values. This is not acceptable (despite the fact that these bytes are never read) due to the potential for optimizations that may read from the uninitialized bytes. Besides which, this fix does not allow for template or constant parameters as there is no way to check the size of a templated type without giving specific, concrete values for the template parameters.
At this point, I’m just going to move on to other things. self_cell appears to be an excellent alternative for anyone using this crate.
About this issue
- Original URL
- State: closed
- Created a year ago
- Reactions: 26
- Comments: 19 (5 by maintainers)
Commits related to this issue
- replace ouroborous crate with self_cell I picked ouroborous originally because the API was a bit nicer than that in self_cell (we get to pick our own field names with ouroborous, but don't in self_ce... — committed to KumoCorp/kumomta by wez a year ago
- fix: replace ouroboros with self_cell https://github.com/joshua-maros/ouroboros/issues/88 — committed to risingwavelabs/risingwave by xxchan a year ago
- Replace ouroboros (unsound) with self_cell See https://github.com/joshua-maros/ouroboros/issues/88 — committed to Yelp/casper by khvzak a year ago
- Bump ouroboros dependency Fixes a soundness issue. See: https://rustsec.org/advisories/RUSTSEC-2023-0042.html https://github.com/joshua-maros/ouroboros/issues/88 — committed to yeslogic/allsorts by adrianwong a year ago
- Bump ouroboros dependency Fixes a soundness issue. See: https://rustsec.org/advisories/RUSTSEC-2023-0042.html https://github.com/joshua-maros/ouroboros/issues/88 — committed to yeslogic/allsorts by adrianwong a year ago
- Stacked borrows: Miri enforces that references passed to functions are valid during the entire execution of the function, even when those references are passed inside a struct. See https://github.co... — committed to Ten0/serde_avro_fast by Ten0 6 months ago
- Stacked borrows: Strongly protected argument calls Miri enforces that references passed to functions are valid during the entire execution of the function, even if they are not dereferenced, and even... — committed to Ten0/serde_avro_fast by Ten0 6 months ago
Hey, I just wanted to say thank you for all the hard work you put into ouroboros.
ManuallyDrop is not currently specified to have the MaybeDangling behavior, and in the codegen backend (to my knowledge) there is no exception for ManuallyDrop. So this is not a bug, Miri is behaving as intended (and it would be bad for Miri to make an exception here if codegen doesn’t also have such an exception).
This looks like a good use-case to add to https://github.com/rust-lang/rfcs/pull/3336. Would it be possible for someone to prepare a brief writeup of what ouroboros needs, and how MaybeDangling is required to achieve that goal?
FWIW, there is currently one exception to this, and that is a
&UnsafeCell<T>
reference. Those must be dereferenceable on function entry, but they are permitted to be deallocated while the function runs.I intend to continue now that a fix has been found. I was planning on leaving due to there being nothing else I could do, since that is no longer the case I will keep working on it.
Did you consider transmuting to an array of MaybeUninit<u8> instead to avoid the padding/unitialized bytes problem?
🤷 IMO the advisory is good enough, people that care enough will see the warning in
cargo audit
and it’s not worth disrupting everyone else.That’s just my opinion though.
To be clear, when I said “intended behavior” I meant that this is not a Miri bug. I do think we should change this. But I can’t just change this in Miri, codegen needs to be adjusted first, and that probably needs an RFC, and that’s why I wrote https://github.com/rust-lang/rfcs/pull/3336.
I will be doing that but only after people have had a chance to migrate. I had previously yanked a version too soon after uploading a fix, and it caused a lot of chaos as many libraries were suddenly broken and unable to compile. Especially since the compiler does not currently actually produce unsound code, this issue is not pressing.
@joshua-maros, do you mind clarifying if you intend to continue working on
ouroboros
moving forward? The original post mentioned that you planned on moving on. Is that still the case (and 0.16 is the last version) or have plans changed?Any suggestions for alternatives that allow generics?
self_cell
is not a real replacement for even simple things, like wrapping aMutexGuard
unfortunately.Hey @joshua-maros, thanks for the efforts of maintaining
ouroboros
for years!!@joshua-maros wrapping in
ManuallyDrop
should palliate the issue at the moment, until we get that magic ofManuallyDrop
extracted into its own wrapper type,MaybeDangling
(https://github.com/rust-lang/rfcs/pull/3336).ManuallyDrop
currently does not prevent themiri
error, which I’d argue to be a bug (ManuallyDrop
should includeMaybeDangling
, shouldn’t it? cc @ralfjung)Until then,
MaybeUninit
does very much work; you may just loseOption
-discriminant-elision optimizations: