Skip to content
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

Open
Mark-Simulacrum opened this issue Aug 22, 2019 · 50 comments
Labels
A-async-await Area: Async & Await A-coroutines Area: Coroutines AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@Mark-Simulacrum
Copy link
Member

Mark-Simulacrum commented Aug 22, 2019

Self-referential generators violate LLVM's expectations for noalias due to the overlapping bounds of the interior references and the &mut self argument to the Generator::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

@Mark-Simulacrum Mark-Simulacrum added I-nominated T-lang Relevant to the language team, which will review and decide on the PR/issue. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness A-async-await Area: Async & Await labels Aug 22, 2019
@Mark-Simulacrum
Copy link
Member Author

cc @rust-lang/lang @comex @rkruppe @RalfJung

@cramertj cramertj changed the title async/await unsoundness Resolve interaction between self-referential generators and stacked borrows Aug 22, 2019
@cramertj cramertj added AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. and removed I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness AsyncAwait-Unclear labels Aug 22, 2019
@comex
Copy link
Contributor

comex commented Aug 22, 2019

Responding to @RalfJung's post from #63209:

I think this is fundamental. Adding derive(Debug) makes the exact content of local variables at any yield point observable. Interesting optimizations based on mutable references being unique rely on the content mutable references point to to not be observable by any other alias.

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 derive(Debug) is.

(By the way, I still want async fns to be able to derive(Clone), although that would obviously require pretty severe restrictions on the body of the function.)

@nikomatsakis
Copy link
Contributor

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 &mut reference T contained within the generator. What we don't have is two &mut A and &mut B references that overlap where neither A nor B gives any indication of arising from a generator.

This seems important: it means that, in principle, we ought to be able to understand that the &mut G reference is somehow special. It is certainly true that the self-referential nature of generators will require extending "stacked borrows" to be more flexible. But I think that generators are something we want to have, and it doesn't seem surprising that our aliasing rules may need to be extended to accommodate and understand them. (If though we have conflict between two random types &mut A and &mut B, that seems more worrisome, though it is likely still true that we want generators regardless.)

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 noalias to &mut references everywhere, we'll either have to extend LLVM or be selective in some other way (as e.g. @comex suggests here).

Does this all sound correct?

@Aaron1011
Copy link
Member

I think it's very important for generators to be able to print out their locals in a Debug implementation. With all of the effort that's gone into making async functions and futures zero-cost, it would be a shame if they became intrinsically less debuggable than a hand-written state machine.

@RalfJung
Copy link
Member

Those hypothetical optimizations could be disabled in async fns across suspend points, though, couldn't they?

Possibly. I am not sure what the memory would look like though. Specifically...

First off, the conflict here is between the reference to a generator type G and some &mut reference T contained within the generator. What we don't have is two &mut A and &mut B references that overlap where neither A nor B gives any indication of arising from a generator.

... it is not enough, in general, if only "one side" of a conflict is informed. &mut T expects everyone else to respect its rules. If we allow &mut T and *mut T to conflict arbitrarily (because *mut T "knows" to expect conflicts), we get basically no optimizations.

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.

Agreed. I do not have a concrete idea for how to do that, though.

I think it's very important for generators to be able to print out their locals in a Debug implementation. With all of the effort that's gone into making async functions and futures zero-cost, it would be a shame if they became intrinsically less debuggable than a hand-written state machine.

The question is, zero-cost when compared with what? Disabling optimizations to enable debug printing does have a cost, async fn then get optimized less than other functions.

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.

@RalfJung RalfJung added the I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness label Aug 23, 2019
@RalfJung
Copy link
Member

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.

@RalfJung RalfJung changed the title Resolve interaction between self-referential generators and stacked borrows Resolve unsound interaction between self-referential generators (and async fn) and stacked borrows Aug 23, 2019
@RalfJung RalfJung changed the title Resolve unsound interaction between self-referential generators (and async fn) and stacked borrows Resolve unsound interaction between self-referential generators (and async fn) and noalias Aug 23, 2019
@RalfJung RalfJung changed the title Resolve unsound interaction between self-referential generators (and async fn) and noalias Resolve unsound interaction between self-referential generators (incl. async fn) and noalias Aug 23, 2019
@nikomatsakis
Copy link
Contributor

@RalfJung

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 -Zno-alias"?

... it is not enough, in general, if only "one side" of a conflict is informed.

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 &mut reference A to a struct and a (simultaneous) &mut reference B to its fields, in that you could use A to access the data from B directly. With a generator, you don't get to reach in and inspect and access its state arbitrarily. You just get to pass it around until you "open it", right? (At which point you aren't passing around the generator anymore.)

In other words, at the end of the day, there is a valid seeming usage pattern that we want to capture, right? In particular:

  • The generator reference is "opened", when generator is invoked
    • now the internal references are active and can be used
    • the reference to the generator itself is not used in this period (right?)
  • then the generator is "closed", when generator yields
    • now the internal references are "suspended"
    • the reference to the generator can be passed around in this period, but it is not used until the generator is opened again

This also seems to be true for Debug impls -- they correspond to opening the generator, inspecting its state, and closing it.

This pattern also seems analogous to a typical &mut reborrow, except that the reborrow can be suspended and reactivated. (Looked at in this way, it's not surprising that indeed it would require an extension of the underlying model to express.)

Is this correct or is the pattern we are trying to describe more complex than that?

@nikomatsakis
Copy link
Contributor

@RalfJung If I recall, one of the more interesting cases you how you can simultaneously have a & reference to a ref-cell and an &mut reference to its interior:

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 UnsafeCell, because passing around p didn't invalidate q in the way that a reference to the interior would typically be invalidated.

It seems like the generator scenario we are describing here is quite similar, no?

@RalfJung
Copy link
Member

@nikomatsakis

If I recall, one of the more interesting cases you how you can simultaneously have a & reference to a ref-cell and an &mut reference to its interior:

See #63787.

(I'll come back to your other points when I find some more time.)

@gnzlbg
Copy link
Contributor

gnzlbg commented Aug 24, 2019

In my view, the only solution one could really call zero-cost is the one that disallows debug printing.

There are a couple of possible "solutions" in between, e.g., having all Generators implement Debug, but having the impls printing nothing, e.g., "Generator(debug-printing is off)" unless -C generator-debug-print is enabled, which we could enable on debug builds by default, or similar.

@RalfJung
Copy link
Member

Do you mean "with -Zno-alias"?

-Zmutable-noalias, yes. It is my understanding that a soundness bug with that flag is still a soundness bug. We want to change the default of that once LLVM is fixed.

In other words, at the end of the day, there is a valid seeming usage pattern that we want to capture, right? In particular:

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 Pin<&mut T> unsafe API also uses mutable references and those might pose problems.

This also seems to be true for Debug impls -- they correspond to opening the generator, inspecting its state, and closing it.

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.

This pattern also seems analogous to a typical &mut reborrow, except that the reborrow can be suspended and reactivated. (Looked at in this way, it's not surprising that indeed it would require an extension of the underlying model to express.)

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 noalias or with the desired optimizations, unless suspending is an explicit action on the to-be-suspended pointer. I don't know what that action would look like.

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.

@comex
Copy link
Contributor

comex commented Aug 24, 2019

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 RefCell::get_mut to get a mutable reference to the interior of the Box and save it across the suspend point, while the Debug impl for Annoying could use RefCell::borrow_mut to replace it with a new Box.

@RalfJung
Copy link
Member

Addendum on the derive(Debug) story: when I said "we can't have it", I meant "we can't debug-print fields that are currently borrowed". All the other fields could still be printed.

@nikomatsakis
Copy link
Contributor

nikomatsakis commented Aug 30, 2019

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 &mut, which I will describe below. Of course, all of this is subject to some caveats in that stacked borrows and all work on aliasing models is still a work-in-progress.

In short, the idea is that if you have a &mut Generator reference, it would be treated similarly to a *mut Generator reference for the purposes of aliasing rules (in stacked borrows terms, SharedRw; in LLVM terms, it would not be marked noalias). In this way, the fact that the &mut Generator (embedded in a Pin) co-exists with the &mut to some of its fields doesn't cause an aliasing conflict, any more than a *mut would.

In other words, the rules for an &mut T reference are not the same for all T, but depend on whether that T is considered "self-referential" -- for now, the only case where this occurs is a generator, but we could extend this in the future. This would allow us to accommodate patterns like rental, which presently may be unsound (we did not discuss rental, owning-ref, or any other crate in any detail, to be clear). If we extended safe Rust with some other form of self-referential struct (which I personally would like to do), then it would presumably leverage the same mechanism.

This is somewhat analogous to the way that a &UnsafeCell<T> reference can co-exist with a &mut T reference to its interior. This also requires some special treatment in stacked borrows.

We also discussed Debug impls. The short version is that Debug impls are not a problem as long as they respect the rules of the borrow checker. That is, if you try to dump the contents of a generator, it should not include the values of variables that were borrowed. This seems fine (and, to me, self-evident, but perhaps not to all). It would of course require some compiler smarts to implement but nothing particularly beyond the pale.

Finally, we discussed whether this "type-based rule" was more complex than a (hypothetical) &pin T would have been. Ralf's response was basically "not really". Or, in more detail, "sort of yes, but mostly because we are trying to be more optimized". In particular, the type-based resolution permits one to have types where only part of the type is self-referential (e.g., a tuple like (Generator, u32)). If you had a &pin (Generator, u32), you would apply conservative treatment to the whole structure. But if you have a type-based rule, we might be more precise. This also arisees around Cell, as I noted above, and in general we are trying to be more precise but it's a bit of a pain (and e.g. Ralf is already choosing to approximate around enums so as to keep the specification tractable).

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.

@bjorn3
Copy link
Member

bjorn3 commented Aug 30, 2019

That is, if you try to dump the contents of a generator, it should not include the values of variables that were borrowed.

Immutably borrowed is fine right?

@RalfJung
Copy link
Member

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.

@RalfJung
Copy link
Member

RalfJung commented Nov 5, 2022

I believe there's a workaround for types that are !Unpin.

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 noalias?

@RalfJung
Copy link
Member

RalfJung commented Nov 5, 2022

rust-lang/rfcs#3336 offers another avenue for resolving this issue properly -- by re-defining Pin<T> as a newtype of MaybeDangling<T>, which gets rid of the noalias annotations.

@RalfJung
Copy link
Member

RalfJung commented Nov 5, 2022

From the plan discussed with the lang team years ago:

In short, the idea is that if you have a &mut Generator reference, it would be treated similarly to a *mut Generator reference for the purposes of aliasing rules (in stacked borrows terms, SharedRw; in LLVM terms, it would not be marked noalias). In this way, the fact that the &mut Generator (embedded in a Pin) co-exists with the &mut to some of its fields doesn't cause an aliasing conflict, any more than a *mut would.

In other words, the rules for an &mut T reference are not the same for all T, but depend on whether that T is considered "self-referential" -- for now, the only case where this occurs is a generator, but we could extend this in the future. This would allow us to accommodate patterns like rental, which presently may be unsound (we did not discuss rental, owning-ref, or any other crate in any detail, to be clear). If we extended safe Rust with some other form of self-referential struct (which I personally would like to do), then it would presumably leverage the same mechanism.

This is somewhat analogous to the way that a &UnsafeCell reference can co-exist with a &mut T reference to its interior. This also requires some special treatment in stacked borrows.

Discussion with @digama0 uncovered that there is a problem with this plan -- to &mut T must still always be non-overlapping to make sure that functions like mem::swap are sound. So even if we had a way for some mutable references to opt-out of noalias, we still couldn't have actually aliasing mutable references. A UnsafeAlias type that acts on mutable references the same way UnsafeCell acts on shared references thus would be very hard to use correctly, and have very strange requirements. The only safe APIs that seem to be sound on top of this are self-referential structs that use pinning -- where the outside world still only ever has a single &mut, and the aliases are all hidden internally.

So maybe it does make sense after all to tie the noalias resolution to pinning here -- either by making the Unpin trait special (which however was never designed for that and is regrettably a safe trait) or by defining Pin<T> such that it allows aliasing even if T is a mutable reference -- hence the relation to MaybeDangling. Conceptually tying this to Pin makes more sense since an &mut Generator (not pinned!) must still be fully unique.

@Darksonn
Copy link
Contributor

Darksonn commented Nov 5, 2022

It seems to me that making Pin special would not work. Pin projections generally unwrap the Pin to get out the mutable reference inside, so any time you project onto a field, you assert unique ownership of the self-referential struct, invalidating the internal pointers. Am I missing something?

@RalfJung
Copy link
Member

RalfJung commented Nov 5, 2022

@Darksonn yeah you are probably right (Zulip discussion here).

@comex
Copy link
Contributor

comex commented Nov 7, 2022

@comex do you remember if we ever had an actual miscompilation example for self-referential generators based on that bad noalias?

I don't remember, but it wouldn't be hard to make one. My miscompilation example in this post about PollFn worked by effectively having LLVM infer noalias on an async fn poll implementation's self argument, thanks to that argument being derived from an &mut PollFn which was itself marked noalias. If rustc didn't exempt references to !Unpin types from noalias, you could just remove the futures::join! / PollFn wrapper from the example, and it would still miscompile.

@RalfJung
Copy link
Member

RalfJung commented Dec 3, 2022

I discovered a potentially problematic interaction between self-referential generators and the dereferenceable attribute: rust-lang/unsafe-code-guidelines#381.

@RalfJung
Copy link
Member

@comex found an actual compilation result that can be explained by saying that dereferenceable is like having a "fake read" when the function starts, and that read is relevant for noalias concerns. (Another solution has been proposed in that thread but I think it has major issues, and discussion has not continued since I pointed those out.)

I feel like we should remove the dereferenceable from !Unpin mutable references. Right now that is the only clear way I see to ensure that Miri detects all the LLVM UB here. Hence I made a PR: #106180.

@RalfJung
Copy link
Member

RalfJung commented Aug 6, 2023

The RFC in rust-lang/rfcs#3467 should pave the way for resolving this issue properly.

@RalfJung
Copy link
Member

RalfJung commented Aug 2, 2024

Turns out just making !Unpin references not noalias is insufficient, one can still cause unintended UB with self-referential futures as discovered in rust-lang/miri#3780. Just like we had to make UnsafeCell block niches to discriminant reads causing aliasing trouble with the UnsafeCell contents, we also need to block niches for fields of a future that are references by other fields. IOW, async lowering should wrap such fields in UnsafePinned, and that type needs to block niches the same way UnsafeCell does.

bors added a commit to rust-lang-ci/rust that referenced this issue Sep 4, 2024
…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?
bors added a commit to rust-lang-ci/rust that referenced this issue Sep 6, 2024
…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?
bors added a commit to rust-lang-ci/rust that referenced this issue Sep 7, 2024
…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?
bors added a commit to rust-lang-ci/rust that referenced this issue Sep 8, 2024
…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?
RalfJung pushed a commit to RalfJung/miri that referenced this issue Sep 10, 2024
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?
lnicola pushed a commit to lnicola/rust-analyzer that referenced this issue Sep 25, 2024
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?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await Area: Async & Await A-coroutines Area: Coroutines AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests