-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Resolve unsound interaction between noalias and self-referential data (incl. generators, async fn) #63818
Comments
Responding to @RalfJung's post from #63209:
Those hypothetical optimizations could be disabled in async fns across suspend points, though, couldn't they? I can't think of any case where this would force the compiler to pessimize all code because of the mere possibility of async being involved. Therefore, the question is whether those optimizations being performed on async fns specifically is more valuable than (By the way, I still want async fns to be able to |
I've not weighed in on this but I wanted to leave a few thoughts. Let me know if I'm confused about something. First off, the conflict here is between the reference to a generator type G and some This seems important: it means that, in principle, we ought to be able to understand that the Also, of course, this problem isn't really specific to generators but is rather a pattern that emerges with any self-referential setup. Presently, safe Rust has no way to express self-referential structs (apart from generators), but I think it's something we would eventually like to support. So let's posit that we find a version of stacked borrows that understands self-referential structs. Clearly, this doesn't mean we can add Does this all sound correct? |
I think it's very important for generators to be able to print out their locals in a |
Possibly. I am not sure what the memory would look like though. Specifically...
... it is not enough, in general, if only "one side" of a conflict is informed.
Agreed. I do not have a concrete idea for how to do that, though.
The question is, zero-cost when compared with what? Disabling optimizations to enable debug printing does have a cost, And even if we rule out optimizations across yield points, we'd still require a non-trivial extension to Stacked Borrows to permit debug printing. That extension will likely have cost elsewhere, in terms of code getting optimized less. In my view, the only solution one could really call zero-cost is the one that disallows debug printing. |
Added back the "unsound" label; to my knowledge, the LLVM IR we are currently generating for some safe self-referential futures is UB and thus we got a soundness bug here. |
Do you mean "with
Indeed. The other thing we have going for us here, I think, is that we can be somewhat selective about what sorts of operations on possible on the generator reference. It's not exactly the same as having a In other words, at the end of the day, there is a valid seeming usage pattern that we want to capture, right? In particular:
This also seems to be true for This pattern also seems analogous to a typical Is this correct or is the pattern we are trying to describe more complex than that? |
@RalfJung If I recall, one of the more interesting cases you how you can simultaneously have a let p = &RefCell::new(22);
let q = p.borrow_mut();
drop(p);
drop(q); This required some sort of special treatment that was focused on It seems like the generator scenario we are describing here is quite similar, no? |
See #63787. (I'll come back to your other points when I find some more time.) |
There are a couple of possible "solutions" in between, e.g., having all Generators implement |
The pattern you are describing seems fine, yes. I think it actually already is fine with current Stacked Borrows if the "outer" thing is a raw pointer and never turned into a reference. In some cases is this currently not possible but will be when rust-lang/rfcs#2582 lands -- except that the
They don't, though. At least, not operationally. They use the wrong pointer to access the field pointed to by the reference -- namely, they use the "outer" pointer.
Why suspend and reactivate? When the generator is closed you said yourself that the pointer may not be used? I don't think a suspend-and-reactivate method is compatible with So I guess after all I do not agree with your model and probably did not understand it. Until that last paragraph I thought we were in agreement but we actually do not seem to be talking about the same thing. |
One point, which may or may not be obvious, is that printing out variables which are mutably borrowed across the suspend point is unsound, even without optimizations. For example, if the borrowed variable is of type struct Annoying(RefCell<Box<i32>>); ...then the function could use |
Addendum on the |
We discussed this issue in the @rust-lang/lang meeting yesterday (recording etc available here, though for this particular issue you'll want to jump in to about the 50 minute mark or so) we discussed this issue. We came to a rough consensus on a solution for how to integrate the current generator design while retaining noalias on In short, the idea is that if you have a In other words, the rules for an This is somewhat analogous to the way that a We also discussed Finally, we discussed whether this "type-based rule" was more complex than a (hypothetical) OK, that's the best of my recollection. Feel free @RalfJung or @Centril to correct me -- and of course the recording is available if you'd like the full details. |
Immutably borrowed is fine right? |
If the variable is only immutably borrowed, I think it's fine, but this relies on there not being an "intermediate" mutable borrow from which the immutable one was reborrowed. |
Indeed -- looks like this was never properly referenced here:
@comex do you remember if we ever had an actual miscompilation example for self-referential generators based on that bad |
rust-lang/rfcs#3336 offers another avenue for resolving this issue properly -- by re-defining |
From the plan discussed with the lang team years ago:
Discussion with @digama0 uncovered that there is a problem with this plan -- to So maybe it does make sense after all to tie the |
It seems to me that making |
@Darksonn yeah you are probably right (Zulip discussion here). |
I don't remember, but it wouldn't be hard to make one. My miscompilation example in this post about |
I discovered a potentially problematic interaction between self-referential generators and the |
@comex found an actual compilation result that can be explained by saying that I feel like we should remove the |
The RFC in rust-lang/rfcs#3467 should pave the way for resolving this issue properly. |
Turns out just making |
…r-errors Supress niches in coroutines to avoid aliasing violations As mentioned [here](rust-lang#63818 (comment)), using niches in fields of coroutines that are referenced by other fields is unsound: the discriminant accesses violate the aliasing requirements of the reference pointing to the relevant field. This issue causes [Miri errors in practice](rust-lang/miri#3780). The "obvious" fix for this is to suppress niches in coroutines. That's what this PR does. However, we have several tests explicitly ensuring that we *do* use niches in coroutines. So I see two options: - We guard this behavior behind a `-Z` flag (that Miri will set by default). There is no known case of these aliasing violations causing miscompilations. But absence of evidence is not evidence of absence... - (What this PR does right now.) We temporarily adjust the coroutine layout logic and the associated tests until the proper fix lands. The "proper fix" here is to wrap fields that other fields can point to in [`UnsafePinned`](rust-lang#125735) and make `UnsafePinned` suppress niches; that would then still permit using niches of *other* fields (those that never get borrowed). However, I know that coroutine sizes are already a problem, so I am not sure if this temporary size regression is acceptable. `@compiler-errors` any opinion? Also who else should be Cc'd here?
…r-errors Supress niches in coroutines to avoid aliasing violations As mentioned [here](rust-lang#63818 (comment)), using niches in fields of coroutines that are referenced by other fields is unsound: the discriminant accesses violate the aliasing requirements of the reference pointing to the relevant field. This issue causes [Miri errors in practice](rust-lang/miri#3780). The "obvious" fix for this is to suppress niches in coroutines. That's what this PR does. However, we have several tests explicitly ensuring that we *do* use niches in coroutines. So I see two options: - We guard this behavior behind a `-Z` flag (that Miri will set by default). There is no known case of these aliasing violations causing miscompilations. But absence of evidence is not evidence of absence... - (What this PR does right now.) We temporarily adjust the coroutine layout logic and the associated tests until the proper fix lands. The "proper fix" here is to wrap fields that other fields can point to in [`UnsafePinned`](rust-lang#125735) and make `UnsafePinned` suppress niches; that would then still permit using niches of *other* fields (those that never get borrowed). However, I know that coroutine sizes are already a problem, so I am not sure if this temporary size regression is acceptable. `@compiler-errors` any opinion? Also who else should be Cc'd here?
…r-errors Supress niches in coroutines to avoid aliasing violations As mentioned [here](rust-lang#63818 (comment)), using niches in fields of coroutines that are referenced by other fields is unsound: the discriminant accesses violate the aliasing requirements of the reference pointing to the relevant field. This issue causes [Miri errors in practice](rust-lang/miri#3780). The "obvious" fix for this is to suppress niches in coroutines. That's what this PR does. However, we have several tests explicitly ensuring that we *do* use niches in coroutines. So I see two options: - We guard this behavior behind a `-Z` flag (that Miri will set by default). There is no known case of these aliasing violations causing miscompilations. But absence of evidence is not evidence of absence... - (What this PR does right now.) We temporarily adjust the coroutine layout logic and the associated tests until the proper fix lands. The "proper fix" here is to wrap fields that other fields can point to in [`UnsafePinned`](rust-lang#125735) and make `UnsafePinned` suppress niches; that would then still permit using niches of *other* fields (those that never get borrowed). However, I know that coroutine sizes are already a problem, so I am not sure if this temporary size regression is acceptable. `@compiler-errors` any opinion? Also who else should be Cc'd here?
…r-errors Supress niches in coroutines to avoid aliasing violations As mentioned [here](rust-lang#63818 (comment)), using niches in fields of coroutines that are referenced by other fields is unsound: the discriminant accesses violate the aliasing requirements of the reference pointing to the relevant field. This issue causes [Miri errors in practice](rust-lang/miri#3780). The "obvious" fix for this is to suppress niches in coroutines. That's what this PR does. However, we have several tests explicitly ensuring that we *do* use niches in coroutines. So I see two options: - We guard this behavior behind a `-Z` flag (that Miri will set by default). There is no known case of these aliasing violations causing miscompilations. But absence of evidence is not evidence of absence... - (What this PR does right now.) We temporarily adjust the coroutine layout logic and the associated tests until the proper fix lands. The "proper fix" here is to wrap fields that other fields can point to in [`UnsafePinned`](rust-lang#125735) and make `UnsafePinned` suppress niches; that would then still permit using niches of *other* fields (those that never get borrowed). However, I know that coroutine sizes are already a problem, so I am not sure if this temporary size regression is acceptable. `@compiler-errors` any opinion? Also who else should be Cc'd here?
Supress niches in coroutines to avoid aliasing violations As mentioned [here](rust-lang/rust#63818 (comment)), using niches in fields of coroutines that are referenced by other fields is unsound: the discriminant accesses violate the aliasing requirements of the reference pointing to the relevant field. This issue causes [Miri errors in practice](rust-lang#3780). The "obvious" fix for this is to suppress niches in coroutines. That's what this PR does. However, we have several tests explicitly ensuring that we *do* use niches in coroutines. So I see two options: - We guard this behavior behind a `-Z` flag (that Miri will set by default). There is no known case of these aliasing violations causing miscompilations. But absence of evidence is not evidence of absence... - (What this PR does right now.) We temporarily adjust the coroutine layout logic and the associated tests until the proper fix lands. The "proper fix" here is to wrap fields that other fields can point to in [`UnsafePinned`](rust-lang/rust#125735) and make `UnsafePinned` suppress niches; that would then still permit using niches of *other* fields (those that never get borrowed). However, I know that coroutine sizes are already a problem, so I am not sure if this temporary size regression is acceptable. `@compiler-errors` any opinion? Also who else should be Cc'd here?
Supress niches in coroutines to avoid aliasing violations As mentioned [here](rust-lang/rust#63818 (comment)), using niches in fields of coroutines that are referenced by other fields is unsound: the discriminant accesses violate the aliasing requirements of the reference pointing to the relevant field. This issue causes [Miri errors in practice](rust-lang/miri#3780). The "obvious" fix for this is to suppress niches in coroutines. That's what this PR does. However, we have several tests explicitly ensuring that we *do* use niches in coroutines. So I see two options: - We guard this behavior behind a `-Z` flag (that Miri will set by default). There is no known case of these aliasing violations causing miscompilations. But absence of evidence is not evidence of absence... - (What this PR does right now.) We temporarily adjust the coroutine layout logic and the associated tests until the proper fix lands. The "proper fix" here is to wrap fields that other fields can point to in [`UnsafePinned`](rust-lang/rust#125735) and make `UnsafePinned` suppress niches; that would then still permit using niches of *other* fields (those that never get borrowed). However, I know that coroutine sizes are already a problem, so I am not sure if this temporary size regression is acceptable. `@compiler-errors` any opinion? Also who else should be Cc'd here?
Self-referential generators violate LLVM's expectations for
noalias
due to the overlapping bounds of the interior references and the&mut self
argument to theGenerator::resume
function.Original issue description: async/await is possibly unsound -- I don't consider myself an authority here, so feel free to edit this top post with relevant information.
Current discussion happened in #63209
The text was updated successfully, but these errors were encountered: