Stream: general

Topic: UnwindSafe


gnzlbg (Oct 31 2019 at 12:32, on Zulip):

@Steven Fackler so maybe a better way to explain UnwindSafe is to explain it as a "warning" that has false positives, and AssertUnwindSafe is just a way to toggle that warning off.

gnzlbg (Oct 31 2019 at 12:33, on Zulip):

In particular, it is ok and safe to always turn this warning off, and it is common to do so in generic code.

gnzlbg (Oct 31 2019 at 12:35, on Zulip):

When the warning triggers, user can either use it to improve their code, or ignore it.

Steven Fackler (Oct 31 2019 at 12:39, on Zulip):

I personally like to think about UnwindSafe as "a waste of time that you should bypass with as little effort as possible" :P

Steven Fackler (Oct 31 2019 at 12:39, on Zulip):

It's insufficiently precise to be useful in practice imo

gnzlbg (Oct 31 2019 at 12:41, on Zulip):

probably to late to deprecate it and remove it

gnzlbg (Oct 31 2019 at 12:42, on Zulip):

adding an auto trait to the language to provide a "warning" with a lot of false positives that is always ok to ignore, and for which most code's only option is to actually ignore it, doesn't sound like a good trade-off

Steven Fackler (Oct 31 2019 at 12:42, on Zulip):

yeah

Steven Fackler (Oct 31 2019 at 12:42, on Zulip):

we could in theory remove the bound on catch_unwind which I think may be the only place it comes up

gnzlbg (Oct 31 2019 at 12:42, on Zulip):

We could at least improve the docs to tell users "If you are here, just use AssertUnwindSafe"

Steven Fackler (Oct 31 2019 at 12:42, on Zulip):

not sure if that's really doable in practice though

gnzlbg (Oct 31 2019 at 12:43, on Zulip):

I think that's doable

Steven Fackler (Oct 31 2019 at 12:43, on Zulip):

worst case we can just add catch_unwind2 or whatever :P

gnzlbg (Oct 31 2019 at 12:43, on Zulip):

we probably can't remove the auto trait

Steven Fackler (Oct 31 2019 at 12:43, on Zulip):

and deprecate the traits

Steven Fackler (Oct 31 2019 at 12:43, on Zulip):

oh yeah they definitely can't go away since they're stable

gnzlbg (Oct 31 2019 at 12:43, on Zulip):

Yeah, deprecate everything, remove the bound on catch_unwind, that would save users the time to actually try to learn what this is for

gnzlbg (Oct 31 2019 at 12:45, on Zulip):

I will open an internal threads later, if there are others that feel that this would be a good idea, I might try wording a small RFC

Steven Fackler (Oct 31 2019 at 12:46, on Zulip):

something related I've been meaning to do is to write up a std::sync reform rfc, to move things out of the one big module into std::mutex etc and shift to non-poisoning versions

Amanieu (Oct 31 2019 at 13:04, on Zulip):

UnwindSafe and poisoning are two "pseudo-safety" mechanisms that in the end turned out to be mostly useless. I hope we've learned the lesson by now and will strictly stick to memory safety.

Steven Fackler (Oct 31 2019 at 14:58, on Zulip):

At least we got rid of the Freeze bound on Mutex::new :P

gnzlbg (Oct 31 2019 at 16:19, on Zulip):

something related I've been meaning to do is to write up a std::sync reform rfc, to move things out of the one big module into std::mutex etc and shift to non-poisoning versions

Please do.

gnzlbg (Oct 31 2019 at 16:21, on Zulip):

I really have no idea why a panic through a mutexguard panics a mutex.

gnzlbg (Oct 31 2019 at 16:22, on Zulip):

Can't it just release the lock ? The unsafe code using the guard needs to make sure that in case of a panic the content behind the mutex has its invariants restored anyways.

rkruppe (Oct 31 2019 at 16:25, on Zulip):

Poisoning is not for safety (as you can see from the fact that un-poisoning is safe), just like UnwindRef it was intended as a guardrail against logical bugs from observing broken internal invariants due to untimely panics

gnzlbg (Oct 31 2019 at 16:27, on Zulip):

Sure, but if a panic can cause some unsafe code to leave some invariants broken, then that unsafe code is broken, independently of whether poisoning or unwindref are used

gnzlbg (Oct 31 2019 at 16:28, on Zulip):

Its more like a second line of defense against broken code, which ends up being a bit weird if one deals with code that's not broken

rkruppe (Oct 31 2019 at 16:33, on Zulip):

The invariants in question can also be logical invariants that aren't relevant for safety but only for correct behavior of the program

chabing (Nov 02 2019 at 01:58, on Zulip):
fn main() {
    println!("Hello, world!");
}
chabing (Nov 02 2019 at 01:58, on Zulip):
fn main() {
chabing (Nov 02 2019 at 01:58, on Zulip):
fn main() {
    println!("Hello, world!");
}
gnzlbg (Nov 02 2019 at 08:54, on Zulip):

@chabing wrong thread ?

RalfJ (Nov 03 2019 at 13:09, on Zulip):

I recently met someone who considered Mutex poisoning one of the best innovations of Rust in the exception safety space and that ocaml might copy it for dealing with their "async exceptions"

RalfJ (Nov 03 2019 at 13:09, on Zulip):

so, not sure about removing that one

RalfJ (Nov 03 2019 at 13:09, on Zulip):

but for UnwindSafe, yes, that didn't really pull its weight

RalfJ (Nov 03 2019 at 13:10, on Zulip):

@gnzlbg have you opened that IRLO thread yet?

gnzlbg (Nov 04 2019 at 09:43, on Zulip):

not yet

gnzlbg (Nov 04 2019 at 12:12, on Zulip):

@RalfJ if a Rust program can result in a lock being poisoned, I think the program has a bug.

gnzlbg (Nov 04 2019 at 12:12, on Zulip):

poisoning is not the way to fix the bug

gnzlbg (Nov 04 2019 at 12:15, on Zulip):

more like a diagnostic mechanism.
If helping users find these bugs and fixing them is the goal, poisoning is overkill.
We could, e.g., have a flag that dumps the stack trace and aborts when a panic unwinds a lock, in a similar vein to how parking_lot locks have optional dead-lock detection (if your program has a deadlock, it has a bug as well - we could poison the locks instead, but that isn't the goal there either).

Laurențiu Nicola (Nov 04 2019 at 13:23, on Zulip):

if a Rust program can result in a lock being poisoned, I think the program has a bug.

One use-case is detecting if someone (e.g. a user) outside of the program kills a thread, or when the program itself does it. It's considered to be a bad idea, but I've seen people calling TerminateThread, for various reasons. Another one is probably software that goes the extra mile to handle errors, panic and restart, à la Erlang.

Laurențiu Nicola (Nov 04 2019 at 13:27, on Zulip):

I think lock poisoning is still somewhat unexplored and has potential for some interesting applications. I'm not convinced I want the standard libraries to do it, but isn't it a bit late anyone? How can we get rid of poisoning without changing the API?

Lokathor (Nov 04 2019 at 15:40, on Zulip):

Yeah if a lock is poisoned it's sometimes rational to ignore the poison and keep going (eg: a global RNG state which can't possibly be bad), and sometimes you want to report it to a log file, or maybe do other things. I don't think there's a single good answer to all those.

gnzlbg (Nov 04 2019 at 16:52, on Zulip):

One use-case is detecting if someone (e.g. a user) outside of the program kills a thread, or when the program itself does it. It's considered to be a bad idea, but I've seen people calling TerminateThread, for various reasons. Another one is probably software that goes the extra mile to handle errors, panic and restart, à la Erlang.

One of the reasons this is a bad idea is because this does not work. When you kill a thread, nothing guarantees that if that threads holds a lock the lock will be poisoned.

gnzlbg (Nov 04 2019 at 16:53, on Zulip):

A program that assumes that this is the case already has a bug.

gnzlbg (Nov 04 2019 at 16:54, on Zulip):

If you happen to, e.g., be on Linux, and the thread happens to call a cancellation point close to when something tries to kill it, then that cancellation point will unwind with a foreign exception, which catch_unwind will silently let through, and that will probably poison a std::sync::Mutex

gnzlbg (Nov 04 2019 at 16:55, on Zulip):

but one of the other things that can happen if you don't hit a cancellation point and you haven't configured your thread in specific ways is asynchronous cancellation, where the OS raises a signal, and if you don't have a signal handler to catch it, the thread just dies, no unwinding

gnzlbg (Nov 04 2019 at 16:57, on Zulip):

or if instead of unwinding from a cancellation point like glibc does, your program uses musl which longjmps, unwinding the thread stack without running destructors

Laurențiu Nicola (Nov 04 2019 at 17:08, on Zulip):

Gah, kill it with fire.

simulacrum (Nov 04 2019 at 17:56, on Zulip):

It would be interesting to try and run crater or manually audit code to determine if there are uses of the poisoned mutex that do something other than unwrap or reach through the poison error; if I'm following correctly, our assertion is that no such valid uses exist.

rkruppe (Nov 04 2019 at 20:04, on Zulip):

The assertion being that poisoning is useless? Unwrapping is perfectly consistent with poisoning being useful, as it will terminate the program when it reaches circumstances that the program hasn't been written to handle (similar to e.g. an out-of-bounds slice index). Sure, a ton of those unwraps are probably written with a mindset of not caring about poisoning and never get exercised when the program runs, but you can't determine whether that's the case by locally inspecting the code.

simulacrum (Nov 04 2019 at 21:16, on Zulip):

Right, this isn't a test for poisoning being useless, but rather that there are no "interesting" uses, so to speak

simulacrum (Nov 04 2019 at 21:17, on Zulip):

not sure if that makes sense :)

rkruppe (Nov 04 2019 at 21:20, on Zulip):

I don't know what "interesting" uses you imagine. The two uses you describe are pretty much the only two (modulo what you do after you've reached through the poison error) I can imagine even in a world where everyone unambiguously agrees that poisoning is Good

simulacrum (Nov 04 2019 at 21:22, on Zulip):

right -- I mean that maybe there's code that does something with the poison error other than re-panic or getting the value out anything

simulacrum (Nov 04 2019 at 21:23, on Zulip):

getting the value out anyway*

simulacrum (Nov 04 2019 at 21:23, on Zulip):

but I'm also not sure what we would get out of such examples, so it may not be worth the trouble :)

rkruppe (Nov 04 2019 at 21:24, on Zulip):

Ah, yes, that would by conceivable. For example, resetting the state to a sensible default so that the program can carry on as normal without seeing more poisoning errors in the future.

simulacrum (Nov 04 2019 at 21:26, on Zulip):

My thought, I guess, is that if there are such uses, that might inform us better as to the uses for poisoning; e.g. one could imagine that you instead of returning Result<Guard<..., T>, PoisonError<T>> or just T always return some sort of type that deref's to T and has a method for "is_poisoned"

simulacrum (Nov 04 2019 at 21:26, on Zulip):

obviously this isn't backwards compatible :)

RalfJ (Nov 05 2019 at 09:18, on Zulip):

RalfJ if a Rust program can result in a lock being poisoned, I think the program has a bug.

@gnzlbg Isn't that the same statement as "if a Rust program can result in a panic, the program has a bug"?

RalfJ (Nov 05 2019 at 09:20, on Zulip):

but one of the other things that can happen if you don't hit a cancellation point and you haven't configured your thread in specific ways is asynchronous cancellation, where the OS raises a signal, and if you don't have a signal handler to catch it, the thread just dies, no unwinding

but in that case the lock will also remain locked, right? so a future call to lock() just don't return.

RalfJ (Nov 05 2019 at 09:21, on Zulip):

though with get_mut one could likely still get to the data...

gnzlbg (Nov 05 2019 at 09:44, on Zulip):

unsafe code is required to be safe in the presence of panics

gnzlbg (Nov 05 2019 at 09:45, on Zulip):

such that catching a panic is safe

gnzlbg (Nov 05 2019 at 09:47, on Zulip):

with thread cancellation, right now we just have instant UB

gnzlbg (Nov 05 2019 at 09:48, on Zulip):

even if thread cancellation unwinds

gnzlbg (Nov 05 2019 at 09:48, on Zulip):

So you can get a poisoned mutex, but your program already has UB

RalfJ (Nov 05 2019 at 09:55, on Zulip):

well that's even "better" then, at least in terms of justifying poisoning

gnzlbg (Nov 05 2019 at 09:56, on Zulip):

how come?

gnzlbg (Nov 05 2019 at 09:56, on Zulip):

sounds like doing a null pointer check after having dereferenced a null pointer

RalfJ (Nov 05 2019 at 09:57, on Zulip):

how come?

someone caused UB by doing thread cancellation. poisoning only has to protect against things that can actually happen in UB-free programs.

gnzlbg (Nov 05 2019 at 09:58, on Zulip):

ah yeah, but in UB free programs, there is really no safety issue with just ignoring that a lock has been poisoned

gnzlbg (Nov 05 2019 at 09:59, on Zulip):

its a good feature to have if you want to diagnose a particular situation, just like deadlock detection

gnzlbg (Nov 05 2019 at 10:01, on Zulip):

but requiring all code to handle whether a lock has been poisoned feels unnecessary, like handling OOM

RalfJ (Nov 05 2019 at 10:21, on Zulip):

ah yeah, but in UB free programs, there is really no safety issue with just ignoring that a lock has been poisoned

poisoning is about correctness, not safety

gnzlbg (Nov 05 2019 at 10:49, on Zulip):

so is deadlock freedom

gnzlbg (Nov 05 2019 at 10:49, on Zulip):

and many other things

gnzlbg (Nov 05 2019 at 10:50, on Zulip):

While a deadlock is always a bug, a poisoned mutex is only sometimes a bug

gnzlbg (Nov 05 2019 at 10:51, on Zulip):

The question is whether it is a good trade-off to force all users of mutexes to check whether the mutex has been poisoned

gnzlbg (Nov 05 2019 at 10:52, on Zulip):

Is the cost of having to do that worth the amount of logic bugs it catches ?

RalfJ (Nov 05 2019 at 12:47, on Zulip):

agreed

Zoxc (Nov 20 2019 at 06:07, on Zulip):

Lock poisoning is also completely useless and pure noise with panic=abort

Last update: Nov 20 2019 at 11:30UTC