-
Notifications
You must be signed in to change notification settings - Fork 179
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
yoke
soundness issue with non-'static
and contravariant cart, using Yoke::attach_to_cart
+ Yoke::get
.
#2926
Comments
Oh, that's pretty insidious. Some easy fixes would be to either restrict carts to However I think we might be able to handle this by PhantomData'ing the cart so that it's forced to be covariant (which when mixed with contravariance, leads to invariance, so we're golden) |
I have a fix that forces things to be invariant, which basically stops the step from However, I'm somewhat uneasy because I'm not convinced that the This is a very subtle interaction between variance, HRTBs, and well-formedness, and WFness isn't documented well enough for me to be able to give a clear answer, but I'm investigating by poking around and I'll see what I find. |
Forcing the cart to be invariant should indeed fix the problem; forcing "covariance" in the right sense - while perhaps more desirable (since it's less restrictive) - is not really possible with the yoke API, AFAICT. The cart's type parameter itself already is covariant in |
For context: this issue in a different crate had a comparable underlying problem but their API involves a macro call that can know about all the lifetime parameters of the (equivalent of the) |
Another way to fix this issue would be to introduce a (covariant) lifetime argument for |
Yep, Rust does let you force all lifetimes in a parameter to be invariant, but it does not let you force them to be "at most covariant", since Rust's variance tools are the ability to:
So the only tools that let you change variance are the ones that invert it (we don't want that), and the ones that force invariance. But anyway, I'm now convinced this is a compiler/language bug (likely related to rust-lang/rust#25860), and am in the process of filing one. Might still land a fix here since it's a complex bug that may never get fixed. |
I'm not seeing any compiler bugs here, but feel free to elaborate, and I can add my interpretation of the situation when I have more time at hand, later today. |
@steffahn Bug filed at rust-lang/rust#106431 The bug is that your My mitigation tactic (force all lifetimes in the cart to be treated as invariant) prevents such a contravariant carted Yoke from being used maliciously after the Yoke is created; however the Yoke should not have been possible to create in the first place. |
Another potential fix would be to make
This gives maximum flexibility but disallows a couple things (that I personally don't care about) from going through the Either way, let me make my PR with the current fix, and we can debate whether we should force invariance on carts or force |
Fix in #2949 |
(I didn't read enough of the yoke PR and misinterpreted bits; what I say here is not new information and matches what you've said.) I don't fully understand the interaction going on here, but given
and this supposedly implying This matches some vague memory of mine as well, where projections interact with implied bounds poorly. If the goal is forcing that the output borrows from the input, |
W.r.t. #2095, a W.r.t. this issue, though, I don't know if it's possible for the trait to guarantee As such, while I'm still weakly in favor of introducing our own ... would it be a sufficient fix to make F: for<'de> FnOnce(CartView<'de, C>) -> <Y as Yokeable<'de>>::Output, with the goal of struct CartView<'de, C> {
view: &'de <C as Deref>::Target,
safe: InvariantMarker<<C as Deref>::Target>,
}
impl Deref for CartView<'de, C> {
type Target = <C as Deref>::Target;
fn deref(&self) -> &Self::Target {
self.view
}
} If I've understood the unsoundness attack properly, this might be sufficient to block it while only making Though note that a targeted fix to Footnotes
|
Maybe to add more explanation, since it’s not the most intuitive thing how adding a lifetime argument alone fixes the issue: The The covariance of Then, the Regarding usage, the common case of carts without lifetimes could just use If the cart had two lifetime In other words: Previous users that might have had used yoke in a struct as |
Yeah, I'd rather not change the Yoke API to include a lifetime, even if the common case gets a I think the plan of restricting |
I agree that Though perhaps as an alternative middle ground, we could define Not doing so is certainly much simpler, though, and I'm having trouble coming up with a use-case that wants a non- Most somewhat realistic cases I can come up with are yoking to another zero-copy structure, e.g. It's perhaps an unfortunate restriction as only an approximation of the actual requirement, and thus probably deserves a note in the docs somewhere, but I don't see it being much more than a technical footnote in practice. I'll often point to the hoops yoke jumps through as fundamentally required for generalized self-heap-referencing; for that purpose it's good if "cart pointee is (Having a |
On the note of more powerful extensions that can be narrowed down with a type synonym: There could be two alternative wrappers for the cart to choose from, and an unsafe trait. E. g. a trait This naturally does not discuss the trade-off of leaving the API more simple vs. more powerful; especially since type synonyms like this tend to make reading the docs a bit harder. |
Yeah, also it's worth noting that (even though yes, replace_cart still has issues) |
Yeah I'm deliberately avoiding PowerYoke-type solutions because those types leak out quickly and Yoke already is in a situation where it can both lead to gnarly compiler bugs (hopefully mostly fixed), and gnarly error messages. |
If you want If attach_to_cart wants to borrow local variables, it would need to put the local variables into the cart. It's easy to do that with a single variable, but two variables with potentially different lifetimes might be tricky. You would need to copy them into a lifetimeless struct, which defeats the purpose of borrowing the local variables. |
Here’s a repro, in small steps with useful type annotations
Run Online in Rust Explorer
The text was updated successfully, but these errors were encountered: