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

Abort by default when unwinding through an unsafe block? #512

Closed
glaebhoerl opened this issue Dec 10, 2014 · 5 comments
Closed

Abort by default when unwinding through an unsafe block? #512

glaebhoerl opened this issue Dec 10, 2014 · 5 comments

Comments

@glaebhoerl
Copy link
Contributor

I don't know whether this is a good idea or if it's been considered before, but it at least seems worth considering if it hasn't been.

The idea is simply:

If unwinding (panic!) reaches an unsafe block, the process is aborted unless there is some explicit indication (e.g. a #[unwinding_allowed] attribute on the unsafe block, or whatever) that it should keep going.

Rationale:

unsafe blocks are one of the only places in the language where unwinding (exception safety) needs to be explicitly reasoned about. If the programmer neglects to do this, the unsafe block may actually be unsafe and fail to uphold core memory safety requirements in the presence of unwinding. In this case, aborting isn't anyone's idea of a good time, but it is less bad than corrupted memory and security bugs.

It may be more prudent to assume that the programmer hasn't considered the possibility of unwinding until she explicitly indicates otherwise.

Unresolved questions:

Is aborting when unwinding hits the unsafe boundary "enough", or by then can it already be "too late"?

@Gankra
Copy link
Contributor

Gankra commented Dec 10, 2014

So this would only abort when the unwinding hits the actual edge of the unsafe{ declaration? It could unwind through heaps of unsafe fns first?

Uncertain how precise people are with their unsafe boundaries. Like, I call this one unsafe fn, so I wrap it unsafe. But the world isn't totally safe until a few lines later where I've fully addressed the actual safety issues.

@glaebhoerl
Copy link
Contributor Author

Just another comment I wanted to add:

It's important that this wouldn't necessarily mean that all unsafe blocks everywhere would eventually end up looking like #[unwinding_allowed] unsafe { ..., or whatever. In that case people would end up adding the attribute habitually, mostly defeating the purpose. But when your expectation is that unwinding can't happen in the unsafe block (which I think is not uncommon?), you would intentionally leave it off. In that case it would be a kind of assertion that unwinding shouldn't happen here (and like an assert, would protect against your expectation being violated).

So this would only abort when the unwinding hits the actual edge of the unsafe{ declaration?

As written, yes.

Like, I call this one unsafe fn, so I wrap it unsafe. But the world isn't totally safe until a few lines later where I've fully addressed the actual safety issues.

Yeah. Provided that people scope their unsafe blocks properly (so that they're (meant to be) externally safe, unlike the above example), then would this idea suffice to turn unsafe blocks that are safe except in the presence of unwinding into ones that are safe even with unwinding?

When people scope it too narrowly, I'm not sure if there's anything we can do, at least nearly as easily. The fact that people don't have clear and strong enough guidance about where to put the unsafe boundary is a separate (and legitimate) issue that might've been addressed by something like #193.

(But along a similar line of thought, perhaps one way it could be made coarser, if we wanted to, would be to (also?) abort if unwinding hits the outermost scope of any function which contains an unsafe block without #[unwinding_allowed]. But this might lead to a much higher false positive rate.)

@pcwalton
Copy link
Contributor

Note that aborting the process does not ensure memory safety, because processes can share memory. (In general, I think the process-vs-thread distinction is often overemphasized when unwinding is discussed; threads may not share memory, and processes can share memory, so the distinction as far as memory is concerned is about defaults—file descriptors, etc. are a different story, however).

@Stebalien
Copy link
Contributor

@glaebhoerl Triage: This issue is largely academic at this point (breaking change). Mind closing?

@glaebhoerl
Copy link
Contributor Author

FWIW in theory this could still make sense as a kind of per-crate opt-in, or a #[assert_nounwind] attribute or something. But those are considerably more niche.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants