-
Notifications
You must be signed in to change notification settings - Fork 58
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
Thread cancellation, asynchronous unwinding exceptions and their interaction with drops #211
Comments
To be clear, Windows has a Forking a multi-threaded process also results in something which strongly resembles killing a thread: the child process after forking will only have a single thread, effectively making it look like all other threads have been killed. This has the same downsides as Since both forking and |
According to the documentation of TerminateThread "the system frees
thread's initial stack" which is exactly what constitutes a violation of
"must not remove before drop" and was decided to be unsound. If the thread
stack was leaked, such function would have been fineish to use from the
soundness perspective.
|
let mut var = 0;
crossbeam::scope(|scope| {
scope.spawn(|| loop { var += 1; } );
unsafe { pthread_exit(); }
}); Using Note that this includes the standard library and any external crates that you might be using. The case for external crates is simple: you are responsible for auditing them (with no help from Rust's type system). However for the standard library this is a bit more complicated: do we want to stable-guarantee that all of the libstd code involved in spawning a thread (
Ah, I seem to have missed that. In that case |
Asynchronous cancellation using
|
Plan is to have unwinding from FFI be fine if the functions are annotated with |
Also, thanks for writing down all these things. Most of those questions have been floated around during the All-hands, but I ended up not remembering any of them :) |
Regarding However using |
@Amanieu |
I don't think trying to make So, these will have to be unsafe operations. In terms of when they (and also e.g. |
Oh no this cannot possibly work. If you unwind e.g. during the execution of |
We know that none of these functions are safe. What we are trying to determine here is: under which conditions is using them (not) UB? Additionally, we are only look at this from the language point of view: everyone will agree that most Rust code assumes that panics don't come from arbitrary instructions and that destructors are executed while unwinding. If a user wants to do "fancy" stuff with unwinding (or the lack thereof) then he is solely responsible for auditing any code that he may be unwinding through to ensure that safety is maintained. |
Ack.
Right, we'll have to decide that. I said my 2 cents above. |
Could there could be some kind of annotation you could use to make this audit easier, at least locally? The compiler must know whether there are any fn may_longjmp() {
let foo = make_foo();
// Fail to compile if any destructors would run on unwind (here for foo)
#[no_drop_on_stack]
ffi_function_which_may_longjmp();
} You'd still need to annotate up your call stack to the point where |
What happens with |
I don't think any of the examples in the OP are affected by that? |
I haven't tried, but IIUC on Itanium-C++ ABI thread cancelation uses On windows, where a Notice that when (*) Note that this isn't a language bug, because we maybe don't provide any guarantees for what happens here at the language level - it'd be only a bug at the implementation level, were we want this to work in certain consistent ways, even if |
Also see rust-lang/reference#693 (comment) for an approximate model of stack frame and their dropping by @gnzlbg. |
I checked: On Darwin, On Linux/glibc, On both OSes, Here are some test results with both panic=unwind and panic=abort on Linux and macOS – various types of exotic crashes: https://gist.github.com/comex/401f5c02f066fdea8e06c75a28267247 |
@comex thanks!
This is interesting, because it doesn't work exactly like a longjmp since it runs some I tried to visualize all the info and came up with this table:
Instead of specifying the behavior of each of these on each platform, it might be better to specify the behavior of Rust depending on the columns.
So if you have a platform and want to call some function that panics, you'd just fill in a row in the table, and answer the questions to see when the behavior is defined, and to what. Something like that might end up reducing the amount of UB to few special cases, e.g.:
leaving a lot of useful cases as "well-defined". |
Closing in favor of #404 which is more specific than this one |
So during the all-hands we identified a use-case that would be good to be documented in the unsafe code guidelines.
There are at least three sources of "cancellation" which might end up "removing" (in Taylor’s words) a value without "dropping" it, which in turn results in unsoundness for e.g. rayon, crossbeam, &pin stuff. These sources are:
longjmp
/setjmp
(used in practice for error handling by rust lua and perhaps many other embedded languages/interpreters);pthread_cancel
: which can run either an asynchronous unwinding exception (might occur at any program point) or raise an unwindingexception at well specified points such assleep
; exact behaviour is specified bypthread_setcancelstate
.pthread_exit
,pthread_kill
: which will "stop" the thread, potentially executing some arbitrary code and cleaning the thread up (freeing thread’s stack).There’s no question that these functions may be useful in one scenario or the other, so it would be good if we figured out scenarios in which these functions are sound to use (e.g. if the thread stack contains only
Copy
types) and encoded this information into our unsafe code guidelines book.cc @nikomatsakis @cramertj
The text was updated successfully, but these errors were encountered: