-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
prevent unwinding past FFI boundaries in code generation #18510
Comments
Assigning P-high, not 1.0. |
Triage: I'm not aware of a code change here, I believe that we are expecting people to handle this themselves with /cc @rust-lang/libs @rust-lang/lang is that the only solution we are pursing here, or do we plan on doing it automatically eventually? |
@steveklabnik IIRC, there's still work to do to ensure that you correctly get an abort if you don't recover from the panic before hitting the boundary. I believe that's what this issue is about. |
cc @ranma42 |
Has there been any work done on this? Automatically inserting catch_unwind statements at FFI boundaries that automatically abort on uncaught panics seems like a straightforward way to close this soundness hole. I see where @brson silently adjusted the tags, so maybe the core team reviewed it back in August. |
UPDATE: This comment appears to have been a bit confused! See revised instructions below. No work has been done on this to my knowledge, but I am in favor of the solution you proposed @coder543, and would be happy to work with you on implementing it. If nothing else, it would help us gain experience with overhead etc. I think that the strategy we would use is to modify the code that generates MIR (our mid-level intermediate IR) for calls, which is found in this file here. You can see that it generates the code to execute upon unwind right here, on this line -- currently, this is always cleanup code. We could modify it to inspect the type of the value being invoked (and, in particular, its ABI) and generate alternate unwind code that aborts (I'm not sure the best way to encode an abort right now though). The function being called is stored in the The main question then is how to generate MIR that aborts. Right now I think we have no way to encode an abort into the IR. We could probably add an ABORT terminator (i.e., a new variant of the MIR data structure So, if you were going to implement this, I think we would do the following steps:
That might be two PRs, or maybe one. To get started, one might also skip the first step, and just experiment with having the compiler print out a match when it finds a C ABI function, and then worry about how to handle it. |
I'm happy to give it a shot!
I mean, isn't using this in a function going to generate some MIR that leads to an abort? Would it be possible to just generate either that, or something equivalent to the MIR for ::sys::abort_internal?
That seems like a good way to start this. I'll go ahead and get things set up here to start doing compiler dev. The compiler is doing its inital build now under Ubuntu 17.04. |
I have things set up where I can print things to stderr from the relevant block of code, recompile through stage 1 for fast rebuilds, and then use the generated compiler to compile a small Rust source file, which causes those debug print statements to be generated. So, everything is configured. Now, to "just" figure out how to print out a match when encountering a C ABI function. I'm done for tonight, but I hope to pick it up again tomorrow evening. |
I actually ended up staying awake for a little longer, and I now have it only debug print a message when it encounters a function call to a C ABI function. I'm not sure |
@nikomatsakis I think at this point it's safe to say I'm going to need help figuring out where to go from here. |
@coder543 Are you still interested in working on this? If not I might try to during the "impl days" in RustFest Zurich. @nikomatsakis It sounds like this is blocked on a mentor providing some more help ;) |
I still doesn't understand whether this issue is supposed to apply to Rust code calling an |
The current situation is that foreign functions are marked as |
I assumed the latter: Rust unwinding into some foreign stack frame is the case where we have some control? |
@SimonSapin feel free to take over! |
@coder543 hey, sorry I dropped the ball on those notifications! Missed them somehow. Still digging out of a months-long hole. Also, re-reading my mentoring instructions, I think I was confused! That is, I also believe that the goal is to catch unwinds in an That said, it's still true that we need an abort terminator, but the job is substantially easier I think. We just need to find when we are generating MIR for @arielb1 does that sounds reasonable to you? |
@nikomatsakis Hey, it's alright. I would have loved to help on this issue, but I'll look for other |
Okay, since I've been complaining about this bug I think maybe I should make an attempt to fix it. :-) But it's my first time this far into the compiler. I started off with this advice:
I could not find any specific "final unwind block" that's already generated. And in the case where the C function merely calls another (Rust) function, why should there be one? edit: The function changed is librustc_mir/build/mod.rs:construct_fn.
Does this seem reasonable, or am I just completely far off so that I better leave it to someone more knowledgeable in order not to consume your time? (Ping @nikomatsakis or someone else who would like to provide some mentoring here) |
@diwic you're in the right general direction -- but I think what I had in mind was modifying the terminator that we create on this particular line. This is lazilly constructed when there is a need to generate unwinding code. (Oh, and plausibly yes we could do the same for PS feel free to reach out on gitter as well. |
It's a lot to take in if you have not worked on the compiler's internals before. So I'm working on it, but not very fast... @nikomatsakis Assume the following code:
Here's the MIR output for
there is no resume cleanup block that we can modify. So instead we need to add a new one, I believe?
This is what I want to do:
...because that's when we tell LLVM our function is nounwind. But probably I picked the wrong id, because it returns false regardless of abi. |
Would it be possible to opt out of the abort generation? Perhaps by tagging the function with the existing #[unwind] attribute? My use case is that I would like to be able to catch Rust panics from the C/C++ side. This is for writing a standard library for a toy language. I realise such unwinding is undefined and hence there are no guarantees. But it would still be useful, at my own risk. |
I have two concerns about this approach: 1) does it really work in practice, and if so what kind of exception is a Rust exception in C++ and 2) why can't you use |
@nikomatsakis or anyone who would like to provide mentoring/review/feedback: So I made some progress today! (Yay!) As can be seen in diwic@729092c It's probably not ready for PR yet, could use some mentoring / feedback:
|
Yes! Tests should definitely be added. One way to do this is to write a
Can you say a bit more, I'm not sure what that means?
OK, I'll double-check when you open a PR.
It is experimental and does not happen by default. You can enable it with In general, it'd be great if you wanted to open a PR and we can discuss in more depth there! Feel free to tag it with |
Second attempt here: #46833 |
Generate Abort instead of Resume terminators on nounwind ABIs. rust-lang#18510 Signed-off-by: David Henningsson <[email protected]>
Thank you so much for pushing on this old bug @diwic! |
JFTR, as implemented, tagging the function with the #[unwind] attribute does opt out of the abort generation. |
re-opening as 1.24.1 removed this behavior, even though it's expected to come back soon. |
|
Closing this in favor of #52652 (which is more recent and active). |
Oops, sorry I didn’t find this when opening #52652 ! |
It's undefined to unwind past an FFI boundary such as a
pub extern "C" fn
. Code generation should automatically insert a landing pad doing an abort. This will eliminate the class of memory safety errors resulting from unwinding into C from Rust. LLVM will be able to optimize it out if it is being caught and handled explicitly, such as to translate into an error code for C.EDIT: Mentoring instructions can be found here.
The text was updated successfully, but these errors were encountered: