mem::forget on stack frames #210
Labels
A-drop
Topic: related to dropping
A-unwind
Topic: related to unwinding
C-open-question
Category: An open question that we should revisit
(think longjmp / setjmp)
Some abstractions, like
Pin
(orcrossbeam::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 onemem::forget
s 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, alsomem::forget
stack frames by using its FFI feature to just call unknown code that does that (e.g. by callinglongjmp
). 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 thisunsafe
feature ? I think that:mem::forget
a stack-frame that only containsT: Copy
types, since such types do not have the safety invariants mentioned abovemem::forget
a stack-frame that contains types with destructors, as long as these types do not maintain any safety invariant like the one abovemem::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 whichlongjmp
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 useslongjmp
, such that Rust frames end up "sandwiched" between asetjmp
and alongjmp
. 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
eithermem::forget
s a stack frame, ordrop
s 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 whenlongjmp
goes through an abort-on-panic shim, and also guarantee thatlongjmp
works even when-C panic=abort
. That is, on some platforms, doing alongjmp
overPin
is actually safe becausePin
destructor gets called.The text was updated successfully, but these errors were encountered: