├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── charter.md ├── faq.md ├── langteam-sync ├── README.md ├── design-meeting-2020-03-02.md ├── design-meeting-2020-03-16.md ├── meeting-2019-10-17.md ├── meeting-2020-03-26.md └── report-2019-10-17.md ├── periodic-summaries ├── November-1stHalf.md ├── October-2ndHalf.md └── README.md ├── planning ├── project-planning.md ├── roadmap │ ├── README.md │ ├── c-unwind-abi.md │ ├── propagate-native-exception-through-Rust-frame.md │ └── propagate-rust-panic-through-native-frame.md └── scope-by-platform-table.md ├── posts ├── gh-comments │ ├── c-unwind-stabilization-report.md │ └── extern-c-change-stabilization.md └── inside-rust │ ├── 01-announcement.md │ └── 02-deallocate-stack-frames.md ├── resolved-concerns ├── 2019-10-17-tbd-on-stable.md └── README.md ├── rfcs ├── 0000-c-unwind-abi.md ├── 0000-longjmp-pof-annotation.md └── announcement-and-charter.md ├── terminology ├── misc.md └── spec-terminology.md └── use-cases.md /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Project "ffi-unwind" 2 | 3 | A [working-group project][shepherds-blog] to extend the language to avoid 4 | undefined behavior when using control-flow constructs that cross between 5 | language boundaries. 6 | 7 | - Shepherds: 8 | - [acfoltzer (Adam)](https://github.com/acfoltzer) 9 | - [batmanaod (Kyle)](https://github.com/batmanaod) 10 | - Rust lang team contacts: 11 | - [nikomatsakis (Niko)](https://github.com/nikmoatsakis) 12 | - [joshtriplett (Josh)](https://github.com/joshtriplett) 13 | - [Our chat room][zulip-room] 14 | - [Our charter](charter.md) 15 | - [Cross-language unwinding FAQ](faq.md) 16 | - [Our project planning](planning/project-planning.md) 17 | - [Technical items in scope (table)](planning/scope-by-platform-table.md) 18 | - [Technical items on our roadmap](planning/roadmap/) 19 | 20 | [shepherds-blog]: http://smallcultfollowing.com/babysteps/blog/2019/09/11/aic-shepherds-3-0/ 21 | [zulip-room]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/welcome/near/177543226 22 | 23 | # Contributing 24 | 25 | We do not have a formal concept of membership. Please feel free to join our 26 | [Zulip chat room][zulip-room] or to open a GitHub Issues and/or Pull Request. 27 | 28 | -------------------------------------------------------------------------------- /charter.md: -------------------------------------------------------------------------------- 1 | # Charter 2 | 3 | ### Mission statement 4 | 5 | To extend the language to avoid undefined behavior when using control-flow 6 | constructs that cross between language boundaries. 7 | 8 | ### Unwinding 9 | 10 | The "MVP" for unwinding, with remaining open questions, was specified in 11 | [RFC-2945][unwind-rfc]. 12 | 13 | ### `longjmp` and `pthread_cancel` 14 | 15 | This is under active discussion in Zulip. 16 | 17 | [unwind-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md 18 | -------------------------------------------------------------------------------- /faq.md: -------------------------------------------------------------------------------- 1 | ## FAQ questions 2 | 3 | ### Why use a new ABI? Why not an attribute? 4 | 5 | - Unlike ABIs, there is no syntax for assigning attributes to function 6 | pointers. The ABI must be part of the type system so that callers of function 7 | pointers know whether or not the function may unwind. 8 | 9 | ### What should I do if I want to have C++ functions that unwind into Rust? [DRAFT; TODO] 10 | 11 | - mark declarations in Rust `extern "C unwind"` 12 | - Do *not* use `catch_unwind` in calling code 13 | - compile w/ `panic = unwind` 14 | - Check compiler, linker, & platform documentation (TODO: expand on this) 15 | - Re-test for every compiler update 16 | - (TODO: other caveats?) 17 | 18 | ### How does cross-language unwinding differ from cross-language `setjmp`/`longjmp`? 19 | 20 | - `setjmp`/`longjmp` across Rust frames is currently intended to have 21 | well defined behavior as long as those frames do not contain 22 | destructors, although we don't have any documentation to that 23 | effect. See [rust-lang/unsafe-code-guidelines#210] for more details. 24 | - When crossing frames that do contain destructors, the behavior of 25 | `longjmp` is [Undefined Behavior]; conversely, a primary goal of 26 | defining cross-language unwinding behavior is to support crossing 27 | frames with destructors. 28 | - Rust does not have a concept of `Copy` for stack-frames, which would permit 29 | the compiler to check that `longjmp` may safely traverse those frames. Such a 30 | language feature [may be added in the future][centril-effects], but although 31 | it would be useful for `longjmp`, it would not be useful for unwinding. 32 | - It should never be assumed that `drop` will be called for objects in 33 | intermediate frames traversed by a `longjmp`, but this may occur on certain 34 | platforms. Rust provides no guarantee either way (which is why this is 35 | considered [Undefined Behavior]). Cross-language unwind, however, will be 36 | defined such that `Drop` objects whose frames are unwound are guaranteed 37 | `drop`ed. 38 | - Unwinding across Rust frames when `panic = abort` is currently undefined 39 | behavior, but we plan to define the behavior to cause the application to 40 | `abort`. The behavior of `setjmp`/`longjmp`, however, is independent of the 41 | `panic` runtime. 42 | - unwinding involves the use of a personality function, which raises additional 43 | cross-language compatibility concerns; `setjmp`/`longjmp` does not. 44 | 45 | [centril-effects]: https://github.com/Centril/rfc-effects/issues/11 46 | 47 | ### What new constraints does this ABI place on Rust's unwinding mechanism? 48 | 49 | - Ideally: none. However, if Rust's default unwinding mechanism changes, a 50 | translation layer will be required to maintain the `C unwind` ABI. 51 | - One more concern is how `panic = abort` will be handled; please refer to [the 52 | roadmap][roadmap-panic-abort] for details. 53 | 54 | [roadmap-panic-abort]: planning/roadmap/c-unwind-abi.md#panic--abort 55 | [Undefined Behavior]: /spec-terminology.md#UB 56 | [rust-lang/unsafe-code-guidelines#210]: https://github.com/rust-lang/unsafe-code-guidelines/issues/210 57 | -------------------------------------------------------------------------------- /langteam-sync/README.md: -------------------------------------------------------------------------------- 1 | This directory is for write-ups related to synchronous discussions between the 2 | FFI-unwind project WG and the Rust lang team. 3 | 4 | * "report" notes are write-ups to present to the lang team (may be less formal than the name would imply) 5 | * "meeting" notes are write-ups drafted during a team meeting 6 | -------------------------------------------------------------------------------- /langteam-sync/design-meeting-2020-03-02.md: -------------------------------------------------------------------------------- 1 | This was the originally scheduled date for the design meeting, but due to a 2 | miscommunication, the lang team was not in attendance. The actual meeting 3 | occurred on [March 16th](design-meeting-2020-03-16.md). 4 | 5 | Participants: Amanieu and Kyle Strand 6 | Notes copied from https://hackmd.io/rG_5ksyCTuKsjks5cHONZQ 7 | 8 | # FFI-unwind design meeting 3/2 9 | 10 | ## Prefer proposal 2 to proposal 1 11 | 12 | Preserving `longjmp` behavior on Windows regardless of `panic=abort` is important. 13 | 14 | Distinguishing between forced/unforced foreign exceptions is technically feasible (this was the main concern about implementing proposal 2) 15 | 16 | - On libunwind, check the `_US_FORCE_UNWIND` flag. 17 | - On SEH, do the same as `catch (...)` which doesn't catch `longjmp`. 18 | 19 | ## Non-ABI attributes could be added to proposal 3 for parity of expressiveness w/ proposal 2 20 | 21 | `#[nounwind]` - a la C++ 22 | `#[abort_if_panic_equals_abort]` (name TBD) - a way to mark external functions such that even with `panic=abort`, they will be invoked with a shim that has landing pads to abort when a non-forced exception is encountered (this is the behavior of `"C unwind"` in proposal 2) 23 | 24 | ### What about function pointers? 25 | 26 | Proposal 2 provides a way to limit code size by marking *function pointers* as "nounwind" (i.e. `extern "C"`). For Proposal 3, to get the same optimization, the function pointer would need to be given an attribute, which currently isn't part of the language; also, this annotated type would need to be a different _type_. 27 | 28 | ```rust 29 | // Proposal 3, after adding function-pointer-annotations to the language 30 | 31 | extern "C" { 32 | #[nounwind] fn marked_nounwind(); 33 | #[abort_if_panic_equals_abort] fn marked_abort(); 34 | fn default_abi(); 35 | } 36 | 37 | fn calls_nounwind(f: #[nounwind] fn()) { 38 | f() 39 | } 40 | 41 | fn main () { 42 | calls_nounwind(marked_nounwind); // UB if unwinds 43 | calls_nounwind(unmarked); // compile error :D 44 | default_abi(); // UB if unwinds under -C panic=abort 45 | marked_nounwind(); // UB if unwinds 46 | marked_abort(); // aborts if unwinds under -C panic=abort 47 | } 48 | ``` 49 | 50 | ```rust 51 | // Proposal 2, no function-pointer-annotations 52 | 53 | extern "C unwind" { 54 | fn marked_unwind(); 55 | } 56 | 57 | extern "C" { 58 | fn default_abi(); 59 | } 60 | 61 | fn calls_nounwind(f: extern "C" fn()) { 62 | f() // Aborts 63 | } 64 | 65 | fn main () { 66 | calls_nounwind(default_abi); // UB if unwinds 67 | calls_nounwind(marked_unwind); // compile error :D 68 | marked_unwind(); // aborts if unwinds under -C panic=abort 69 | default_abi(); // UB if unwinds 70 | } 71 | ``` 72 | 73 | ### Do we need "unwind" variants for other ABIs? (e.g. "system") 74 | 75 | "system" is only used on x86 Windows, probably not worth supporting `"system unwind"`. Same with other ABIs. 76 | -------------------------------------------------------------------------------- /langteam-sync/design-meeting-2020-03-16.md: -------------------------------------------------------------------------------- 1 | Notes copied from 2 | https://paper.dropbox.com/doc/ffi-unwind-design-meeting--AxQqqMXwYCbyYe2rfwtZaQ~vAg-Utb2e8ehhS42CYfDSj9h4 3 | 4 | # Notes and minutes from 16 March 2020 design meeting 5 | - The biggest question before us is: 6 | - do we want a “C unwind” ABI or not 7 | - With Proposal Two: 8 | - extern “C” means “no unwind”, if you try to unwind you get UB 9 | - exception: “forced unwind”, so long as there are no dtors 10 | - extern “C unwind” means 11 | - if you invoke with `-Cpanic=abort`, we’ll catch the exception and abort 12 | - Proposal 3 13 | - no “C unwind” 14 | - just have “extern C” but unwinding is allowed when `-Cpanic=unwind` and otherwise not 15 | - exception: “forced unwind”, so long as there are no dtors 16 | - basically the same as C code calling C++ that may throw an exception 17 | - Size measurements? 18 | - No because these proposals all basically allow us to avoid overhead, 19 | - Note that in proposal 3: 20 | - extern “C” with panic=unwind can unwind, and hence there might be some landing pads that are not optimized away 21 | - relatively minor case 22 | - but users who are size-sensitive are often using panic=abort anyway and this doesn’t apply then 23 | - If we added a “no-unwind attribute” to functions, then: 24 | - we could get some size wins (but wouldn’t apply to ptr calls unless we propagate to type system) 25 | - you would also get the reasoning benefits 26 | - From previous meeting, came to conclusion that 27 | - A “C unwind” ABI is “better” than a unified “C” ABI if the annotations are all correct: 28 | - 29 | - A single “C” ABI is better in the following ways: 30 | - Less error prone, 31 | - If the user invokes a function that has “C” ABI but actually unwinds: 32 | - Given a “C unwind” ABI, that is UB 33 | - Given a unified ABI, it unwinds successfully 34 | - Some cases where this could happen somewhat unexpectedly: 35 | - C++ could throw bad-alloc exceptions 36 | - Ralf raised some soundness concerns around `catch_unwind`: 37 | - code may rely on `catch_unwind` to catch exceptions, do some “fixup”, and then re-throw the exception 38 | - currently, `catch_unwind` lets foreign exceptions unwind through 39 | - but this is relatively easy to fix 40 | - ultimately an orthogonal concern 41 | - If we adopt a unified ABI: 42 | - the main downside is that you can have libraries that “rely” on C++ exceptions 43 | - those libraries work in -Cpanic=unwind 44 | - but they are “incompatible” with -Cpanic=abort, the exceptions just start to cause UB 45 | - to rely on C++ exceptions “portably” 46 | - you have to catch the C++ exceptions in C++ code 47 | - or compile with `-fno-exceptions` (i.e., don’t throw exceptions) 48 | - What about things like `read` which might need to unwind on some platforms with cancelation etc? 49 | - those are forced unwinds: 50 | - with panic=abort, they only work reliably if you have no destructors 51 | - with panic=unwind, they would run destructors but this is not portable 52 | - Advantage of “Proposal 2”, if you get your annotations right, your code works “right” (or, at least, it aborts) regardless of whether you use `-Cpanic=abort` or `-Cpanic=unwind` 53 | - but unexpected exceptions fail more strongly even in `-Cpanic=unwind` 54 | - Another advantage of “Proposal 2” is 55 | - if Rust decides to adopt a non-native unwinding representation, then the impact will be smaller, because most “C” functions are known not to unwind 56 | - we’d only have to adapt the “C unwind” shims 57 | - Some common scenarios 58 | - Invoking a C library that uses longjmp error recovery (e.g. lua): 59 | - Under all proposals, you can do that so long as the frames being jumped over have no destructors 60 | - Same applies to `pthread_exit` or similar pthread mechanisms 61 | - Invoking a C++ function that throws exceptions which we wish to propagate through Rust code 62 | - this could include `bad_alloc` etc 63 | - Has anyone asked for this? 64 | - This requires `-Cpanic=unwind` to work, and the C++ code cannot be compiled with `-fno-exceptions` 65 | - Proposals 1/2: 66 | - You would use “C unwind” ABI to signal that it is happening 67 | - Proposal 3: 68 | - You would just use “C” to invoke the C++ code 69 | - Invoking a C++ function that throws exceptions and we catch them in the C++ code 70 | - Proposals all basically work equivalently, since there are no foreign exceptions propagating 71 | - Rust code invoking C (or C++…) that invokes Rust (R1..C..R2) 72 | - Want to have Rust panics from R2 propagate through C code and get caught by R1 73 | - Proposals 1/2: 74 | - Use “C unwind” for the middle C code (which will be invoked by pointer) 75 | - Use “C unwind” at the R2 entry points 76 | - Proposal 3: 77 | - Use “C” for everything (and compile everything with panic=unwind), works fine 78 | - They do need the ability to invoke extern functions via fn pointers that will unwind 79 | - Under any proposal, it would be “observable” what type string we use for Rust panics etc 80 | - If the C code had destructors etc, then they would execute under any proposal 81 | - C code invoke Rust that invokes C 82 | - this is “C++ functions that throw exceptions that propagate through Rust” 83 | - There are procedural macros for catching Rust panics and turning them into error codes before passing them back to C++ 84 | - What shims do we insert and other things? 85 | - Proposal 1/2: 86 | - invoking a “C” ABI function 87 | - always marked as “nounwind” 88 | - invoking a “C unwind” ABI functions 89 | - with `-Cpanic=abort`, we catch non-forced exceptions and turn them into aborts 90 | - forced exceptions will be UB if there are destructors in scope, so they can propagate freely 91 | - generating the body of an `extern` `"``C``"` Rust function 92 | - with `-Cpanic=unwind`, we have to catch Rust panics and turn them into aborts 93 | - with `-Cpanic=abort` we don’t have to because it already aborted 94 | - you can do this with only safe code, hence the concern 95 | - generating the body of an `extern "C unwind"` Rust function 96 | - “do nothing” 97 | - Proposal 3: 98 | - C and Rust calls are basically “the same” modulo other ABI differences 99 | - with `-Cpanic=abort` — we can consider them “nounwind” 100 | - what’s the story here around forced unwind anyway? “well, they’re a bit magic” 101 | - with `-Cpanic=unwind` — we do not :) 102 | - Subtyping etc? 103 | - We do not plan compiler to permit coercions between C unwind and C 104 | - but users can write their own shims 105 | - and we could conceivably allow compiler to do it 106 | - Debug mode shims 107 | - e.g. for places where UB would be introduced — have to check compilation time etc 108 | - Key observations: 109 | - Introduce ABI “C unwind” is more work to implement, though definitely do-able 110 | - Depending audience, learning curve for “C unwind” may be higher or less high 111 | - e.g. explaining the need to use “C unwind” 112 | - But distinguishing “C unwind” ABIs does allow us to: 113 | - potentially switch Rust unwinding in the future 114 | - identify Rust libraries that are relying on unwinding and handle that case more gracefully with `-Cpanic=abort` 115 | - Tracking issue: 116 | -------------------------------------------------------------------------------- /langteam-sync/meeting-2019-10-17.md: -------------------------------------------------------------------------------- 1 | Centril: rather not see "unspecified behavior" (at least this definition) added 2 | as new RFC; apparent preference for "implementation-defined" behavior. But, 3 | likes idea of RFCs from this WG defining their own terminology 4 | 5 | ScottMCM: "opposite perspective - many disagreements in Discord about what's 6 | 'really' undefined", would be helpful to have a standardized definition of 7 | "unspecified behavior" 8 | -------------------------------------------------------------------------------- /langteam-sync/meeting-2020-03-26.md: -------------------------------------------------------------------------------- 1 | Minutes: https://github.com/rust-lang/lang-team/blob/master/minutes/2020-03-26.md#shepherded-item-updates 2 | 3 | Most people are in favor of adding `"C unwind"` 4 | 5 | Niko: 6 | 7 | > We were saying that it seems like at this point it would make sense to make a revised RFC that states 8 | > 9 | > add "C unwind" 10 | > clarifies that all unwinding across a C ABI is UB except the case of: 11 | > * forced unwind where no destructors are on stack 12 | > 13 | > requires an abort guard in the case of a extern "C" fn foo() compiled with panic=unwind (to avoid UB) 14 | > 15 | > declares that unwinding across a "C unwind" ABI is currently 16 | > * permitted for exceptions that arose from a Rust panic 17 | > * "not yet specified" for foreign exceptions (that will be follow-up work) 18 | -------------------------------------------------------------------------------- /langteam-sync/report-2019-10-17.md: -------------------------------------------------------------------------------- 1 | # Summary of activity - Oct 17, 2019 2 | 3 | ## Project repo activity 4 | 5 | ### Terminology 6 | 7 | [Spec terminology](https://github.com/nikomatsakis/project-ffi-unwind/blob/master/spec-terminology.md) 8 | 9 | * UB - existing definition in Unsafe Code Guidelines glossary 10 | * Unspecified behavior - similar to C++ usage: behavior varies, but "not 11 | completely undefined" - intent is "non-adversarial" compilation 12 | * TBD - WG-specific; behavior that the WG plans to specify 13 | * When TBD behavior hits Stable, it is considered Unspecified. 14 | 15 | ### Roadmaps 16 | 17 | [Technical details of what we want to 18 | specify](https://github.com/nikomatsakis/project-ffi-unwind/tree/master/roadmap) 19 | 20 | ### Project-planning 21 | 22 | [Non-technical "meta" 23 | planning](https://github.com/nikomatsakis/project-ffi-unwind/blob/master/project-planning.md) 24 | 25 | ### "0th RFC" 26 | 27 | - [gnzlbg](https://github.com/nikomatsakis/project-ffi-unwind/pull/9) 28 | - [niko](https://github.com/nikomatsakis/project-ffi-unwind/pull/11) 29 | 30 | Intent: 31 | - technical: just the new ABI string, with "unspecified" (TBD) behavior 32 | - meta: announce group, share links to Zulip & repo 33 | 34 | ### "resolved concerns" 35 | 36 | [README](https://github.com/nikomatsakis/project-ffi-unwind/blob/master/resolved-concerns/README.md) 37 | 38 | First instance: summarized a Zulip conversation w/ gnzlbg 39 | 40 | ## Misc 41 | 42 | ### Adopting "unspecified behavior" definition in Rust project 43 | 44 | Already have UB, and "TBD" does not apply outside of WGs (though other WGs may find it useful) 45 | 46 | Will submit separate RFC for this 47 | 48 | ### Adam's status 49 | 50 | - low availability for next ~month 51 | - trying to keep as up-to-date as possible with filling in details about Lucet use cases on Zulip 52 | - read about half of the PRs, very few of the PR comments 53 | - Lucet: on nightly, using `unwind(allowed)`, so no urgent need for new lang feature 54 | 55 | ### General plan: proceed as ["series of RFCs"][series-RFCS] 56 | 57 | Each RFC has had "quite some vetting"; to participate in early 58 | discussion/design, participate in project WG 59 | 60 | [series-RFCS]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/meeting.20time/near/178405449 61 | 62 | ### Back-and-forth from convos to GitHub PRs/issues 63 | 64 | Seems fairly productive, but a bit confusing -- no formal mechanism other than 65 | manual permalinks 66 | 67 | Several comments of "not sure what this is responding to" 68 | 69 | AFAIK (Kyle here) we agree that important points are captured in GitHub, either 70 | via PR discussions or actual repo content. This, together with keeping the PR 71 | discussions more minimal, should let interested parties track progress without 72 | requiring deep-dives on Zulip conversations or long GitHub threads. We haven't 73 | tested this, per se, but Adam has been very busy this week and hasn't been able 74 | to catch up on all the Zulip conversations, so a good first test will be 75 | whether he feels "caught up" when he reviews "RFC 0" (has asked to be added as 76 | reviewer when it's closer to finalization) 77 | 78 | ### "feeling lost" 79 | 80 | Niko: "I'd mostly like to have a set of steps we are following" - 81 | [Zulip](https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/meeting.20time/near/177807832) 82 | 83 | Not sure if this is resolved by the "project-planning" document or not 84 | -------------------------------------------------------------------------------- /periodic-summaries/November-1stHalf.md: -------------------------------------------------------------------------------- 1 | # Zulip topic "collections", 1st half of Nov. 2 | 3 | ## Reconsidering `"C unwind"` 4 | 5 | This is by far the most important and most discussed topic from this period. 6 | 7 | * [First HackMD summary & conversation](https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/.22C.20unwind.22.20vs.20.22C.20noexcept.22/near/179273421) 8 | * [More followup to first HackMD](https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/ffi-unwind.20.E2.80.93.20meaning.20of.20C/near/180220435) 9 | * [`extern "C"` introduces an "accidental" `noexcept` feature](https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/noexcept-like.20feature/near/178839028) 10 | * [On whether FFI should ever change semantic behavior of callee](https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/FFI.20.22change.20behavior.20or.20protect.20the.20caller.22/near/180783324) 11 | 12 | ## Recommending immediate action to lang team on the existing soundness bug w/ `noexcept` in non-`unsafe` code 13 | 14 | [three approaches; link to GitHub PR; discussion](https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/.22fixing.20the.20soundness.20bug.22/near/180788544) 15 | 16 | ## Catching foreign exceptions 17 | 18 | [`catch_unwind` won't allow valid *access* to exception objects from user code](https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/Catching.20foreign.20exceptions/near/179248202) 19 | 20 | ## Measurements of binary-size impact 21 | 22 | * [Some data](https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/measuring.20code.20size/near/180209406) 23 | * [A branch to measure impact of safeguards for `panic = abort`](https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/implementing.20.22abort.20on.20FFI.20call.22/near/180636576) 24 | -------------------------------------------------------------------------------- /periodic-summaries/October-2ndHalf.md: -------------------------------------------------------------------------------- 1 | ## `catch_unwind` and foreign exceptions 2 | 3 | [Zulip][catch-foreign-exceptions] 4 | 5 | (TODO: add this to "resolved concerns") 6 | 7 | gnzlbg: "we kind of need to be able to do this"; link to [Rust bug about 8 | `ForceUnwind`][rustc-65441] 9 | 10 | acfoltzer & Amanieu: should never catch foreign exceptions 11 | 12 | acfoltzer: "major red flags...constrain Rust panic mechanism" 13 | 14 | JoshT: not "the original problem the working group set out to solve" 15 | 16 | Conclusion: leave undefined (_not_ TBD) for now; also, gnzlbg's issue was 17 | addressed by a PR by Amanieu (see below) 18 | 19 | ## Unspecified behavior compile error 20 | 21 | [Zulip][unspec-behavior-compile] 22 | 23 | Code that does not compile is by definition not exhibiting "unspecified 24 | behavior." 25 | 26 | However, code may have unspecified on some platforms but fail to compile on 27 | others (though it may compile in the future). 28 | 29 | ## Amanieu PR for foreign exceptions 30 | 31 | Amanieu wrote [a PR][foreign-exceptions-pr] to ensure that foreign exceptions 32 | can propagate through Rust frames: 33 | 34 | > * Drop code will be executed as the exception unwinds through the stack, as 35 | > with a Rust panic. 36 | > * `catch_unwind` will not catch the exception, instead the exception will 37 | > silently continue unwinding past it. 38 | 39 | ## "native" vs "foreign" 40 | 41 | [Zulip][native-frame-def] 42 | 43 | Kyle S: only use "native" to refer to exception mechanism; use "foreign" when 44 | referring to non-Rust frames 45 | 46 | ## letting `extern "C"` unwind 47 | 48 | [Zulip][let-extern-c-unwind] 49 | 50 | [Shared HackMD write-up][unwind-by-default-hackmd] 51 | 52 | Amanieu provided a detailed analysis of the behavior of cross-language 53 | unwinding in various circumstances. 54 | 55 | We believe that there is no undefined behavior at the LLVM level even _without_ 56 | `-fexceptions`, though we need to confirm w/ LLVM owners that it is not 57 | automatically UB to unwind through C code compiled without `-fexceptions` 58 | 59 | [Concern about `pthread_exit`][pthread_exit] 60 | 61 | [Centril objects][centril-unwind-objectsions]: 62 | * possible breaking changes 63 | * de-facto constraint on `panic` implementation 64 | * all implementation-defined behavior is, in practice, "defined for the whole 65 | language" since we only have one implementation 66 | * Need relevant LLVM guarantees to be stated in LangRef 67 | 68 | Josh T: unwind by default sounds reasonable 69 | 70 | Kyle: Let's proceed w/ `extern "C unwind"`; nothing prevents us from landing 71 | `extern "C unwind"` and then later making `extern "C"` behave the same way, 72 | unless we make the abort-on-unwind behavior "well defined" (and therefore 73 | stable forever, at least until the next edition) 74 | 75 | ## Noexcept-like feature 76 | 77 | [Zulip][noexcept-feature] 78 | 79 | gnzlbg: attribute is a bigger lang change than new ABI would be 80 | 81 | Amanieu: not sure `noexcept` would be as useful in Rust as in C++ 82 | 83 | [Kyle][noexcept-not-now]: I think `noexcept` probably falls under project group 84 | scope, but it is not a high priority and doesn't block other work 85 | 86 | ## Participant status updates 87 | 88 | Is this section useful? 89 | 90 | * acfoltzer: really busy through mid-November 91 | * Alex Crichton: not caught up on threads but open to being pinged for 92 | questions 93 | * Niko: active in conversations, etc but not entirely caught up on channels 94 | * Amanieu: didn't know about project group before, but participating now 95 | 96 | ## Kyle propsoed action items 97 | 98 | [Zulip][kyle-action-items] 99 | 100 | "more or less in parallel" 101 | 102 | * RFC for announcing project group (*no* lang changes proposed) 103 | * continue to design `extern "C unwind"` 104 | * stabilize the abort-on-unwind-through-`extern "C"` logic 105 | * possibly, design some explicit opt-in syntax for the abort-on-unwind feature 106 | 107 | ## Misc 108 | 109 | * [RustConf talk on interacting w/ C++ exceptions][katharina-talk] 110 | * [Caution about scope creep based on Itanium ABI][scope-itanium] 111 | 112 | [rustc-65441]: https://github.com/rust-lang/rust/issues/65441 113 | [catch-foreign-exceptions]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/meeting.20time/near/178406886 114 | [katharina-talk]: https://youtu.be/kdxAkCpKkbY 115 | [unspec-behavior-compile]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/meeting.20time/near/178414227 116 | [scope-itanium]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/WG.20scope/near/178415057 117 | [foreign-exceptions-pr]: https://github.com/rust-lang/rust/pull/65646 118 | [native-frame-def]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/Native.20frame.20definition/near/178713063 119 | [let-extern-c-unwind]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/Allow.20unwinding.20from.20extern.20.22C.22.20by.20default/near/178725735 120 | [pthread_exit]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/Allow.20unwinding.20from.20extern.20.22C.22.20by.20default/near/178758001 121 | [unwind-by-default-hackmd]: https://hackmd.io/ymsEL6OpR6OSMoFr1As1rw 122 | [noexcept-feature]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/noexcept-like.20feature/near/178839070 123 | [noexcept-not-now]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/noexcept-like.20feature/near/178880684 124 | [centril-unwind-objectsions]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/Allow.20unwinding.20from.20extern.20.22C.22.20by.20default/near/178900804 125 | [kyle-action-items]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/lang.20team.20sync/near/179002351 126 | -------------------------------------------------------------------------------- /periodic-summaries/README.md: -------------------------------------------------------------------------------- 1 | This directory is for summaries of project discussions (primarily in Zulip) 2 | over a certain period. 3 | 4 | The intent is to provide a quick way for anyone to catch up on recent project 5 | discussions and activity. The summaries may also be useful when working on 6 | other documents: 7 | 8 | * drafting lang team "sync" reports 9 | * Updating a "state of the project" document (currently does not exist) 10 | -------------------------------------------------------------------------------- /planning/project-planning.md: -------------------------------------------------------------------------------- 1 | ## Current action items 2 | 3 | 1. [x] Make a [draft charter](charter.md) for the project 4 | 1. [x] Create a Zulip channel 5 | 1. [x] Close RFCs #2753 and #2699 and point to this repository and the Zulip channel 6 | 1. [ ] Create design documents and drafts in this repo 7 | * create a "sketch" document to start, fill it in over time 8 | 1. [ ] Create issues for the outstanding concerns 9 | * lock issues that are not the current focus topic? 10 | * unsafe code guidelines just used "focus" labels, maybe do that 11 | * or maybe use time-based issues (woah!) where the sync meeting marks a 12 | "pause" to collect, incorporate new findings, and set new topics 13 | 1. [ ] Finish `extern "C"` alternatives write-up and add it to project repo 14 | 1. [ ] Seek input from other language designers & communities 15 | 1. [x] Zig - designer should have time after Thanksgiving 16 | 1. [ ] [Others](zulip-other-languages) 17 | 18 | [zulip-other-languages]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/Input.20from.20other.20language.20designers.20.26.20communities/near/180385809 19 | 20 | ## Goals 21 | 22 | * Initial: 23 | * refine charter to create a roadmap 24 | * what are the bits of unspecified behavior yet to be fixed 25 | * which use cases are we aiming at and which are non-goals? 26 | * which bits of unspecified behavior must be specified 27 | * seek consensus on `extern "C"` behavior: maintain plan to stabilize 28 | `abort`-on-unwind? 29 | * Subsequent: 30 | * work through those bits of unspecified behavior and work toward a final design 31 | 32 | ## Meta 33 | 34 | * Have a sync meeting every week or maybe 2 weeks 35 | * all are welcome to come, hold on Zulip (?) or Zoom 36 | * discuss the conversations that occured over last period 37 | * try to summarize new points that were raised and add to an existing document that contains the "pros/cons" 38 | * when this document seems to be at a steady state, time for a decision 39 | * make our proposal and bring it to the lang team 40 | * announce result and give some time, then add to FAQ 41 | * prep a summary to bring to lang team and post publicly 42 | * Idea: post summary as a new RFC, then close the old one with a link to 43 | the new one? Keep RFC threads short and time-boxed 44 | * Membership: 45 | * Avoid having a "full membership" concept, as Niko is not sure that it's 46 | needed here 47 | * But if we were going to have members, it should be reserved for folks who 48 | have "productively contributed" to several design discussions 49 | -------------------------------------------------------------------------------- /planning/roadmap/README.md: -------------------------------------------------------------------------------- 1 | This directory contains details about particular items on 2 | the roadmap. 3 | -------------------------------------------------------------------------------- /planning/roadmap/c-unwind-abi.md: -------------------------------------------------------------------------------- 1 | # "C unwind" ABI 2 | 3 | ## Summary 4 | 5 | ### The problem 6 | 7 | Functions that use the plain `"C"` ABI are **not permitted to unwind**. 8 | Doing so is [Undefined Behavior], which means that the compiler is free 9 | to assume it cannot happen. The results are therefore unpredictable. 10 | It is always a bug. 11 | 12 | We would like the behavior to no longer be undefined. We are considering 13 | [different options](extern-c-behavior) for this. 14 | 15 | [extern-c-behavior]: https://hackmd.io/JIsPlpIPR2yTC051m4Mliw 16 | [//]: # (TODO: move HackMD doc into this repo.) 17 | 18 | The remainder of this document examines the `"C unwind"` proposal, which would 19 | be an explicit way of opting-in to unwinding across FFI boundaries. 20 | 21 | ### The proposed ABI 22 | 23 | We are adding therefore a new ABI `"C unwind"`. This ABI can be used 24 | to indicate functions that follow the C ABI and which **may** unwind. 25 | The precise unwinding mechanism that such functions use will depend on 26 | the target definition. In general, though, it is meant to match the 27 | "native" unwinding mechanism of the target: typically whatever C++ 28 | compilers use. 29 | 30 | **Warning:** We are still in the process of fully specifying when and 31 | how "unwinding interop" works between native functions and Rust. This 32 | roadmap item alone **only** adds the ABI string to Rust. All details 33 | of how that ABI strings are implemented for various targets are still 34 | considered [To Be Defined]. As a result, programs that rely on those 35 | details are not truly stable; you may find that the details change as 36 | Rust evolves. Eventually, though, we do intend to define many (but not 37 | all) aspects of how Rust panics and native unwinding interoperate. 38 | Moreover, we guarantee that unwinding will **not** result in 39 | [Undefined Behavior] and in particular not [LLVM-UB]. 40 | 41 | ## The goal 42 | 43 | In practice, there are a number of crates that rely on unwinding 44 | across a "C" ABI today -- even though that is [Undefined 45 | Behavior]. Creating the "C unwind" ABI means that those crates can 46 | migrate to this ABI and better express their intent. It does not (in 47 | and of itself) make the behavior of those crates defined -- they are 48 | still relying on [To Be Defined] details. But it *does* mean that they 49 | will no longer trigger [Undefined Behavior]. 50 | 51 | ## Panic = abort 52 | 53 | In order to safely call functions that may unwind, a Rust function must have 54 | a landing pad. Typically, `panic = abort` prevents landing pads from being 55 | generated, which would make the behavior of `"C unwind"` 56 | [LLVM-undefined][LLVM-UB]. To make this behavior well defined, we would like to 57 | require landing-pad generation for any function calling a `"C unwind"` 58 | function, even when compiling with `panic = abort`. These landing pads would of 59 | course `abort` the application rather than propagate the unwind. 60 | 61 | [Undefined Behavior]: /spec-terminology.md#UB 62 | [LLVM-UB]: /spec-terminology.md#LLVM-UB 63 | [To Be Defined]: /spec-terminology.md#TBD 64 | -------------------------------------------------------------------------------- /planning/roadmap/propagate-native-exception-through-Rust-frame.md: -------------------------------------------------------------------------------- 1 | # Propagate native exception through Rust frame 2 | 3 | ## Summary 4 | 5 | These roadmap items refer to when a "native exception" is raised in a 6 | native function defined with the `"C unwind"` ABI, as might happen if 7 | we invoked a C++ function that throws an exception. 8 | 9 | (TODO: Investigate details of how this would be declared in any case.) 10 | 11 | In this case, the stack would look like this: 12 | 13 | ``` 14 | [Native frames, thread boundary, or app entry point] 15 | | 16 | ... 17 | | 18 | [Rust frame] 19 | | ^ 20 | | (calls) | (unwinding 21 | v | goes this 22 | [native function] | way) 23 | | | 24 | +--- native function throws -+ 25 | ``` 26 | 27 | ## Current status 28 | 29 | Currently, all details of how a "native exception" interacts with Rust 30 | frames are [To Be Defined] behavior. The goal of these roadmap items 31 | is to define some aspects of this behavior. Note that these 32 | definitions will be on a "per target" basis, as the details vary with 33 | the ABI. 34 | 35 | ## Possible properties of the Rust frames 36 | 37 | Even within a given target, it is worth separating out some different 38 | cases: 39 | 40 | * The Rust frame has **no in-scope destructors**. 41 | * We do not currently have a language or tooling mechanism for guaranteeing 42 | that Rust function is guaranteed to have no destructors. There is an 43 | [existing suggestion][centril-effects] for a language feature to 44 | provide such a guarantee. This may also be useful for indicating cases 45 | where it is legal to `longjmp` over a frame, though that is not one of 46 | our direct goals. See [the FAQ][FAQ-longjmp]. 47 | * The Rust frame **has destructors** it would like to execute. 48 | * We need to define how they interact with a native exception. 49 | 50 | [centril-effects]: https://github.com/Centril/rfc-effects/issues/11 51 | [FAQ-longjmp]: faq.md#how-does-cross-language-unwinding-differ-from-cross-language-setjmplongjmp 52 | 53 | ## Unwind-termination and related concerns 54 | 55 | There are several possibilities for the top section of the diagram, above the 56 | Rust frames: 57 | 58 | * Native frames - the native unwind re-enters native frames 59 | * The native code runtime should be able to treat this as a normal exception 60 | at this point, as though Rust had never been involved. 61 | * App entry point - the unwind is not caught and does not cross a thread 62 | boundary 63 | * Thread boundary - the exception propagates all the way to a Rust frame that 64 | was invoked from another thread 65 | 66 | The first two possibilities (re-entering native frames or reaching the 67 | app entry point) are [To Be Defined] behavior. Their behavior should 68 | be a natural consequence of how the `"C unwind"` ABI is defined. 69 | 70 | The final possibility, wherein a native unwind propagates through Rust 71 | frames all the way to a thread boundary, is [Unspecified Behavior], 72 | meaning that we **do not** intend to define it. 73 | 74 | [Undefined Behavior]: /spec-terminology.md#UB 75 | [LLVM-UB]: /spec-terminology.md#LLVM-UB 76 | [To Be Defined]: /spec-terminology.md#TBD 77 | [Undefined Behavior]: /spec-terminology.md#unspecified 78 | -------------------------------------------------------------------------------- /planning/roadmap/propagate-rust-panic-through-native-frame.md: -------------------------------------------------------------------------------- 1 | # Propagate Rust panic through native frames 2 | 3 | ## Summary 4 | 5 | These roadmap items refer to when a panic is raised in a Rust function 6 | that is defined with `"C unwind"` ABI, as would happen if C or C++ 7 | code invoked the following function: 8 | 9 | ```rust 10 | extern "C unwind" fn example() { 11 | panic!("Uh oh"); 12 | } 13 | ``` 14 | 15 | In this case, the stack would look like one of the two scenarios: 16 | 17 | ``` 18 | [Rust frames, thread boundary, or app entry point] 19 | | 20 | ... 21 | | 22 | [Native frame] 23 | | ^ 24 | | (calls) | (unwinding 25 | v | goes this 26 | [Rust function `example`] | way) 27 | | | 28 | +--- rust function panics --+ 29 | ``` 30 | 31 | ## Possible properties of the native frames 32 | 33 | * The native frame has **no destructors**. An example of this might be a C function. 34 | * The precise meaning of "no destructors" is defined in terms of the native ABI. 35 | * We need to define how the Rust panic propagates across such frames. 36 | * The native frame **has destructors** it would like to execute. These could come from C++ 37 | RAII or similar mechanisms. 38 | * We need to define how the Rust panic is defined and which destructors would execute. 39 | * The native frame has **no destructors or any affordances for 40 | unwinding**. An example of this might be a C function compiled with 41 | `-fno-exceptions` or `-fno-unwind-tables`. 42 | * The precise meaning of "affordances for unwinding" is defined in terms of the native ABI. 43 | 44 | Currently, having a Rust panic "unwind" a native frame is **undefined 45 | behavior** in all of the above cases. However, we plan to specify the 46 | first case (no destructors) before we think about the more complex 47 | cases (contains destructors, no affordances). We will also have to 48 | specify this on a per-target basis, as the details will vary depending 49 | on what exception mechanism is in use. 50 | 51 | Another consideration is whether the the native frames include a `catch` block. 52 | There are several possibilities here as well: 53 | 54 | * "catch native" -- e.g. a C++ block catching a specific subset of C++ exceptions 55 | * "catch all" -- e.g. a C++ catch without an exception argument 56 | * "catch and release" -- e.g. a C++ "catch all" that re-`throw`s the exception 57 | 58 | So far we do **not** have plans to specify these cases. 59 | 60 | ## Unwind-termination and related concerns 61 | 62 | There are several possibilities for the top section of the diagram, above the 63 | native frames: 64 | 65 | * Rust frames - the `panic` re-enters Rust frames 66 | * At this point, the `panic` will be no different from a normal `panic`, 67 | unless and until it re-enters native frames. 68 | * The `panic` may caught as usual, either with `catch_panic` or by crossing a 69 | thread boundary. 70 | * Thread boundary - the exception propagates all the way to a native frame that 71 | was invoked from another thread 72 | * App entry point - the unwind is not caught and does not cross a thread 73 | boundary. 74 | * This should cause the application to terminate. 75 | 76 | Again, these are all currenntly undefined. 77 | 78 | We **do** expect to make the first and last possibilities (re-entering Rust 79 | frames or reaching the app entry point) well-defined; their behavior should be 80 | a natural consequence of how the `"C unwind"` ABI is defined. 81 | 82 | We **do not** currently have plans to define the middle case, wherein a `panic` 83 | propagates through native frames all the way to a thread boundary. 84 | 85 | -------------------------------------------------------------------------------- /planning/scope-by-platform-table.md: -------------------------------------------------------------------------------- 1 | # Scope of project, by feature and platform 2 | 3 | ## Key 4 | 5 | * :white_check_mark: -- available and well-defined on stable 6 | * :yellow_heart: -- available on nightly 7 | * :revolving_hearts: -- open RFC 8 | * :speech_balloon: -- under active discussion 9 | * :clock1: -- not yet under active discussion 10 | * :no_entry_sign: -- out of scope for this group, no current plans to implement or specify 11 | 12 | ## Table 13 | 14 | 15 | | Thing | linux | mac | msvc | other targets | 16 | | --- | --- | --- | --- | --- | 17 | | ["C unwind" ABI] | :speech_balloon: | :speech_balloon: | :speech_balloon: | :speech_balloon: | 18 | | [propagate Rust panic through native frame], no destructors | :clock1: | :clock1: | :clock1: |:no_entry_sign: | 19 | | [propagate Rust panic through native frame], with destructors | :clock1: | :clock1: | :clock1: |:no_entry_sign: | 20 | | [propagate native exception through Rust frame], no destructors | :clock1: | :clock1: | :clock1: |:no_entry_sign: | 21 | | [propagate native exception through Rust frame], with destructors | :clock1: | :clock1: | :clock1: |:no_entry_sign: | 22 | | catch Rust panic within native code | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: | 23 | | catch native exception within Rust code | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: | :no_entry_sign: | 24 | 25 | ["C unwind" ABI]: planning/roadmap/c-unwind-abi.md 26 | [propagate Rust panic through native frame]: planning/roadmap/propagate-rust-panic-through-native-frame.md 27 | [propagate native exception through Rust frame]: planning/roadmap/propagate-rust-panic-through-native-frame.md 28 | -------------------------------------------------------------------------------- /posts/gh-comments/c-unwind-stabilization-report.md: -------------------------------------------------------------------------------- 1 | # Request for Stabilization 2 | 3 | This is a request for *partial* stabilization of the `c_unwind` feature, 4 | [RFC-2945][rfc-text]. The newly-introduced ABIs will be stabilized, but the 5 | changes in behavior to existing ABIs (such as `extern "C"`) will remain behind 6 | the `c_unwind` feature gate. 7 | 8 | ## Summary 9 | 10 | This stabilization enables ABI strings ending in `-unwind`; specifically: 11 | 12 | - `"C-unwind"` 13 | - `"cdecl-unwind"` 14 | - `"stdcall-unwind"` 15 | - `"fastcall-unwind"` 16 | - `"vectorcall-unwind"` 17 | - `"thiscall-unwind"` 18 | - `"aapcs-unwind"` 19 | - `"win64-unwind"` 20 | - `"sysv64-unwind"` 21 | - `"system-unwind"` 22 | 23 | The behavior for unforced unwinding (the typical case) is specified in [this 24 | table from the RFC][rfc-table], and has not been changed since the RFC was 25 | accepted. To summarize: 26 | 27 | Each ABI is mostly equivalent to the same ABI without `-unwind`, except that 28 | the behavior is defined to be safe when an unwinding operation (`panic` or C++ 29 | style exception) crosses the ABI boundary. For `panic=unwind`, this is a valid 30 | way to let exceptions from one language unwind the stack in another language 31 | without terminating the process (as long as the exception is caught in the same 32 | language from which it originated); for `panic=abort`, this will typically 33 | abort the process immediately. 34 | 35 | For initial stabilization, *no change* will be made to the existing ABIs. The 36 | existing ABIs have soundness holes, though, which will be fixed when the 37 | behavior described in the RFC is stabilized. Thus, we would like to stabilize 38 | the remainder of the `c_unwind` feature soon. We are splitting the 39 | stabilization into separate releases to give users time to adopt the new ABIs. 40 | 41 | ## Examples 42 | 43 | ### Rust `panic` with `"C-unwind"` 44 | 45 | ```rust 46 | #[no_mangle] 47 | extern "C-unwind" fn example() { 48 | panic!("Uh oh"); 49 | } 50 | ``` 51 | 52 | This function is now permitted to unwind C++ stack frames. 53 | 54 | ``` 55 | [Rust function with `catch_unwind`, which stops the unwinding] 56 | | 57 | ... 58 | | 59 | [C++ frames] 60 | | ^ 61 | | (calls) | (unwinding 62 | v | goes this 63 | [Rust function `example`] | way) 64 | | | 65 | +--- rust function panics --+ 66 | ``` 67 | 68 | If the C++ frames have objects, their destructors will be called. 69 | 70 | ### C++ `throw` with `"C-unwind"` 71 | 72 | ```rust 73 | #[link(...)] 74 | extern "C-unwind" { 75 | // A C++ function that may throw an exception 76 | fn may_throw(); 77 | } 78 | 79 | #[no_mangle] 80 | extern "C-unwind" fn rust_passthrough() { 81 | let b = Box::new(5); 82 | unsafe { may_throw(); } 83 | println!("{:?}", &b); 84 | } 85 | ``` 86 | 87 | A C++ function with a `try` block may invoke `rust_passthrough` and `catch` an 88 | exception thrown by `may_throw`. 89 | 90 | ``` 91 | [C++ function with `try` block that invokes `rust_passthrough`] 92 | | 93 | ... 94 | | 95 | [Rust function `rust_passthrough`] 96 | | ^ 97 | | (calls) | (unwinding 98 | v | goes this 99 | [C++ function `may_throw`] | way) 100 | | | 101 | +--- C++ function throws ----+ 102 | ``` 103 | 104 | If `may_throw` does throw an exception, `b` will be dropped. Otherwise, `5` 105 | will be printed. 106 | 107 | ## Changes since the RFC 108 | 109 | The implementation is as specified in the RFC, but there have been a few 110 | considerations not directly addressed in the text of the RFC. 111 | 112 | ### Additional ABI strings 113 | 114 | The RFC, as written, only introduces the `"C-unwind"` ABI string; the others 115 | [listed above](#summary) were introduced later. Their semantics are exactly as 116 | one would expect: they are identical to their non-`unwind` counterparts except 117 | when an unwind reaches their ABI boundary, in which case the behavior is the 118 | same as the RFC specifies for `"C-unwind"`. 119 | 120 | Similarly, the new rules for `extern "C"` regarding unwinding are now applied 121 | to all non-`unwind` ABI strings other than `"Rust"`. 122 | 123 | ### Double unwinding 124 | 125 | [PR #92911][pr-double-unwind] ensured that the following scenarios both safely 126 | abort the program: 127 | 128 | * multiple foreign (e.g. C++) exceptions 129 | * a `panic` occurring while a foreign exception is unwinding the stack, or 130 | vice-versa 131 | 132 | ### Mixing panic modes 133 | 134 | We addressed a [soundness hole][issue-mixed-panic] regarding the behavior when 135 | a foreign exception enters the Rust runtime via a crate compiled with 136 | `panic=unwind`, but escapes into a crate compiled with `panic=abort`. We 137 | decided to [prohibit][pr-fix-mixed-panic] any crate from being linked with the 138 | `panic=abort` runtime if it has both of the following characteristics: 139 | 140 | * It contains a call to an `-unwind` foreign function or function pointer 141 | * It was compiled with `panic=unwind` 142 | 143 | Note: `cargo` will automatically unify all crates to use the same `panic` 144 | runtime, so this prohibition does not apply to projects compiled with `cargo`. 145 | 146 | [PR #97235][pr-fix-mixed-panic] implemented this prohibition. 147 | 148 | ### Unresolved questions 149 | 150 | None of the [unresolved questions][rfc-unresolved] have been resolved. 151 | Specifically, the following are still considered undefined behavior: 152 | 153 | * Letting a foreign exception unwind a Rust frame that calls `catch_unwind` 154 | * Calling `pthread_exit` or `longjmp` in such a way that Rust frames are 155 | deallocated 156 | 157 | ## Tests 158 | 159 | ### Codegen 160 | 161 | There is [a folder of codegen tests][codegen-unwind] for the new ABI strings. 162 | 163 | Additional codegen tests are: 164 | 165 | * [panic=abort][codegen-extra-1] 166 | * [extern-exports][codegen-extra-2] 167 | * [extern-imports][codegen-extra-3] 168 | 169 | ### `"C"` guaranteed to abort on panic 170 | 171 | [This test][abort-on-panic] verifies that an `extern "C"` function that 172 | `panic!`s will always abort if the panic would otherwise "escape". 173 | 174 | ### Full examples 175 | 176 | Full example projects mixing Rust code with non-Rust code are: 177 | 178 | * [a `panic` escaping into C][panic-into-c] 179 | * [a C++ exception escapig into Rust][cpp-throw-into-rust] 180 | * [double unwinding][double-unwind] 181 | 182 | ### Open PR 183 | 184 | [PR #97235][pr-fix-mixed-panic] introduces tests for the [mixed-panic-modes 185 | issue](#mixing-panic-modes). 186 | 187 | ## Documentation 188 | 189 | Here are documentation PRs for 190 | 191 | * [the Reference][reference] 192 | * [the Rustonomicon][nomicon] 193 | 194 | I have not yet suggested any changes for the Book, but it may be appropriate to 195 | add something like this: 196 | 197 | > Usually you want the non-unwind versions of ABI strings, but if you are 198 | > messing around with FFI and unwinding, refer to the Nomicon. 199 | 200 | I don't think Rust by Example needs to be updated. 201 | 202 | 203 | [rfc-text]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md 204 | [rfc-table]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md#abi-boundaries-and-unforced-unwinding 205 | [rfc-unresolved]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md#unresolved-questions 206 | [pr-fix-mixed-panic]: https://github.com/rust-lang/rust/pull/97235/files 207 | [pr-double-unwind]: https://github.com/rust-lang/rust/pull/92911 208 | [issue-mixed-panic]: https://github.com/rust-lang/rust/issues/96926 209 | [codegen-unwind]: https://github.com/rust-lang/rust/tree/master/src/test/codegen/unwind-abis 210 | [codegen-extra-1]: https://github.com/rust-lang/rust/blob/master/src/test/codegen/unwind-and-panic-abort.rs 211 | [codegen-extra-2]: https://github.com/rust-lang/rust/blob/master/src/test/codegen/unwind-extern-exports.rs 212 | [codegen-extra-3]: https://github.com/rust-lang/rust/blob/master/src/test/codegen/unwind-extern-imports.rs 213 | [panic-into-c]: https://github.com/rust-lang/rust/tree/master/src/test/run-make-fulldeps/c-unwind-abi-catch-lib-panic 214 | [cpp-throw-into-rust]: https://github.com/rust-lang/rust/tree/master/src/test/run-make-fulldeps/foreign-exceptions 215 | [double-unwind]: https://github.com/rust-lang/rust/tree/master/src/test/run-make-fulldeps/foreign-double-unwind 216 | [abort-on-panic]: https://github.com/rust-lang/rust/blob/master/src/test/ui/panics/abort-on-panic.rs 217 | [nomicon]: https://github.com/rust-lang/nomicon/pull/365 218 | [reference]: https://github.com/rust-lang/reference/pull/1226 219 | -------------------------------------------------------------------------------- /posts/gh-comments/extern-c-change-stabilization.md: -------------------------------------------------------------------------------- 1 | # Request for Stabilization 2 | 3 | This is a request for *full* stabilization of the `c_unwind` feature, [RFC-2945][rfc-text]. The behavior of non-`"Rust"`, non-`unwind` ABIs (such as `extern "C"`) will be modified to close soundness holes caused by permitting stack-unwinding to cross FFI boundaries that do not support unwinding. 4 | 5 | ## Summary 6 | 7 | When using `panic=unwind`, if a Rust function marked `extern "C"` panics (and that panic is not caught), the runtime will now abort. 8 | 9 | Previously, the runtime would simply attempt to unwind the caller's stack, but the behavior when doing so was undefined, because `extern "C"` functions are optimized with the assumption that they cannot unwind (i.e. in `rustc`, they are given the LLVM `nounwind` annotation). 10 | 11 | This affects existing programs. If a program relies on a Rust panic "escaping" from `extern "C"`: 12 | * It is currently unsound. 13 | * Once this feature is stabilized, the program will crash when this occurs, whereas previously it may have appeared to work as expected. 14 | * Replacing `extern "x"` with `extern "x-unwind"` will produce the intended behavior without the unsoundness. 15 | 16 | The behavior of function calls using `extern "C"` is unchanged; thus, it is still undefined behavior to call a C++ function that throws an exception using the `extern "C"` ABI, even when compiling with `panic=unwind`. 17 | 18 | ## Changes since the RFC 19 | 20 | None. 21 | 22 | ### Unresolved questions 23 | 24 | This feature does not affect the behavior of `pthread_exit` or `longjmp`, so stabilization will neither break existing code using these features nor provide any soundness guarantee. 25 | 26 | ## Tests 27 | 28 | ### `"C"` guaranteed to abort on panic 29 | 30 | [This test][abort-on-panic] verifies that an `extern "C"` function that `panic!`s will always abort if the panic would otherwise "escape". 31 | 32 | ## Documentation 33 | 34 | The documentation PRs for this feature did not distinguish between the initial partially-stabilized behavior and the fully-stabilized behavior, so they already include the modifications to non-`unwind` ABIs. 35 | 36 | * [The Rustonomicon PR][nomicon] has already been merged. 37 | * [The Reference PR][reference] is still awaiting approval. 38 | 39 | I've also [opened an issue][book-issue] to discuss adding a brief note about this feature in the Book. 40 | 41 | 42 | [rfc-text]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md 43 | [abort-on-panic]: https://github.com/rust-lang/rust/blob/master/tests/ui/panics/abort-on-panic.rs 44 | [nomicon]: https://github.com/rust-lang/nomicon/pull/365 45 | [reference]: https://github.com/rust-lang/reference/pull/1226 46 | [book-issue]: https://github.com/rust-lang/book/issues/3729 47 | -------------------------------------------------------------------------------- /posts/inside-rust/01-announcement.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Announcing the first FFI-unwind project design meeting 4 | 5 | -- Kyle Strand, Niko Matsakis, and Amanieu d'Antras on behalf of the FFI-unwind project group -- 6 | 7 | The FFI-unwind project group, announced in [this RFC][rfc-announcement], is 8 | working to extend the language to support unwinding that crosses FFI 9 | boundaries. 10 | 11 | We have reached our first technical decision point, on a question we have been 12 | discussing internally for quite a while. This blog post lays out the arguments 13 | on each side of the issue and invites the Rust community to join us at an 14 | upcoming meeting to help finalize our decision, which will be formalized and 15 | published as our first language-change RFC. This RFC will propose an "MVP" 16 | specification for well-defined cross-language unwinding. 17 | 18 | The meeting will be on [TODO - TBD](meeting-link). 19 | 20 | ## Background: what is unwinding? 21 | 22 | Exceptions are a familiar control flow mechanism in many programming languages. 23 | They are particularly commonplace in managed languages such as Java, but they 24 | are also part of the C++ language, which introduced them to the world of 25 | unmanaged systems programming. 26 | 27 | When an exception is thrown, the runtime _unwinds_ the stack, essentially 28 | traversing it backwards and calling clean-up or error-recovery code such as 29 | destructors or `catch` blocks. 30 | 31 | Compilers may implement their own unwinding mechanisms, but in native code such 32 | as Rust binaries, the mechanism is more commonly provided by the platform ABI. 33 | 34 | It is well known that Rust does not have exceptions as such. But Rust _does_ 35 | support unwinding! There are two scenarios that will cause unwinding to occur: 36 | 37 | * By default, Rust's `panic!()` unwinds the stack. 38 | * Using FFI, Rust can call functions in other languages (such as C++) that can 39 | unwind the stack. 40 | * There are some special cases where C libraries can actually cause 41 | unwinding. For instance, on Microsoft platforms, `longjmp` is implemented 42 | as "forced unwinding" (see below) 43 | 44 | Currently, when foreign (non-Rust) code invokes Rust code, the behavior of a 45 | `panic!()` unwind that "escapes" from Rust is explicitly undefined. Similarly, 46 | when Rust calls a foreign function that unwinds, the behavior once the unwind 47 | operation encounters Rust frames is undefined. The primary reason for this is 48 | to ensure that Rust implementations may use their own unwinding mechanism, 49 | which may not be compatible with the platform-provided "native" unwinding 50 | mechanism. Currently, however, `rustc` uses the native mechanism, and there are 51 | no plans to change this. 52 | 53 | ### Forced unwinding 54 | 55 | Platform ABIs can define a special kind of unwinding called "forced unwinding." 56 | This type of unwinding works regardless of whether the code being unwound 57 | supports unwinding or not. However, destructors may not be executed if the 58 | frames being unwound do not have unwinding support. 59 | 60 | There are two common examples of forced unwinding: 61 | 62 | * On Windows platforms, `longjmp` is implemented as a forced unwind. 63 | * On glibc Linux, `pthread_exit` and `pthread_cancel` are implemented as a forced unwind. 64 | * In fact, `pthread_cancel` can cause all manner of C functions to unwind, 65 | including common functions like `read` and `write`. (For a complete list, 66 | search for "cancellation points" in the [pthreads man page](man-pthreads).) 67 | 68 | ## Requirements for any cross-language unwinding specification 69 | 70 | * Unwinding between Rust functions (and in particular unwinding of Rust panics) 71 | may not necessarily use the system unwinding mechanism 72 | * In practice, we do use the system mechanism today, but we would like to 73 | reserve the freedom to change this. 74 | * If you enable `-Cpanic=abort`, we are able to optimize the size of binaries 75 | to remove most code related to unwinding. 76 | * Even with `-Cpanic=unwind` it should be possible to optimize away code when 77 | unwinding is known to never occur. 78 | * In practice, most "C" functions are never expected to unwind (because they 79 | are written in C, for example, and not in C++). 80 | * However, because unwinding is now part of most system ABIs, even C 81 | functions can unwind — most notably cancellation points triggered 82 | by `pthread_cancel`. 83 | * Changing the behavior from `-Cpanic=unwind` to `-Cpanic=abort` should not 84 | cause Undefined Behavior. 85 | * However, this may not be tenable, or at least not without making binaries 86 | much larger. See the discussion below for more details. 87 | * It may, of course, cause programs to abort that used to execute 88 | successfully. This could occur if a panic would've been caught and 89 | recovered. 90 | * We cannot change the ABI (the `"C"` in `extern "C"`) of functions in the libc 91 | crate, because this would be a breaking change: function pointers of different 92 | ABIs have different types. 93 | * This is relevant for the libc functions which may perform forced unwinding 94 | when `pthread_cancel` is called. 95 | 96 | ## The primary question: should the `"C"` ABI permit unwinding? 97 | 98 | The core question that we would like to decide is whether the `"C"` ABI, as 99 | defined by Rust, should permit unwinding. 100 | 101 | This is not a question we expected to be debating. We've long declared that 102 | unwinding through Rust's `"C"` ABI is undefined behavior. In part, this is 103 | because nobody had spent the time to figure out what the correct behavior would 104 | be, or how to implement it, although (as we'll see shortly) there are other 105 | good reasons for this choice. 106 | 107 | In any case, in PR #65646, @Amanieu proposed that we could, in fact, simply 108 | define the behavior of unwinding across `"C"` boundaries. In discussing this, 109 | discovered that the question of whether the `"C"` ABI should permit unwinding was 110 | less clear-cut than we had assumed. 111 | 112 | If the `"C"` ABI does not permit unwinding, a new ABI, called `"C unwind"`, 113 | will be introduced specifically to support unwinding. 114 | 115 | ## Three specific proposals 116 | 117 | The project group has narrowed the design space down to three specific 118 | proposals. Two of these introduce the new `"C unwind"` ABI, and one does not. 119 | 120 | Each proposal specifies the behavior of each type of unwind (Rust `panic!`, 121 | foreign (e.g. C++), and forced (e.g. `pthread_exit`)) when it encounters an 122 | ABI boundary under either the `panic=unwind` or `panic=abort` compile-mode. 123 | 124 | Note that currently, `catch_unwind` does not intercept foreign unwinding 125 | (forced or unforced), and our initial RFCs will not change that. We may decide 126 | at a later date to define a way for Rust code to intercept foreign exceptions. 127 | 128 | Throughout, the unwind generated by `panic!` will be referred to as 129 | `panic`-unwind. 130 | 131 | ### Proposal 1: Introduce `"C unwind"`, minimal specification 132 | 133 | * `"C"` ABI boundary, `panic=` 134 | * `panic`-unwind: program aborts 135 | * forced unwinding, no destructors: unwind behaves normally 136 | * other foreign unwinding: undefined behavior 137 | * `"C unwind"` ABI boundary 138 | * With `panic=unwind`: all types of unwinding behave normally 139 | * With `panic=abort`: all types of unwinding abort the program 140 | 141 | This proposal provides 2 ABIs, each suited for different purposes: you would 142 | generally use `extern "C"` when interacting with C APIs (making sure to avoid 143 | destructors where `longjmp` might be used), and `extern "C unwind"` 144 | when interacting with C++ APIs. The main advantage of this proposal is that 145 | switching between `panic=unwind` and `panic=abort` does not introduce UB if you 146 | have correctly marked all potential unwinding calls as `"C unwind"` (your 147 | program will abort instead). 148 | 149 | ### Proposal 2: Introduce `"C unwind"`, forced unwinding always permitted 150 | 151 | This is the same as the previous design, except that when compiled with 152 | `panic=abort`, forced unwinding would *not* be intercepted at `"C unwind"` ABI 153 | boundaries; that is, they would behave normally (though still UB if there are 154 | any destructors), without causing the program to abort. `panic`-unwind and 155 | non-forced foreign exceptions would still cause the program to abort. 156 | 157 | The advantage of treating forced unwinding differently is that it reduces 158 | portability incompatibilities. Specifically, it ensures that using `"C unwind"` 159 | cannot cause `longjmp` or `pthread_exit` to stop working (abort the program) 160 | when the target platform and/or compile flags are changed. With proposal 1, 161 | `longjmp` will be able to cross `"C unwind"` boundaries _except_ on Windows 162 | with MSVC under `panic=abort`, and `pthread_exit` will work inside `"C unwind"` 163 | functions _except_ when linked with glibc under `panic=abort`. The downside of 164 | this proposal is that the abort stubs around `"C unwind"` calls in `panic=abort` 165 | become more complicated since they need to distinguish between different types 166 | of foreign exceptions. 167 | 168 | ### Proposal 3: No new ABI 169 | 170 | * `panic=unwind`: unwind behaves normally 171 | * `panic=abort`: 172 | * `panic`-unwind: does not exist; `panic!` aborts the program 173 | * forced unwinding, no destructors: unwind behaves normally 174 | * other foreign unwinding: undefined behavior 175 | 176 | The main advantage of this proposal is its simplicity: there is only one ABI and 177 | the behavior of `panic=abort` is identical to that of `-fno-exceptions` in C++. 178 | However this comes with the downside that switching to `panic=abort` may in some 179 | cases introduce UB (though only in unsafe code) if FFI calls unwind through Rust 180 | code. 181 | 182 | Another advantage is that forced unwinding from existing functions defined in 183 | the `libc` crate such as `pthread_exit` and `longjmp` will be able to unwind 184 | frames with destructors when compiled with `panic=unwind`, which is not possible 185 | with the other proposals. 186 | 187 | ## Comparison table for the proposed designs 188 | 189 | In this table, "UB" stands for "undefined behavior". Some instances of UB can 190 | be detected at runtime, but the code to do so would impose an undesirable 191 | code-size penalty; the group recommends generating such code only for debug 192 | builds. These cases are marked "UB (debug: abort)" 193 | 194 | Note that unwinding through a frame that has destructors without running those 195 | destructors (e.g. because they have been optimized out by `panic=abort`) is 196 | always undefined behavior. 197 | 198 | | | `panic`-unwind | Forced unwind, no destructors | Forced unwind with destructors | Other foreign unwind | 199 | | ------------------------------------------------------ | ------------------------------------- | ----------------------------- | ------------------------------ | -------------------- | 200 | | Proposals 1 & 2, `"C"` boundary, `panic=unwind` | abort | unwind | UB | UB | 201 | | Proposals 1 & 2, `"C"` boundary, `panic=abort` | `panic!` aborts (no unwinding occurs) | unwind | UB | UB | 202 | | Proposals 1 & 2, `"C unwind"` boundary, `panic=unwind` | unwind | unwind | unwind | unwind | 203 | | Proposal 1, `"C unwind"` boundary, `panic=abort` | `panic!` aborts | abort | abort | abort | 204 | | Proposal 2, `"C unwind"` boundary, `panic=abort` | `panic!` aborts | unwind | UB | abort | 205 | | Proposal 3, `"C"` boundary, `panic=unwind` | unwind | unwind | unwind | unwind | 206 | | Proposal 3, `"C"` boundary, `panic=abort` | `panic!` aborts | unwind | UB | UB | 207 | 208 | [rfc-announcement]: https://github.com/rust-lang/rfcs/pull/2797 209 | [meeting-link]: https://arewemeetingyet.com/UTC/XXX-TODO 210 | [man-pthreads]: http://man7.org/linux/man-pages/man7/pthreads.7.html 211 | -------------------------------------------------------------------------------- /posts/inside-rust/02-deallocate-stack-frames.md: -------------------------------------------------------------------------------- 1 | 2 | # Rust & the case of the disappearing stack frames 3 | 4 | -- Kyle Strand on behalf of the FFI-unwind project group -- 5 | 6 | Now that the [FFI-unwind Project Group][proj-group-gh] has merged [an 7 | RFC][c-unwind-rfc] specifying the `"C unwind"` ABI and removing some instances 8 | of undefined behavior in the `"C"` ABI, we are ready to establish new goals for 9 | the group. 10 | 11 | Our most important task, of course, is to implement the newly-specified 12 | behavior. This work has been undertaken by Katelyn Martin and can be followed 13 | [here][c-unwind-pr]. 14 | 15 | The requirements of our current charter, and the [RFC creating the 16 | group][proj-group-rfc], are effectively fulfilled by the specification of `"C 17 | unwind"`, so one option is to simply wind down the project group. While 18 | drafting the `"C unwind"` RFC, however, we discovered that the existing 19 | guarantees around `longjmp` and similar functions could be improved. Although 20 | this is not strictly related to unwinding[1](#longjmp-unwind), they 21 | are closesly related: they are both "non-local" control-flow mechanisms that 22 | prevent functions from returning normally. Because one of the goals of the Rust 23 | project is for Rust to interoperate with existing C-like languages, and these 24 | control-flow mechanisms are widely used in practice, we believe that Rust must 25 | have some level of support for them. 26 | 27 | This blog post will explain the problem space. If you're interested in helping 28 | specify this behavior, please come join us in [our Zulip 29 | stream][proj-group-zulip]! 30 | 31 | ## `longjmp` and its ilk 32 | 33 | Above, I mentioned `longjmp` and "similar functions". Within the context of the 34 | `"C unwind"` PR, this referred to functions that have different implementations 35 | on different platforms, and which, on *some* platforms, rely on [forced 36 | unwinding][forced-unwinding]. In our next specification effort, however, we 37 | would like to ignore the connection to unwinding entirely, and define a class 38 | of functions with the following characteristic: 39 | 40 | > a function that causes a "jump" in control flow by deallocating some number of 41 | > stack frames without performing any additional "clean-up" such as running 42 | > destructors 43 | 44 | This is the class of functions we would like to address. The other primary 45 | example is `pthread_exit`. As part of our specification, we would like to 46 | create a name for this type of function, but we have not settled on one yet; 47 | for now, we are referring to them as "cancelable", "`longjmp`-like", or 48 | "stack-deallocating" functions. 49 | 50 | ## Our constraints 51 | 52 | Taking a step back, we have two mandatory constraints on our design: 53 | 54 | * There must be sound way to call `libc` functions that may `pthread_cancel`. 55 | * There must be a sound way for Rust code to invoke C code that may `longjmp` 56 | over Rust frames. 57 | 58 | In addition, we would like to adhere to several design principles: 59 | 60 | * The specified behavior can't be target-platform-specific; in other words, our 61 | specification of Rust's interaction with `longjmp` should not depend on 62 | whether `longjmp` deallocates frames or initiates a forced-unwind. 63 | Optimizations, however, *can* be target-platform-specific. 64 | * There should be no difference in the specified behavior of frame-deallocation 65 | performed by `longjmp` versus that performed by `pthread_cancel`. 66 | * We will only permit canceling POFs ("Plain Old Frames", explained in the next 67 | section). 68 | 69 | ## POFs and stack-deallocating functions 70 | 71 | The `"C unwind"` RFC introduced a new concept designed to help us deal with 72 | force-unwinding or stack-deallocating functions: the [POF, or "Plain Old 73 | Frame"][POF-definition]. These are frames that can be trivially deallocated, 74 | i.e., they do no "cleanup" (such as running `Drop` destructors) before 75 | returning. 76 | 77 | From the definition, it should be clear that it is dangerous to call a 78 | stack-deallocating function in a context that could destroy a non-POF stack 79 | frame. A simple specification for Rust's interaction with stack-deallocating 80 | functions, then, could be that they are safe to call as long as only POFs are 81 | deallocated. This would make Rust's guarantees for `longjmp` essentially the 82 | same as C++'s. 83 | 84 | For now, however, we are considering POFs to be "necessary but not sufficient." 85 | We believe that a more restrictive specification may provide the following 86 | advantages: 87 | 88 | * more opportunities for helpful compiler warnings or errors to prevent misuse 89 | of stack-deallocation functions 90 | * semantic tracatbility: we can make reliance on stack-frame-deallocation 91 | visible for all functions involved 92 | * increased optimization potential when cleanup is "guaranteed" (i.e., the 93 | compiler may turn a POF into a non-POF if it knows that this is safe and that 94 | the newly inserted cleanup operation is necessary for an optimization) 95 | 96 | ## Annotating POFs 97 | 98 | Our current plan is to introduce a new annotation for frames that are intended 99 | to be safe to cancel. These functions, of course, must be POFs. The 100 | annotation would be "transitive", just like `async`: functions without this 101 | annotation either must not invoke any annotated functions or must guarantee 102 | that they will cause the stack-deallocation to terminate (for instance, a 103 | non-POF, non-annotated function may call `setjmp`). 104 | 105 | ### Open questions 106 | 107 | The name of the annotation should be based on the terminology used to refer to 108 | functions that are safe to deallocate. Because this terminology is not 109 | finalized, we do not yet have a name for the annotation. 110 | 111 | It is also not yet clear whether annotated functions should be able to invoke 112 | any functions without this annotation. As long as the function call does not 113 | return a new `Drop` resource (making the annotated function no longer a POF), 114 | it may be safe, as long as we guarantee that the annotated function cannot be 115 | canceled while the un-annotated function is still on the stack; i.e., 116 | cancelation must happen during an active call to an annotated cancelable 117 | function. 118 | 119 | Most importantly, we do not have a plan for how to indicate that a 120 | non-annotated function can safely call an annotated function. The example of 121 | using `setjmp` to ensure that a `longjmp` will not discard a stack frame is 122 | non-trivial: 123 | 124 | * `setjmp` is not a function but a C macro. There is no way to call it directly 125 | in Rust. 126 | * `setjmp` does not prevent arbitrary `longjmp`s from crossing over a frame, 127 | the way C++'s `catch` can catch any exception. Instead, `setjmp` creates an 128 | object of type `jmp_buf`, which must be passed to `longjmp`; this causes the 129 | jump to stop at the corresponding `setjmp` call. 130 | 131 | And, of course, `setjmp`/`longjmp` is not the only example of such a mechanism! 132 | Thus, there is probably no way for the compiler to guarantee that this is safe, 133 | and it's unclear what heuristics could be applied to make it as safe as 134 | possible. 135 | 136 | ### Examples 137 | 138 | Let us use `#[pof-longjmp]` as a placeholder for the annotation indicating a 139 | function that can be safely deallocated, and let us assume that the following 140 | function is a wrapper around `longjmp`: 141 | 142 | ```rust 143 | extern "C" { 144 | #[pof-longjmp] 145 | fn longjmp(CJmpBuf) -> !; 146 | } 147 | ``` 148 | 149 | The compiler would not allow this: 150 | 151 | ```rust 152 | fn has_drop(jmp_buf: CJmpBuf) { 153 | let s = "string data".to_owned(); 154 | unsafe { longjmp(jmp_buf); } 155 | println!("{}", s); 156 | } 157 | ``` 158 | 159 | Here, `s` implements `Drop`, so `has_drop` is not a POF. Since `longjmp` is 160 | annotated `#[pof-longjmp]`, the un-annotated function `has_drop` can't call it 161 | (even in an `unsafe` block). If, however, `has_drop` is annotated: 162 | 163 | ```rust 164 | #[pof-longjmp] 165 | fn has_drop(jmp_buf: CJmpBuf) { 166 | let s = "string data".to_owned(); 167 | unsafe { longjmp(jmp_buf); } 168 | println!("{}", s); 169 | } 170 | ``` 171 | 172 | ...there is a different error: `#[pof-longjmp]` can only be applied to POFs, 173 | and since `s` implements `Drop`, `has_drop` is not a POF. 174 | 175 | An example of a permissible `longjmp` call would be: 176 | 177 | ```rust 178 | #[pof-longjmp] 179 | fn no_drop(jmp_buf: CJmpBuf) { 180 | let s = "string data"; 181 | unsafe { longjmp(jmp_buf); } 182 | println!("{}", s); 183 | } 184 | ``` 185 | 186 | ## Difference from the "never" type 187 | 188 | Above, a Rust call to C's `longjmp` function was declared with the "never" 189 | return type, `!`. A function with this return type cannot return normally, 190 | which sounds superficially similar to functions that can be "canceled". 191 | However, cancelation is more restrictive: `-> !` usually indicates an infinite 192 | loop. This cannot cause a resource leak, so it is safe for non-POFs. 193 | 194 | ## Join us! 195 | 196 | If you would like to help us create this specification and write an RFC for it, 197 | please join us in [zulip][proj-group-zulip]! 198 | 199 | #### Footnotes 200 | 201 | 1: As mentioned in the RFC, on Windows, 202 | `longjmp` actually *is* an unwinding operation. On other platforms, however, 203 | `longjmp` is unrelated to unwinding. 204 | 205 | [proj-group-gh]: https://github.com/rust-lang/project-ffi-unwind 206 | [proj-group-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2797-project-ffi-unwind.md 207 | [proj-group-zulip]: https://rust-lang.zulipchat.com/#narrow/stream/210922-project-ffi-unwind/topic/welcome.2C.20redux/near/216807277 208 | [c-unwind-rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md 209 | [c-unwind-pr]: https://github.com/rust-lang/rust/pull/76570 210 | [forced-unwinding]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md#forced-unwinding 211 | [POF-definition]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md#plain-old-frames 212 | -------------------------------------------------------------------------------- /resolved-concerns/2019-10-17-tbd-on-stable.md: -------------------------------------------------------------------------------- 1 | # TBD behavior on stable 2 | 3 | ## Three categories of behavior 4 | 5 | * Undefined Behavior -- always a bug. Conceptually, the "miri virtual machine" panics here. This is the same definition [in use by the unsafe code guidelines](https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#undefined-behavior) group 6 | * Unspecified Behavior or Details -- the "miri virtual machine" does not panic, but we don't say what happens. As you point out, there are a number of *reasons* we might not have said what happens yet: 7 | * We do not expect to *ever* specify this, because we want the freedom to change it. 8 | * Example: Rust struct layout (without a repr attribute) -- not strictly a *behavior*, of course 9 | * Example: whether some local variable is materialized on the stack. 10 | * We *may* specify this at some point in the future, but there are no plans to do so. 11 | * Example: Rust ABI compatibility 12 | * Example: What symbols get exported by a DLL 13 | * We wish to specify this behavior in the near future, as part of the FFI-unwind project 14 | * This is what I am calling **to be determined behavior**, but this is not a term that would appear in the Rust reference. In that context, we would describe such behavior as unspecified, but link to the ffi-unwind project as a place where users can learn more. 15 | * Examples: 16 | * Details of how a Rust panic presents itself in "C unwind" ABI on msvc 17 | 18 | ## TBD as an "project-local planning measure" 19 | 20 | TBD is perhaps *best* understood as an "internal planning" device. The ffi-unwind group calls such behavior TBD if the ffi-unwind group expects to specify it at some point. 21 | 22 | ## Controversy: should TBD behavior hit stable? 23 | 24 | * Unspecified behavior hits stable all the time, that's normal 25 | * But the more we can minimize unspecified behavior on stable, the better, because it leaves room for "de facto" dependencies to form 26 | * If we truly wish to avoid TBD behavior, it will be hard to stabilize "C unwind" for any platform without fully specifying all details. 27 | * Specifically, I think we agree that "unwinding native frames with no destrutors" are an easy case that don't require us to specify as many things -- but those frames are out of our control, so we once we permit a panic to propagate through a "C unwind" boundary, we can't really restrict what the native frames might do 28 | * The whole problem here is that there exists code using unwinding today (on stable, I believe) 29 | * We are going to make that code migrate to "C unwind" 30 | * If they could migrate to nightly, they would've done so already and used `#[unwind(allows)]` 31 | * What if *in their specific case* we have decided enough for their behavior to be considered stable (e.g., they don't have destructors that might execute) 32 | * We are in active communication with (many of) the libary authors in question, and the ffi-unwind group is actively working on specifying those TBD details. 33 | 34 | ## Core tradeoff 35 | 36 | We are fundamentally weighing two things against one another 37 | 38 | * the risk of de facto dependencies (which favors nightly) 39 | * preventing libraries from using the feature on stable (which favors stable) 40 | 41 | ## Resolution 42 | 43 | Ordinarily we definitely lean towards preventing de facto dependencies, and that is the correct call. In this case, de facto dependencies have developed (on the "C" ABI, presently), and we are attempting to get the semantics we want without breaking those dependencies (although they do require changes). **Therefore, it makes sense for us to permit "C unwind" on stable, with the explicit caveat that some details are unspecified (but we are working on it).** 44 | 45 | -------------------------------------------------------------------------------- /resolved-concerns/README.md: -------------------------------------------------------------------------------- 1 | This directory stores "resolved concerns" -- basically, whenever there 2 | is a non-obvious tradeoff, we try to create a document that describes 3 | the tradeoff in full, and then documents the resolution we ultimately 4 | reached. Sometimes these documents include dissents, if not everyone 5 | agreed with the ultimate outcome. These documents can then later be 6 | referenced if needed to recall the reasoning involved. 7 | -------------------------------------------------------------------------------- /rfcs/0000-c-unwind-abi.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | - Feature Name: `"C unwind" ABI` 4 | - Start Date: 2019-04-03 5 | - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) 6 | - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) 7 | - Project group: [FFI-unwind][project-group] 8 | 9 | [project-group]: https://github.com/rust-lang/project-ffi-unwind 10 | 11 | # Summary 12 | [summary]: #summary 13 | 14 | We introduce a new ABI string, `"C unwind"`, to enable unwinding from other 15 | languages (such as C++) into Rust frames and from Rust into other languages. 16 | 17 | Additionally, we define the behavior for a limited number of 18 | previously-undefined cases when an unwind operation reaches a Rust function 19 | boundary with a non-`"Rust"`, non-`"C unwind"` ABI. 20 | 21 | As part of this specification, we introduce the term ["Plain Old Frame" 22 | (POF)][POF-definition]. These are frames that have no pending destructors and 23 | can be trivially deallocated. 24 | 25 | This RFC does not define the behavior of `catch_unwind` in a Rust frame being 26 | unwound by a foreign exception. This is something the [project 27 | group][project-group] would like to specify in a future RFC; as such, it is 28 | "TBD" (see ["Unresolved questions"][unresolved-questions]). 29 | 30 | # Motivation 31 | [motivation]: #motivation 32 | 33 | There are some Rust projects that need cross-language unwinding to provide 34 | their desired functionality. One major example is Wasm interpreters, including 35 | the Lucet and Wasmer projects. 36 | 37 | There are also existing Rust crates (notably, wrappers around the `libpng` and 38 | `libjpeg` C libraries) that `panic` across C frames. The safety of such 39 | unwinding relies on compatibility between Rust's unwinding mechanism and the 40 | native exception mechanisms in GCC, LLVM, and MSVC. Despite using a compatible 41 | unwinding mechanism, the current `rustc` implementation assumes that `extern 42 | "C"` functions cannot unwind, which permits LLVM to optimize with the 43 | assumption that such unwinding constitutes undefined behavior. 44 | 45 | The desire for this feature has been previously discussed on other RFCs, 46 | including [#2699][rfc-2699] and [#2753][rfc-2753]. 47 | 48 | ## Key design goals 49 | 50 | As explained in [this Inside Rust blog post][inside-rust-requirements], we have 51 | several requirements for any cross-language unwinding design. 52 | 53 | The ["Analysis of key design goals"][analysis-of-design-goals] section analyzes 54 | how well the current design satisfies these constraints. 55 | 56 | * **Changing from `panic=unwind` to `panic=abort` cannot cause undefined 57 | behavior:** We wish to ensure that changing from `panic=unwind` to 58 | `panic=abort` never creates undefined behavior (relate to `panic=unwind`), 59 | even if one is relying on a library that triggers a panic or a foreign 60 | exception. 61 | * **Optimization with `panic=abort`:** when using `panic=abort`, we 62 | wish to enable as many code-size optimizations as possible. This 63 | means that we shouldn't have to generate unwinding tables or other 64 | such constructs, at least in most cases. 65 | * **Preserve the ability to change how Rust panics are propagated when 66 | using the Rust ABI:** Currently, Rust panics are propagated using 67 | the native unwinding mechanism, but we would like to keep the 68 | freedom to change that. 69 | * **Enable Rust panics to traverse through foreign frames:** Several 70 | projects would like the ability to have Rust panics propagate 71 | through foreign frames. Those frames may or may not register 72 | destructors of their own with the native unwinding mechanism. 73 | * **Enable foreign exceptions to propagate through Rust frames:** 74 | Similarly, we would like to make it possible for C++ code (or other 75 | languages) to raise exceptions that will propagate through Rust 76 | frames "as if" they were Rust panics (i.e., running destrutors or, 77 | in the case of `unwind=abort`, aborting the program). 78 | * **Enable error handling with `longjmp`:** 79 | As mentioned above, some existing Rust libraries rely on the ability to 80 | `longjmp` across Rust frames to interoperate with Ruby, Lua, and other C 81 | APIs. The behavior of `longjmp` traversing Rust frames is not specified or 82 | guaranteed to be safe; in the current implementation of `rustc`, 83 | however, it [is safe][longjmp-pr]. On Windows, `longjmp` is implemented as a 84 | form of unwinding called ["forced unwinding"][forced-unwinding], so any 85 | specification of the behavior of forced unwinding across FFI boundaries 86 | should be forward-compatible with a [future RFC][unresolved-questions] that 87 | will provide a well-defined way to interoperate with longjmp-based APIs. 88 | * **Do not change the ABI of functions in the `libc` crate:** Some `libc` 89 | functions may invoke `pthread_exit`, which uses [a form of 90 | unwinding][forced-unwinding] in the GNU libc implementation. Such functions 91 | must be safe to use with the existing `"C"` ABI, because changing the types 92 | of these functions would be a breaking change. 93 | 94 | [inside-rust-requirements]: https://blog.rust-lang.org/inside-rust/2020/02/27/ffi-unwind-design-meeting.html#requirements-for-any-cross-language-unwinding-specification 95 | [longjmp-pr]: https://github.com/rust-lang/rust/pull/48572 96 | 97 | # Guide-level explanation 98 | [guide-level-explanation]: #guide-level-explanation 99 | 100 | Functions declared with `extern "C"` are generally not permitted to unwind 101 | (with some very narrow exceptions, described [below][forced-unwinding]). 102 | This means, for example, that such a function cannot throw an uncaught C++ 103 | exception, and it also cannot invoke Rust code that may panic (unless that 104 | panic is caught with `catch_unwind`). 105 | 106 | When declaring an external function that may unwind, such as an entrypoint to a 107 | C++ library, use `"C unwind"` instead: 108 | 109 | ``` 110 | extern "C unwind" { 111 | fn may_throw(); 112 | } 113 | ``` 114 | 115 | Rust functions that call a possibly-unwinding external function should either 116 | use the default Rust ABI (which can be made explicit with `extern "Rust"`) or 117 | the `"C unwind"` ABI: 118 | 119 | ``` 120 | extern "C unwind" fn can_unwind() { 121 | may_throw(); 122 | } 123 | ``` 124 | 125 | Using the `"C unwind"` ABI to "sandwich" Rust frames between frames from 126 | another language (such as C++) allows an exception initiated in a callee frame 127 | in the other language to traverse the intermediate Rust frames before being 128 | caught in the caller frames. I.e., a C++ exception may be thrown, 129 | cross into Rust via an `extern "C unwind"` function declaration, safely unwind 130 | the Rust frames, and cross back into C++ (where it may be caught) via a Rust 131 | `"C unwind"` function definition. 132 | 133 | Conversely, languages that support the native unwinding mechanism, such as C++, 134 | may be "sandwiched" between Rust frames, so that Rust `panic`s may safely 135 | unwind the C++ frames, if the Rust code declares both the C++ entrypoint and 136 | the Rust entrypoint using `"C unwind"`. 137 | 138 | ## "Plain Old Frames" 139 | [POF-definition]: #plain-old-frames 140 | 141 | A "POF", or "Plain Old Frame", is defined as a frame that can be trivially 142 | deallocated: returning from or unwinding a POF cannot cause any 143 | observable effects. This means that POFs do not contain any pending destructors 144 | (live `Drop` objects) or `catch_unwind` calls. 145 | 146 | The terminology is intentionally akin to [C++'s "Plain Old Data" 147 | types][cpp-POD-definition], which are types that, among other requirements, are 148 | trivially destructible (their destructors do not cause any observable effects, 149 | and may be elided as an optimization). 150 | 151 | Rust frames that do contain pending destructors or `catch_unwind` calls are 152 | called non-POFs. 153 | 154 | Note that a non-POF may _become_ a POF, for instance if all `Drop` objects are 155 | moved out of scope, or if its only `catch_unwind` call is in a code path that 156 | will not be executed. The next section provides an example. 157 | 158 | [cpp-POD-definition]: https://en.cppreference.com/w/cpp/named_req/PODType 159 | 160 | ## Forced unwinding 161 | [forced-unwinding]: #forced-unwinding 162 | 163 | This is a special kind of unwinding used to implement `longjmp` on Windows and 164 | `pthread_exit` in `glibc`. A brief explanation is provided in [this Inside Rust 165 | blog post][inside-rust-forced]. This RFC distinguishes forced unwinding from 166 | other types of foreign unwinding. 167 | 168 | Since language features and library functions implemented using forced 169 | unwinding on some platforms use other mechanisms on other platforms, Rust code 170 | cannot rely on forced unwinding to invoke destructors (calling `drop` on `Drop` 171 | types). In other words, a forced unwind operation on one platform will simply 172 | deallocate Rust frames without true unwinding on other platforms. 173 | 174 | This RFC specifies that, regardless of the platform or the ABI string (`"C"` or 175 | `"C unwind"`), any platform features that may rely on forced unwinding are 176 | undefined behavior if they cross non-[POFs][POF-definition]. For now, however, 177 | we do not specify the conditions required to use forced unwinding safely; we 178 | will specify this in [a future RFC][unresolved-questions]. 179 | 180 | [inside-rust-forced]: https://blog.rust-lang.org/inside-rust/2020/02/27/ffi-unwind-design-meeting.html#forced-unwinding 181 | 182 | ## Changes to `extern "C"` behavior 183 | [extern-c-behavior]: #changes-to-extern-c-behavior 184 | 185 | Prior to this RFC, any unwinding operation that crossed an `extern "C"` 186 | boundary, either from a `panic!` "escaping" from a Rust function defined with 187 | `extern "C"` or by entering Rust from another language via an entrypoint 188 | declared with `extern "C"`, caused undefined behavior. 189 | 190 | This RFC retains most of that undefined behavior, with one exception: with the 191 | `panic=unwind` runtime, `panic!` will cause an `abort` if it would otherwise 192 | "escape" from a function defined with `extern "C"`. 193 | 194 | ## Interaction with `panic=abort` 195 | 196 | If a non-forced foreign unwind would enter a Rust frame via an `extern "C 197 | unwind"` ABI boundary, but the Rust code is compiled with `panic=abort`, the 198 | unwind will be caught and the process aborted. 199 | 200 | With the exception of the above case, however, unwinding from another language 201 | into Rust through an FFI entrypoint declared with `extern "C"` is always 202 | undefined behavior, and is not guaranteed to cause the program to abort under 203 | `panic=abort`. 204 | 205 | # Reference-level explanation 206 | [reference-level-explanation]: #reference-level-explanation 207 | 208 | This table shows the behavior of an unwinding operation reaching each type of 209 | ABI boundary (function declaration or definition). "UB" stands for undefined 210 | behavior. `"C"`-like ABIs are `"C"` itself but also related ABIs such as 211 | `"system"`. 212 | 213 | | panic runtime | ABI | `panic`-unwind | Unforced foreign unwind | 214 | | -------------- | ------------ | ------------------------------------- | ----------------------- | 215 | | `panic=unwind` | `"C unwind"` | unwind | unwind | 216 | | `panic=unwind` | `"C"`-like | abort | UB | 217 | | `panic=abort` | `"C unwind"` | `panic!` aborts | abort | 218 | | `panic=abort` | `"C"`-like | `panic!` aborts (no unwinding occurs) | UB | 219 | 220 | The interaction of Rust frames with C functions that deallocate frames (i.e. 221 | functions that may use forced unwinding on specific platforms) is independent 222 | of the panic runtime, ABI, or platform. 223 | 224 | * **When deallocating Rust non-POFs:** this is explicitly undefined behavior. 225 | * **When deallocating Rust [POFs][POF-definition]:** for now, this is not 226 | specified, and must be considered undefined behavior. However, we do plan to 227 | specify a safe way to deallocate POFs with `longjmp` or `pthread_exit` in [a 228 | future RFC][unresolved-questions]. 229 | 230 | No subtype relationship is defined between functions or function pointers using 231 | different ABIs. This RFC also does not define coercions between `"C"` and 232 | `"C unwind"`. 233 | 234 | As noted in the [summary][summary], if a Rust frame containing a pending 235 | `catch_unwind` call is unwound by a foreign exception, the behavior is 236 | undefined for now. 237 | 238 | # Drawbacks 239 | [drawbacks]: #drawbacks 240 | 241 | Forced unwinding is treated as universally unsafe across 242 | [non-POFs][POF-definition], but on some platforms it could theoretically be 243 | well-defined. As noted [above](forced-unwind), however, this would make the UB 244 | inconsistent across platforms, which is not desirable. 245 | 246 | This design imposes some burden on existing codebases (mentioned 247 | [above][motivation]) to change their `extern` annotations to use the new ABI. 248 | 249 | Having separate ABIs for `"C"` and `"C unwind"` may make interface design more 250 | difficult, especially since this RFC [postpones][unresolved-questions] 251 | introducing coercions between function types using different ABIs. 252 | 253 | A single ABI that "just works" with C++ (or any other language that may throw 254 | exceptions) would be simpler to learn and use than two separate ABIs. 255 | 256 | # Rationale and alternatives 257 | [rationale-and-alternatives]: #rationale-and-alternatives 258 | 259 | ## Other proposals discussed with the lang team 260 | [alternatives]: #other-proposals-discussed-with-the-lang-team 261 | 262 | Two other potential designs have been discussed in depth; they are 263 | explained in [this Inside Rust blog post][inside-rust-proposals]. The design in this 264 | RFC is referred to as "option 2" in that post. 265 | 266 | "Option 1" in that blog post only differs from the current proposal in the 267 | behavior of a forced unwind across a `"C unwind"` boundary under `panic=abort`. 268 | Under the current proposal, this type of unwind is permitted, allowing 269 | `longjmp` and `pthread_exit` to behave "normally" with both the `"C"` and the 270 | `"C unwind"` ABI across all platforms regardless of panic runtime. If 271 | [non-POFs][POF-definition] are unwound, this results in undefined behavior. 272 | Under "option 1", however, all foreign unwinding, forced or unforced, is caught 273 | at `"C unwind"` boundaries under `panic=abort`, and the process is aborted. 274 | This gives `longjmp` and `pthread_exit` surprising behavior on some platforms, 275 | but avoids that cause of undefined behavior in the current proposal. 276 | 277 | The other proposal in the blog post, "option 3", is dramatically different. In 278 | that proposal, foreign exceptions are permitted to cross `extern "C"` 279 | boundaries, and no new ABI is introduced. 280 | 281 | [inside-rust-proposals]: https://blog.rust-lang.org/inside-rust/2020/02/27/ffi-unwind-design-meeting.html#three-specific-proposals 282 | 283 | ## Reasons for the current proposal 284 | [rationale]: #reasons-for-the-current-proposal 285 | 286 | Our reasons for preferring the current proposal are: 287 | 288 | * Introducing a new ABI makes reliance on cross-language exception handling 289 | more explicit. 290 | * `panic=abort` can be safely used with `extern "C unwind"` (there is no 291 | undefined behavior except with improperly used forced unwinding), but `extern 292 | "C"` has more optimization potential (eliding landing pads). Having two ABIs 293 | puts this choice in the hands of users. 294 | * The single-ABI proposal ("option 3") causes any foreign exception entering 295 | Rust to have undefined behavior under `panic=abort`, whereas the current 296 | proposal does not permit the `panic=abort` runtime to introduce undefined 297 | behavior to a program that is well-defined under `panic=unwind`. 298 | * This optimization could be made available with a single ABI by means of a 299 | function attribute indicating that a function cannot unwind (similar to C++'s 300 | `noexcept`). Such attributes [are already available in nightly 301 | Rust][nightly-attributes]. However, Rust does not yet support attributes 302 | for function pointers, so until that feature is added, there would be no 303 | way to indicate whether function pointers unwind using an attribute. 304 | * This design has simpler forward compatibility with alternate `panic!` 305 | implementations. Any well-defined cross-language unwinding will require shims 306 | to translate between the Rust unwinding mechanism and the natively provided 307 | mechanism. In this proposal, only `"C unwind"` boundaries would require shims. 308 | 309 | ## Analysis of key design goals 310 | [analysis-of-design-goals]: #analysis-of-design-goals 311 | 312 | This section revisits the key design goals to assess how well they 313 | are met by the proposed design. 314 | 315 | ### Changing from `panic=unwind` to `panic=abort` cannot cause UB 316 | 317 | This constraint is met: 318 | 319 | * Unwinding across a "C" boundary is UB regardless 320 | of whether one is using `panic=unwind` or `panic=abort`. 321 | * Unwinding across a "C unwind" boundary is always defined, 322 | though it is defined to abort if `panic=abort` is used. 323 | * Forced exceptions behave the same regardless of panic mode. 324 | 325 | ### Optimization with panic=abort 326 | 327 | Using this proposal, the compiler is **almost always** able to reduce 328 | overhead related to unwinding when using panic=abort. The one 329 | exception is that invoking a "C unwind" ABI still requires some kind 330 | of minimal landing pad to trigger an abort. The expectation is that 331 | very few functions will use the "C unwind" boundary unless they truly 332 | intend to unwind -- and, in that case, those functions are likely 333 | using panic=unwind anyway, so this is not expected to make much 334 | difference in practice. 335 | 336 | ### Preserve the ability to change how Rust panics are propagated when using the Rust ABI 337 | 338 | This constraint is met. If we were to change Rust panics to a 339 | different mechanism from the mechanism used by the native ABI, 340 | however, there would have to be a conversion step that interconverts 341 | between Rust panics and foreign exceptions at "C unwind" ABI 342 | boundaries. 343 | 344 | ### Enable Rust panics to traverse through foreign frames 345 | 346 | This constraint is met. 347 | 348 | ### Enable foreign exceptions to propagate through Rust frame 349 | 350 | This constraint is partially met: the behavior of foreign exceptions 351 | with respect to `catch_unwind` is currently undefined, and left for 352 | future work. 353 | 354 | ### Enable error handling with `longjmp` 355 | 356 | This constraint has been [deferred][unresolved-questions]. 357 | 358 | ### Do not change the ABI of functions in the `libc` crate 359 | 360 | This constraint has been [deferred][unresolved-questions]. 361 | 362 | # Prior art 363 | [prior-art]: #prior-art 364 | 365 | C++ as specified has no concept of "foreign" exceptions or of an underlying 366 | exception mechanism. However, in practice, the C++ exception mechanism is the 367 | "native" unwinding mechanism used by compilers. 368 | 369 | On Microsoft platforms, when using MSVC, unwinding is always supported for both 370 | C++ and C code; this is very similar to "option 3" described in [the 371 | inside-rust post][inside-rust-proposals] mentioned [above][alternatives]. 372 | 373 | On other platforms, GCC, LLVM, and any related compilers provide a flag, 374 | `-fexceptions`, for explicitly ensuring that stack frames have unwinding 375 | support regardless of the language being compiled. Conversely, 376 | `-fno-exceptions` removes unwinding support even from C++. This is somewhat 377 | similar to how Rust's `panic=unwind` and `panic=abort` work for `panic!` 378 | unwinds, and under the "option 3" proposal, the behavior would be similar for 379 | foreign exceptions as well. In the current proposal, though, such foreign 380 | exception support is not enabled by default with `panic=unwind` but requires 381 | the new `"C unwind"` ABI. 382 | 383 | ## Attributes on nightly Rust and prior RFCs 384 | [nightly-attributes]: #attributes-on-nightly-rust-and-prior-rfcs 385 | 386 | Currently, nightly Rust provides attributes, `#[unwind(allowed)]` and 387 | `#[unwind(abort)]`, that permit users to select a well-defined behavior when a 388 | `panic` reaches an `extern "C"` function boundary. Stabilization of these 389 | attributes has [a tracking issue][attributes-tracking-issue], but most 390 | of the discussion about whether this was the best approach took place in two 391 | RFC PR threads, [#2699][rfc-2699] and [#2753][rfc-2753]. 392 | 393 | The attribute approach was deemed insufficient for the following reasons: 394 | 395 | * Currently, Rust does not support attributes on function pointers. This may 396 | change in the future, but until then, attributes cannot provide any way to 397 | differentiate function pointers that may unwind from those that are 398 | guaranteed not to. Assuming that no function pointers may unwind is not 399 | viable, because that severly limits the utility of cross-FFI unwinding. 400 | Conversely, assuming that all `extern "C"` function pointers may unwind is 401 | inconsistent with the no-unwind default for `extern "C"` functions. 402 | * The existence of a compatible unwind mechanism on both sides of a function 403 | invocation boundary is part of the binary interface for that invocation, so 404 | the ABI string is a more appropriate part of the language syntax than 405 | function attributes to indicate that unwinding may occur. 406 | * The ability of a function to unwind must be part of the type system to ensure 407 | that callers that cannot unwind don't invoke functions that can unwind. 408 | Although attributes are sometimes part of a function's type, a function's ABI 409 | string is always part of its type, so we are not introducing any new elements 410 | to the type system. 411 | 412 | [attributes-tracking-issue]: https://github.com/rust-lang/rust/issues/58760 413 | [rfc-2699]: https://github.com/rust-lang/rfcs/pull/2699 414 | [rfc-2753]: https://github.com/rust-lang/rfcs/pull/2753 415 | 416 | ## Older discussions about unwinding through `extern "C"` boundaries 417 | 418 | As mentioned [above][motivation], it is currently undefined behavior for 419 | `extern "C"` functions to unwind. As documented in [this 420 | issue][abort-unwind-issue], the lang team has long intended to make `panic!` 421 | cause the runtime to abort rather than unwind through an `extern "C"` boundary 422 | (which the current proposal [also specifies][extern-c-behavior]). 423 | 424 | The abort-on-unwind behavior was [stabilized in 1.24][1.24-release] and 425 | [reverted in 1.24.1][1.24.1-release]; the team originally planned to [stabilize 426 | it again][1.33-stabilization] in 1.33, but ultimately [decided not 427 | to][1.33-discussion]. Community discussion [on discourse][discourse-thread] was 428 | largely concerned with the lack of any stable language feature to permit 429 | unwinding across FFI boundaries, and this contributed to the decision to block 430 | the re-stabilization of the abort-on-unwind behavior until such a feature could 431 | be introduced. 432 | 433 | [abort-unwind-issue]: https://github.com/rust-lang/rust/issues/52652 434 | [1.24-release]: https://blog.rust-lang.org/2018/02/15/Rust-1.24.html#other-good-stuff 435 | [1.24.1-release]: https://blog.rust-lang.org/2018/03/01/Rust-1.24.1.html#do-not-abort-when-unwinding-through-ffi 436 | [1.33-stabilization]: https://github.com/rust-lang/rust/pull/55982 437 | [1.33-discussion]: https://github.com/rust-lang/rust/issues/58794 438 | [discourse-thread]: https://internals.rust-lang.org/t/unwinding-through-ffi-after-rust-1-33/9521?u=batmanaod 439 | 440 | # Unresolved questions 441 | [unresolved-questions]: #unresolved-questions 442 | 443 | The behavior of `catch_unwind` when a foreign exception encounters it is 444 | currently [left undefined][reference-level-explanation]. We would like to 445 | provide a well-defined behavior for this case, which will probably be either to 446 | let the exception pass through uncaught or to catch some or all foreign 447 | exceptions. 448 | 449 | We would also like to specify conditions under which `longjmp` and 450 | `pthread_exit` may safely deallocate Rust frames. This RFC specifies that 451 | frames deallocated in this way [must be POFs][reference-level-explanation]. 452 | However, this condition is merely necessary rather than sufficient to ensure 453 | well-defined behavior. 454 | 455 | Within the context of this RFC and in discussions among members of the 456 | [FFI-unwind project group][project-group], this class of formally-undefined 457 | behavior which we plan to define in future RFCs is referred to as "TBD 458 | behavior". 459 | 460 | # Future possibilities 461 | [future-possibilities]: #future-possibilities 462 | 463 | The [FFI-unwind project group][project-group] intends to remain active at least 464 | until all ["TBD behavior"][unresolved-questions] is defined. 465 | 466 | We may want to provide more means of interaction with foreign exceptions. For 467 | instance, it may be possible to provide a way for Rust to catch C++ exceptions 468 | and rethrow them from another thread. Such a mechanism may either be 469 | incorporated into the functionality of `catch_unwind` or provided as a separate 470 | language or standard library feature. 471 | 472 | Coercions between `"C unwind"` function types (such as function pointers) and 473 | the other ABIs are not part of this RFC. However, they will probably be 474 | indispensible for API design, so we plan to provide them in a future RFC. 475 | 476 | As mentioned [above][rationale], shims will be required if Rust changes its 477 | unwind mechanism. 478 | -------------------------------------------------------------------------------- /rfcs/0000-longjmp-pof-annotation.md: -------------------------------------------------------------------------------- 1 | - Feature Name: `annotation-for-safe-longjmp` 2 | - Start Date: 2019-06-11 3 | - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) 4 | - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) 5 | - Project group: [FFI-unwind][project-group] 6 | 7 | [project-group]: https://github.com/rust-lang/project-ffi-unwind 8 | 9 | 10 | 11 | # Motivation 12 | [motivation]: #motivation 13 | 14 | Additionally, there are libraries such as `rlua` that rely on `longjmp` across 15 | Rust frames; on Windows, `longjmp` is implemented via [forced 16 | unwinding][forced-unwinding]. The current `rustc` implementation makes it safe 17 | to `longjmp` across Rust [POFs][POF-definition] (frames without `Drop` types), 18 | but this is not formally specified in an RFC or by the Reference. 19 | 20 | # Guide-level explanation 21 | [guide-level-explanation]: #guide-level-explanation 22 | 23 | This RFC specifies that, regardless of the platform or the ABI string (`"C"` or 24 | `"C unwind"`), any platform features that may rely on forced unwinding is 25 | defined behavior when all unwound frames are POFs 26 | 27 | As an example: 28 | 29 | ```rust 30 | #[cancelable] 31 | fn foo(c: bool, d: D) { 32 | if c { 33 | drop(d); 34 | } 35 | longjmp_if_true(c); 36 | } 37 | 38 | /// Calls `longjmp` if `c` is true; otherwise returns normally. 39 | #[cancelable] 40 | extern "C" fn longjmp_if_true(c: bool); 41 | ``` 42 | 43 | If a `longjmp` occurs, it can safely traverse the `foo` frame, which will be a 44 | POF because `d` has already been dropped. 45 | 46 | Since `longjmp_if_true` function is using the `"C"` rather than the `"C 47 | unwind"` ABI, the optimizer may assume that it cannot unwind; on LLVM, this is 48 | represented by the `nounwind` attribute. On most platforms, `longjmp` is not a 49 | form of unwinding: the `foo` frame is simply discarded. On Windows, `longjmp` 50 | is implemented as a forced unwind, which is permitted to traverse `nounwind` 51 | frames. Since `foo` contains a `Drop` type the forced unwind will include a 52 | call to the frame's cleanup logic, but that logic will not produce any 53 | observable effect; in particular, `D::drop()` will not be called again. The 54 | observable behavior should therefore be the same on all platforms. 55 | 56 | Conversely, if, due to a bug, `longjmp` were called unconditionally, then this 57 | code would have undefined behavior on all platforms when `c` is false, because 58 | `foo` would not be a POF. 59 | 60 | 63 | 64 | # Reference-level explanation 65 | [reference-level-explanation]: #reference-level-explanation 66 | 67 | Regardless of the panic runtime, ABI, or platform, the interaction of Rust 68 | frames with C functions that deallocate frames (i.e. functions that may use 69 | forced unwinding on specific platforms) is specified as follows: 70 | 71 | * **When deallocating Rust [POFs][POF-definition]:** frames are safely 72 | deallocated; no undefined behavior 73 | * **When deallocating Rust non-POFs:** undefined behavior 74 | 75 | # Rationale and alternatives 76 | [rationale-and-alternatives]: #rationale-and-alternatives 77 | 78 | ## Analysis of key design goals 79 | [analysis-of-design-goals]: #analysis-of-design-goals 80 | 81 | ### Enable error handling with `longjmp` 82 | 83 | This constraint is met: `longjmp` is treated the same across all platforms, and 84 | is safe as long as only [POFs][POF-definition] are deallocated. 85 | 86 | ### Do not change the ABI of functions in the `libc` crate 87 | 88 | This constraint is met: `libc` functions will continue to use the `"C"` ABI. 89 | `pthread_exit` will be treated the same across all platforms, and will be safe 90 | as long as only [POFs][POF-definition] are deallocated. 91 | -------------------------------------------------------------------------------- /rfcs/announcement-and-charter.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | - Feature Name: `project-unwind-FFI` 4 | - Start Date: 2019-10-27 5 | - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) 6 | - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) 7 | 8 | # Summary 9 | [summary]: #summary 10 | 11 | * To create a "project group" with the purpose of designing subsequent RFCs to 12 | flesh out the details of the "C unwind" ABI 13 | * The "project group" term is newly introduced: it is a specific type of 14 | working group whose goal is to flesh out a particular proposal or complete 15 | a project. 16 | * This project group plans to specify how "C unwind" works on major 17 | platforms. 18 | * The primary goal is to enable Rust panics to propagate safely across 19 | foreign frames. 20 | * A future goal may be to enable foreign exceptions to propagate across Rust 21 | frames. 22 | * We do not plan to allow catching or throwing foreign exceptions from Rust 23 | code 24 | 25 | # Motivation 26 | [motivation]: #motivation 27 | 28 | Unwinding through Rust's `extern "C"` ABI is [Undefined Behavior]. There is an 29 | [existing plan][abort-unwind] to make the behavior of Rust's `panic` 30 | well-defined by causing Rust functions defined with `extern "C"` to abort the 31 | application whenever an uncaught `panic` would otherwise escape into the 32 | caller. Unfortunately, previous attempts to stabilize this behavior have caused 33 | existing, working projects to break. 34 | 35 | The problem here is not that the existing projects break *per se*: they are 36 | relying on [Undefined Behavior], so breakage is to be expected as a 37 | possibility. The problem is that there is no alternative available to them that 38 | would allow them to keep working (even if they are continuing to rely on 39 | behavior that is not yet fully specified). 40 | 41 | Previous attempts to provide a well-defined mechanism for unwinding across FFI 42 | boundaries have failed to reach consensus. Notably, two proposed RFCs generated 43 | over 400 comments between them before ultimately being closed: 44 | 45 | * [rust-lang/rfcs#2699](https://github.com/rust-lang/rfcs/pull/2699) 46 | * [rust-lang/rfcs#2753](https://github.com/rust-lang/rfcs/pull/2753) 47 | 48 | GitHub comment threads become difficult to follow for discussions this lengthy, 49 | and the disagreements in these threads have felt less productive than we 50 | believe they could be if more structure were provided. 51 | 52 | We would also like to demonstrate the Rust lang team's commitment to providing 53 | such a mechanism without needing to agree in advance on what language changes 54 | will be stabilized in order to do so. 55 | 56 | # Prototyping 'shepherded' project groups 57 | [prototyping-project-groups]: #prototyping-shepherded-project-groups 58 | 59 | With this RFC, we formally announce the formation of a project-specific, 60 | shepherded "project group" to adopt responsibility for driving progress on 61 | specifying unwinding behavior at FFI boundaries. 62 | 63 | ## What is a "project group"? 64 | 65 | The "project group" term has not previously been used: it is intended to 66 | formalize a concept that has existed informally for some time, under a number 67 | of names (including "working group"). 68 | 69 | A "project group" is a group of people working on a particular project at the 70 | behest of an official Rust team. Project groups must have: 71 | 72 | * A **charter** defining the project's scope 73 | * A **liaison** with an official Rust team (who may or may not also be a shepherd) 74 | * A small number of **shepherds**, who are responsible for summarizing 75 | conversations and keeping the lang team abreast of interesting developments. 76 | * A GitHub repository hosted under the `rust-lang` organization containing the 77 | charter and instructions for how community members can monitor the group's 78 | progress and/or participate. 79 | 80 | [This blog post][shepherds-3.0] explains in detail the role of the 81 | shepherds. 82 | 83 | ## Project group roadmap and RFCs 84 | 85 | The first step of the project group is to define a **roadmap** indicating the 86 | planned sequence in which it will design and propose particular behaviors and 87 | features. Once the project group feels it has completed work on some item in 88 | the roadmap, that item will be submitted to the community as an RFC or FCP. 89 | 90 | ## Stabilizing unspecified "TBD" behavior 91 | [stabilizing-tbd]: stabilizing-unspecified-tbd-behavior 92 | 93 | We would like to be able to provide features in stable Rust without a full 94 | formal specification but with an informal statement of intent regarding the 95 | behavior. These features would be considered "unspecified behavior" (rather 96 | than "undefined behavior"), and their behavior would therefore be subject to 97 | some change, but such a change would be guaranteed not to violate the project 98 | group's stated intent for the feature. 99 | 100 | Internally, a project group should refer to such unspecified behavior as "TBD", 101 | to indicate that refining the specification of the behavior is within the scope 102 | of the project. Outside of the context of the project group, such as in general 103 | reference material for the language, the behavioral intent as determined by the 104 | project group should be described, but will not imply any special status 105 | compared to other instances of unspecified behavior in the language. 106 | 107 | ## Details of the FFI-unwind project group 108 | 109 | [Repository][ffi-unwind project] 110 | 111 | Initial shepherds: 112 | 113 | * [acfoltzer (Adam)](https://github.com/acfoltzer) 114 | * [batmanaod (Kyle)](https://github.com/batmanaod) 115 | 116 | Lang team liaisons: 117 | 118 | * [nikmoatsakis (Niko)](https://github.com/nikmoatsakis) 119 | * [joshtriplett (Josh)](https://github.com/joshtriplett) 120 | 121 | ### Charter 122 | [charter]: #charter 123 | 124 | The FFI-unwind project group has the following initial scope: 125 | 126 | * to define the details of the "C unwind" ABI on major Tier 1 platforms 127 | * in particular, to define with sufficient detail to enable the use cases 128 | described in the Motivation section of this RFC 129 | 130 | Certain elements are definitively out of scope: 131 | 132 | * The group does not intend to consider mechanisms to enable "interop" 133 | between Rust panics and exceptions from other languges. For example, 134 | we do not intend to permit Rust code to catch C++ exceptions, though 135 | we will have to consider what happens when a C++ exception unwinds 136 | past a `catch_unwind` boundary. 137 | 138 | ### Constraints and considerations 139 | 140 | In its work, the project-group should consider various constraints and 141 | considerations: 142 | 143 | * The possibility that C++ may adopt new unwinding mechanisms in the future. 144 | * The possibility that Rust may alter its unwinding mechanism in the future -- 145 | in particular, the project group must not propose a design that would 146 | constrain Rust's unwinding implementation. 147 | 148 | ### Participation in the project group 149 | 150 | Like any Rust group, the FFI-unwind project group intends to operate 151 | in a public and open fashion and welcomes participation. Visit the 152 | [repository][ffi-unwind project] for more details. 153 | 154 | # Drawbacks 155 | [drawbacks]: #drawbacks 156 | 157 | * The adoption of project groups for major language design efforts is a change 158 | in the status quo. We believe that this change will be an improvement over 159 | the current RFC-centric process, but we should be wary of unintended 160 | consequences of from such a change. 161 | * [Stabilization of "TBD" features][stabilizing-tbd] may be surprising or 162 | confusing to users, and it will encourage reliance on (some) unspecified 163 | behavior. 164 | 165 | # Prior art 166 | [prior-art]: #prior-art 167 | 168 | Although the term "project group" is new, some existing efforts, such as the 169 | Unsafe Code Guidelines effort and the work around defining const evaluation, 170 | were organized in a similar fashion. 171 | 172 | In addition to the [blog post Niko Matsakis][shepherds-3.0] about 173 | shepherding, James Munns wrote a [more formal shepherding 174 | proposal][shepherding-3.1]. 175 | 176 | The [governance WG][governance-wg] and [lang-team meta working 177 | group][lang-meta-wg] were both formed at least in part to improve the process 178 | for large-scale design efforts. One existing proposal is for ["staged 179 | RFCs"][staged-rfc]; this may be considered a precursor to the current 180 | "shepherded project group" proposal. 181 | 182 | 183 | # Unresolved questions and Future possibilities 184 | [unresolved-questions]: #unresolved-questions 185 | 186 | Since this RFC merely formalizes the creation of the project group, it 187 | intentionally leaves all technical details within the project's scope 188 | unresolved. 189 | 190 | # Future possibilities 191 | [future-possibilities]: #future-possibilities 192 | 193 | The project group will start with a fairly [limited scope][charter], but if the 194 | initial effort to design and stabilize a safe cross-language unwinding feature 195 | on a limited set of platforms goes well, there are many related areas of 196 | potential exploration. Three noteworthy examples are: 197 | 198 | * Catching foreign unwinding (e.g. Rust catching C++ exceptions, or C++ 199 | catching Rust `panic`s) 200 | * Defining subtype relationships among `fn`s using ABIs with different `unwind` 201 | behavior 202 | * Monitoring progress, or even participating in discussion about, the [ISO C and 203 | C++ proposal][c-cpp-unified-proposal] for cross-language error handling 204 | 205 | [Undefined Behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html 206 | [abort-unwind]: https://github.com/rust-lang/rust/issues/52652 207 | [ffi-unwind project]: https://github.com/rust-lang/project-ffi-unwind 208 | [shepherds-3.0]: http://smallcultfollowing.com/babysteps/blog/2019/09/11/aic-shepherds-3-0/ 209 | [c-cpp-unified-proposal]: http://open-std.org/JTC1/SC22/WG21/docs/papers/2018/p1095r0.pdf 210 | [shepherding-3.1]: https://jamesmunns.com/blog/shepherding-3-1/ 211 | [governance-wg]: https://github.com/rust-lang/wg-governance 212 | [lang-meta-wg]: https://github.com/rust-lang/lang-team/tree/master/working-groups/meta 213 | [staged-rfc]: http://smallcultfollowing.com/babysteps/blog/2018/06/20/proposal-for-a-staged-rfc-process/ 214 | -------------------------------------------------------------------------------- /terminology/misc.md: -------------------------------------------------------------------------------- 1 | # Miscellaneous terminology 2 | 3 | ## Native vs foreign 4 | 5 | [Zulip conversation][zulip] 6 | 7 | We will avoid the use of the term "native (stack) frames", since it is 8 | ambiguous. Instead, we will refer to "Rust frames" versus "foreign frames", 9 | where "foreign" will mean stack frames from languages other than Rust. 10 | 11 | We will still use the word "native" to describe the exception _mechanism_, 12 | which is distinct from "foreign" exception _objects_ (which means exception 13 | objects created in other languages). 14 | 15 | [zulip]: https://rust-lang.zulipchat.com/#narrow/stream/210922-wg-ffi-unwind/topic/Native.20frame.20definition/near/178713063 16 | -------------------------------------------------------------------------------- /terminology/spec-terminology.md: -------------------------------------------------------------------------------- 1 | # Terminology about specifications 2 | 3 | Language and platform specifications have several different terms used 4 | to describe how well-defined a language feature is, i.e., how well 5 | constrained the runtime behavior is. In cases where our terminology 6 | overlaps with that from other communities, we try to remain generally 7 | compatible. 8 | 9 | 10 | 11 | ## Undefined Behavior 12 | 13 | As is typical within the Rust community we use the phrase **undefined 14 | behavior** to refer to illegal program actions that can result in 15 | arbitrary results. In short, "undefined behavior" is always a bug and 16 | never something you should do. See the [Rust 17 | reference](https://doc.rust-lang.org/reference/behavior-considered-undefined.html) 18 | and the [entry in the Unsafe Code Guidelines glossary][ucg-ub] for more details. 19 | 20 | [ucg-ub]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#undefined-behavior 21 | 22 | Our usage of the term is generally the same as the [standard 23 | usage](https://en.wikipedia.org/wiki/Undefined_behavior) from other 24 | languages. 25 | 26 | 27 | 28 | ## LLVM-undefined behavior (LLVM-UB) 29 | 30 | We use the phrase **LLVM undefined behavior** to indicate things that 31 | are considered undefined behavior by LLVM itself. Barring bugs, the 32 | Rust compiler should never produce LLVM IR that contains LLVM-UB 33 | unless the behavior in question is *also* UB in Rust. Of course, there 34 | are bugs in the Rust compiler from time to time, and hence it can 35 | happen that we generate LLVM IR which contains LLVM-UB even if the 36 | corresponding Rust source code is meant to be fully defined (see 37 | e.g. [rust-lang/rust#28728]). The main reason it is worth separating 38 | LLVM-UB from the more general form of Rust UB is that, while both 39 | forms of UB can cause arbitrary things to happen in your 40 | code. However, as a practical measure, LLVM-UB is much more *likely 41 | to* in practice. 42 | 43 | [rust-lang/rust#28728]: https://github.com/rust-lang/rust/issues/28728 44 | 45 | 46 | 47 | ## Unspecified behavior 48 | 49 | We use the term "unspecified behavior" to refer to behavior that may 50 | vary across Rust releases, depending on what options are given to the 51 | compiler, or even -- in extreme cases -- across executions of the Rust 52 | compiler. However, unlike undefined behavior, the resulting execution 53 | is not completely undefined, and it must typically fall within some 54 | range of possibilities. Often, we will not specify precisely *how* 55 | something is implemented, but rather the patterns that must work. 56 | 57 | An example of "unspecified behavior" is the [layout for structs with 58 | no declared `#[repr]` attribute][ucg-struct]. This layout can and 59 | does change across Rust releases -- but of course within a given 60 | compilation, a struct must have *some* layout. Moreover, we guarantee 61 | that programs can (for example) use `sizeof` to determine the size of 62 | that layout, or access fields using Rust syntax like `foo.bar`. This 63 | requires the layout to be communicated in some fashion but doesn't 64 | specify how that is done. 65 | 66 | [ucg-struct]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/reference/src/layout/structs-and-tuples.md 67 | 68 | Our usage of the term is generally the same as the [standard 69 | usage](https://en.wikipedia.org/wiki/Unspecified_behavior) from other 70 | languages. 71 | 72 | 73 | 74 | ### To Be Defined Behavior (TBD) 75 | 76 | Internally to the ffi-unwind project group, we refer to a subset of 77 | Unspecified Behavior as **to be defined** or **TBD**. This simply 78 | means that this is behavior that we **expect** to specify as part of 79 | this group. 80 | 81 | This designation is not part of the "Rust reference" -- for "official" 82 | purposes, all TBD behavior is simply unspecified. In particular, if 83 | you find yourself relying on it, don't be surprised if it changes. 84 | But when writing official documentation, it is recommended to link to 85 | the ffi-unwind repository with a note like "this behavior is in the 86 | process of being specified; see the ffi-unwind repository for more 87 | details". 88 | 89 | -------------------------------------------------------------------------------- /use-cases.md: -------------------------------------------------------------------------------- 1 | # fastly use case 2 | 3 | ``` 4 | ... 5 | catch_unwind - 6 | Rust | 7 | Native frames| 8 | Rust ! ------+ 9 | 10 | ``` 11 | 12 | The native frames "speak DWARF" and have no landing pads registered. 13 | Important thing is that "native frames" do not try to intercept the 14 | panic, they just want it to "pass through". 15 | 16 | Requirement: 17 | 18 | * some way to translate a Rust panic into native system 19 | * some way to convert back 20 | 21 | # lua use case 22 | 23 | ``` 24 | ... 25 | setjmp - 26 | Rust | 27 | Native frames | 28 | Rust longjmp ----+ 29 | 30 | ``` 31 | 32 | The native frames "speak DWARF" and have no landing pads registered. 33 | Important thing is that "native frames" do not try to intercept the 34 | panic, they just want it to "pass through". 35 | 36 | Requirement: 37 | 38 | * some way to translate a Rust panic into native system 39 | * some way to convert back 40 | # more generally 41 | 42 | --------------------------------------------------------------------------------