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

mem::forget on stack frames #210

Closed
gnzlbg opened this issue Oct 16, 2019 · 4 comments
Closed

mem::forget on stack frames #210

gnzlbg opened this issue Oct 16, 2019 · 4 comments
Labels
A-drop Topic: related to dropping A-unwind Topic: related to unwinding C-open-question Category: An open question that we should revisit

Comments

@gnzlbg
Copy link
Contributor

gnzlbg commented Oct 16, 2019

(think longjmp / setjmp)

Some abstractions, like Pin (or crossbeam::scope), allow passing references to the current stack frame to other threads of execution, and use the destructor to make sure that these references do not dangle when the stack frame is dropped. That is, their safety invariant requires that their destructor is run, which does not happen if one mem::forgets their stack frame.

We guarantee that these abstractions are sound, that is, that safe Rust code cannot cause undefined behavior by using them. Therefore, a safe API that would allow to mem::forget a stack frame that contains a type with such safety invariants would be unsound if it does not require the user to restore this (*).

unsafe Rust code is actually allowed to temporarily break safety invariants as long as it restores them at the safe boundary. unsafe Rust can, in practice, also mem::forget stack frames by using its FFI feature to just call unknown code that does that (e.g. by calling longjmp). At the spec level, we do not say anywhere that calling such an FFI function is allowed, so I'd say that right now this is UB, at least by omission.

So what can unsafe code do with this unsafe feature ? I think that:

  • it can definitely mem::forget a stack-frame that only contains T: Copy types, since such types do not have the safety invariants mentioned above
  • it can also definitely mem::forget a stack-frame that contains types with destructors, as long as these types do not maintain any safety invariant like the one above
  • it can also mem::forget a stack-frame that contains a type with a safety invariant like the above, as long as it restores this invariant before leaving the safe code boundary (e.g. it could forget the frame, and push a previously saved frame to the end of the stack that restores it for these types)

I think it would be good to have a name for the safety invariant that these types rely on (e.g. "not mem::forget safe" ?).

I also think it would be good to guarantee somewhere when mem::forget on stack frames is correct.

By this I don't want to suggest that we should add a feature to Rust to do this. C has such features already, setjmp/longjmp, and currently at the implementation level we do guarantee that they work properly (**).

This issue isn't about setjmp/longjmp specifically, but about the possibility that Rust frames can get deallocated without calling their destructors, which is only one of the many ways in which longjmp can work.


(*) That is, I believe that if we had a way to "mark" those types, and could be able to tell if a stack frame contains one of them, it might be possible to build a safe API for this.


(**) For example, rlua passes C code that uses setjmp a Rust callback that calls some other C code that uses longjmp, such that Rust frames end up "sandwiched" between a setjmp and a longjmp. We have fixed bugs in the compiler to make sure that this "works" on all platforms, and have tests ensuring that this works.

Note that, in practice, a longjmp either mem::forgets a stack frame, or drops it, depending on the platform (some change the stack pointer, forgetting frames, others unwind calling destructors, and others unwind not calling destructors). We implement the Rust side of things such that no platform aborts when longjmp goes through an abort-on-panic shim, and also guarantee that longjmp works even when -C panic=abort. That is, on some platforms, doing a longjmp over Pin is actually safe because Pin destructor gets called.

@asajeffrey
Copy link

Every since ManuallyDrop became a thing, AFAIK Rust has no way (without macros) to guarantee drop code runs before deallocation.

@gnzlbg
Copy link
Contributor Author

gnzlbg commented Oct 16, 2019

@asajeffrey Is it possible to break Pins safety invariant on safe Rust using ManuallyDrop ?

@RalfJung
Copy link
Member

Also see #211

Is it possible to break Pins safety invariant on safe Rust using ManuallyDrop ?

AFAIK not. That would be a serious bug.
Basically it is up to whatever library that returns a pinned ptr to make sure the drop actually happens.

@RalfJung RalfJung added C-open-question Category: An open question that we should revisit T-drop labels Oct 16, 2019
@RalfJung RalfJung added A-unwind Topic: related to unwinding and removed T-drop labels Dec 14, 2019
@RalfJung RalfJung added the A-drop Topic: related to dropping label Feb 19, 2020
@RalfJung
Copy link
Member

Closing as a duplicate of #404. Also a bunch has happened since this was opened, see als the unwind ABI work and the notion of a "plain old frame".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-drop Topic: related to dropping A-unwind Topic: related to unwinding C-open-question Category: An open question that we should revisit
Projects
None yet
Development

No branches or pull requests

3 participants