Skip to content

Commit 25b7648

Browse files
committed
Auto merge of rust-lang#86155 - alexcrichton:abort-on-unwind, r=nikomatsakis
rustc: Fill out remaining parts of C-unwind ABI This commit intends to fill out some of the remaining pieces of the C-unwind ABI. This has a number of other changes with it though to move this design space forward a bit. Notably contained within here is: * On `panic=unwind`, the `extern "C"` ABI is now considered as "may unwind". This fixes a longstanding soundness issue where if you `panic!()` in an `extern "C"` function defined in Rust that's actually UB because the LLVM representation for the function has the `nounwind` attribute, but then you unwind. * Whether or not a function unwinds now mainly considers the ABI of the function instead of first checking the panic strategy. This fixes a miscompile of `extern "C-unwind"` with `panic=abort` because that ABI can still unwind. * The aborting stub for non-unwinding ABIs with `panic=unwind` has been reimplemented. Previously this was done as a small tweak during MIR generation, but this has been moved to a separate and dedicated MIR pass. This new pass will, for appropriate functions and function calls, insert a `cleanup` landing pad for any function call that may unwind within a function that is itself not allowed to unwind. Note that this subtly changes some behavior from before where previously on an unwind which was caught-to-abort it would run active destructors in the function, and now it simply immediately aborts the process. * The `#[unwind]` attribute has been removed and all users in tests and such are now using `C-unwind` and `#![feature(c_unwind)]`. I think this is largely the last piece of the RFC to implement. Unfortunately I believe this is still not stabilizable as-is because activating the feature gate changes the behavior of the existing `extern "C"` ABI in a way that has no replacement. My thinking for how to enable this is that we add support for the `C-unwind` ABI on stable Rust first, and then after it hits stable we change the behavior of the `C` ABI. That way anyone straddling stable/beta/nightly can switch to `C-unwind` safely.
2 parents d54fbb9 + bb68c66 commit 25b7648

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+560
-593
lines changed

compiler/rustc_attr/src/builtin.rs

-44
Original file line numberDiff line numberDiff line change
@@ -87,50 +87,6 @@ pub enum OptimizeAttr {
8787
Size,
8888
}
8989

90-
#[derive(Copy, Clone, PartialEq)]
91-
pub enum UnwindAttr {
92-
Allowed,
93-
Aborts,
94-
}
95-
96-
/// Determine what `#[unwind]` attribute is present in `attrs`, if any.
97-
pub fn find_unwind_attr(sess: &Session, attrs: &[Attribute]) -> Option<UnwindAttr> {
98-
attrs.iter().fold(None, |ia, attr| {
99-
if sess.check_name(attr, sym::unwind) {
100-
if let Some(meta) = attr.meta() {
101-
if let MetaItemKind::List(items) = meta.kind {
102-
if items.len() == 1 {
103-
if items[0].has_name(sym::allowed) {
104-
return Some(UnwindAttr::Allowed);
105-
} else if items[0].has_name(sym::aborts) {
106-
return Some(UnwindAttr::Aborts);
107-
}
108-
}
109-
110-
struct_span_err!(
111-
sess.diagnostic(),
112-
attr.span,
113-
E0633,
114-
"malformed `unwind` attribute input"
115-
)
116-
.span_label(attr.span, "invalid argument")
117-
.span_suggestions(
118-
attr.span,
119-
"the allowed arguments are `allowed` and `aborts`",
120-
(vec!["allowed", "aborts"])
121-
.into_iter()
122-
.map(|s| format!("#[unwind({})]", s)),
123-
Applicability::MachineApplicable,
124-
)
125-
.emit();
126-
}
127-
}
128-
}
129-
130-
ia
131-
})
132-
}
133-
13490
/// Represents the following attributes:
13591
///
13692
/// - `#[stable]`

compiler/rustc_error_codes/src/error_codes/E0633.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
#### Note: this error code is no longer emitted by the compiler.
2+
13
The `unwind` attribute was malformed.
24

35
Erroneous code example:
46

5-
```compile_fail,E0633
7+
```compile_fail
68
#![feature(unwind_attributes)]
79
810
#[unwind()] // error: expected one argument

compiler/rustc_feature/src/active.rs

-5
Original file line numberDiff line numberDiff line change
@@ -311,11 +311,6 @@ declare_features! (
311311
/// Allows `extern "platform-intrinsic" { ... }`.
312312
(active, platform_intrinsics, "1.4.0", Some(27731), None),
313313

314-
/// Allows `#[unwind(..)]`.
315-
///
316-
/// Permits specifying whether a function should permit unwinding or abort on unwind.
317-
(active, unwind_attributes, "1.4.0", Some(58760), None),
318-
319314
/// Allows attributes on expressions and non-item statements.
320315
(active, stmt_expr_attributes, "1.6.0", Some(15701), None),
321316

compiler/rustc_feature/src/builtin_attrs.rs

-4
Original file line numberDiff line numberDiff line change
@@ -419,10 +419,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
419419
),
420420
gated!(panic_runtime, AssumedUsed, template!(Word), experimental!(panic_runtime)),
421421
gated!(needs_panic_runtime, AssumedUsed, template!(Word), experimental!(needs_panic_runtime)),
422-
gated!(
423-
unwind, AssumedUsed, template!(List: "allowed|aborts"), unwind_attributes,
424-
experimental!(unwind),
425-
),
426422
gated!(
427423
compiler_builtins, AssumedUsed, template!(Word),
428424
"the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \

compiler/rustc_feature/src/removed.rs

+5
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ declare_features! (
156156
(removed, min_type_alias_impl_trait, "1.56.0", Some(63063), None,
157157
Some("removed in favor of full type_alias_impl_trait")),
158158

159+
/// Allows `#[unwind(..)]`.
160+
///
161+
/// Permits specifying whether a function should permit unwinding or abort on unwind.
162+
(removed, unwind_attributes, "1.56.0", Some(58760), None, Some("use the C-unwind ABI instead")),
163+
159164
// -------------------------------------------------------------------------
160165
// feature-group-end: removed features
161166
// -------------------------------------------------------------------------

compiler/rustc_middle/src/middle/codegen_fn_attrs.rs

+3-7
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,9 @@ bitflags! {
5252
/// `#[rustc_allocator]`: a hint to LLVM that the pointer returned from this
5353
/// function is never null.
5454
const ALLOCATOR = 1 << 1;
55-
/// `#[unwind]`: an indicator that this function may unwind despite what
56-
/// its ABI signature may otherwise imply.
57-
const UNWIND = 1 << 2;
58-
/// `#[rust_allocator_nounwind]`, an indicator that an imported FFI
59-
/// function will never unwind. Probably obsolete by recent changes with
60-
/// #[unwind], but hasn't been removed/migrated yet
61-
const RUSTC_ALLOCATOR_NOUNWIND = 1 << 3;
55+
/// An indicator that function will never unwind. Will become obsolete
56+
/// once C-unwind is fully stabilized.
57+
const NEVER_UNWIND = 1 << 3;
6258
/// `#[naked]`: an indicator to LLVM that no function prologue/epilogue
6359
/// should be generated.
6460
const NAKED = 1 << 4;

compiler/rustc_middle/src/mir/terminator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ pub enum TerminatorKind<'tcx> {
237237
/// consider it in borrowck. We don't want to accept programs which
238238
/// pass borrowck only when `panic=abort` or some assertions are disabled
239239
/// due to release vs. debug mode builds. This needs to be an `Option` because
240-
/// of the `remove_noop_landing_pads` and `no_landing_pads` passes.
240+
/// of the `remove_noop_landing_pads` and `abort_unwinding_calls` passes.
241241
unwind: Option<BasicBlock>,
242242
},
243243

compiler/rustc_middle/src/ty/layout.rs

+115-66
Original file line numberDiff line numberDiff line change
@@ -2601,65 +2601,124 @@ where
26012601
fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi);
26022602
}
26032603

2604+
/// Calculates whether a function's ABI can unwind or not.
2605+
///
2606+
/// This takes two primary parameters:
2607+
///
2608+
/// * `codegen_fn_attr_flags` - these are flags calculated as part of the
2609+
/// codegen attrs for a defined function. For function pointers this set of
2610+
/// flags is the empty set. This is only applicable for Rust-defined
2611+
/// functions, and generally isn't needed except for small optimizations where
2612+
/// we try to say a function which otherwise might look like it could unwind
2613+
/// doesn't actually unwind (such as for intrinsics and such).
2614+
///
2615+
/// * `abi` - this is the ABI that the function is defined with. This is the
2616+
/// primary factor for determining whether a function can unwind or not.
2617+
///
2618+
/// Note that in this case unwinding is not necessarily panicking in Rust. Rust
2619+
/// panics are implemented with unwinds on most platform (when
2620+
/// `-Cpanic=unwind`), but this also accounts for `-Cpanic=abort` build modes.
2621+
/// Notably unwinding is disallowed for more non-Rust ABIs unless it's
2622+
/// specifically in the name (e.g. `"C-unwind"`). Unwinding within each ABI is
2623+
/// defined for each ABI individually, but it always corresponds to some form of
2624+
/// stack-based unwinding (the exact mechanism of which varies
2625+
/// platform-by-platform).
2626+
///
2627+
/// Rust functions are classfied whether or not they can unwind based on the
2628+
/// active "panic strategy". In other words Rust functions are considered to
2629+
/// unwind in `-Cpanic=unwind` mode and cannot unwind in `-Cpanic=abort` mode.
2630+
/// Note that Rust supports intermingling panic=abort and panic=unwind code, but
2631+
/// only if the final panic mode is panic=abort. In this scenario any code
2632+
/// previously compiled assuming that a function can unwind is still correct, it
2633+
/// just never happens to actually unwind at runtime.
2634+
///
2635+
/// This function's answer to whether or not a function can unwind is quite
2636+
/// impactful throughout the compiler. This affects things like:
2637+
///
2638+
/// * Calling a function which can't unwind means codegen simply ignores any
2639+
/// associated unwinding cleanup.
2640+
/// * Calling a function which can unwind from a function which can't unwind
2641+
/// causes the `abort_unwinding_calls` MIR pass to insert a landing pad that
2642+
/// aborts the process.
2643+
/// * This affects whether functions have the LLVM `nounwind` attribute, which
2644+
/// affects various optimizations and codegen.
2645+
///
2646+
/// FIXME: this is actually buggy with respect to Rust functions. Rust functions
2647+
/// compiled with `-Cpanic=unwind` and referenced from another crate compiled
2648+
/// with `-Cpanic=abort` will look like they can't unwind when in fact they
2649+
/// might (from a foreign exception or similar).
26042650
pub fn fn_can_unwind(
2605-
panic_strategy: PanicStrategy,
2651+
tcx: TyCtxt<'tcx>,
26062652
codegen_fn_attr_flags: CodegenFnAttrFlags,
2607-
call_conv: Conv,
26082653
abi: SpecAbi,
26092654
) -> bool {
2610-
if panic_strategy != PanicStrategy::Unwind {
2611-
// In panic=abort mode we assume nothing can unwind anywhere, so
2612-
// optimize based on this!
2613-
false
2614-
} else if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::UNWIND) {
2615-
// If a specific #[unwind] attribute is present, use that.
2616-
true
2617-
} else if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) {
2618-
// Special attribute for allocator functions, which can't unwind.
2619-
false
2620-
} else {
2621-
if call_conv == Conv::Rust {
2622-
// Any Rust method (or `extern "Rust" fn` or `extern
2623-
// "rust-call" fn`) is explicitly allowed to unwind
2624-
// (unless it has no-unwind attribute, handled above).
2625-
true
2626-
} else {
2627-
// Anything else is either:
2628-
//
2629-
// 1. A foreign item using a non-Rust ABI (like `extern "C" { fn foo(); }`), or
2630-
//
2631-
// 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`).
2632-
//
2633-
// In both of these cases, we should refer to the ABI to determine whether or not we
2634-
// should unwind. See Rust RFC 2945 for more information on this behavior, here:
2635-
// https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
2636-
use SpecAbi::*;
2637-
match abi {
2638-
C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
2639-
unwind
2640-
}
2641-
Cdecl
2642-
| Fastcall
2643-
| Vectorcall
2644-
| Aapcs
2645-
| Win64
2646-
| SysV64
2647-
| PtxKernel
2648-
| Msp430Interrupt
2649-
| X86Interrupt
2650-
| AmdGpuKernel
2651-
| EfiApi
2652-
| AvrInterrupt
2653-
| AvrNonBlockingInterrupt
2654-
| CCmseNonSecureCall
2655-
| Wasm
2656-
| RustIntrinsic
2657-
| PlatformIntrinsic
2658-
| Unadjusted => false,
2659-
// In the `if` above, we checked for functions with the Rust calling convention.
2660-
Rust | RustCall => unreachable!(),
2661-
}
2655+
// Special attribute for functions which can't unwind.
2656+
if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::NEVER_UNWIND) {
2657+
return false;
2658+
}
2659+
2660+
// Otherwise if this isn't special then unwinding is generally determined by
2661+
// the ABI of the itself. ABIs like `C` have variants which also
2662+
// specifically allow unwinding (`C-unwind`), but not all platform-specific
2663+
// ABIs have such an option. Otherwise the only other thing here is Rust
2664+
// itself, and those ABIs are determined by the panic strategy configured
2665+
// for this compilation.
2666+
//
2667+
// Unfortunately at this time there's also another caveat. Rust [RFC
2668+
// 2945][rfc] has been accepted and is in the process of being implemented
2669+
// and stabilized. In this interim state we need to deal with historical
2670+
// rustc behavior as well as plan for future rustc behavior.
2671+
//
2672+
// Historically functions declared with `extern "C"` were marked at the
2673+
// codegen layer as `nounwind`. This happened regardless of `panic=unwind`
2674+
// or not. This is UB for functions in `panic=unwind` mode that then
2675+
// actually panic and unwind. Note that this behavior is true for both
2676+
// externally declared functions as well as Rust-defined function.
2677+
//
2678+
// To fix this UB rustc would like to change in the future to catch unwinds
2679+
// from function calls that may unwind within a Rust-defined `extern "C"`
2680+
// function and forcibly abort the process, thereby respecting the
2681+
// `nounwind` attribut emitted for `extern "C"`. This behavior change isn't
2682+
// ready to roll out, so determining whether or not the `C` family of ABIs
2683+
// unwinds is conditional not only on their definition but also whether the
2684+
// `#![feature(c_unwind)]` feature gate is active.
2685+
//
2686+
// Note that this means that unlike historical compilers rustc now, by
2687+
// default, unconditionally thinks that the `C` ABI may unwind. This will
2688+
// prevent some optimization opportunities, however, so we try to scope this
2689+
// change and only assume that `C` unwinds with `panic=unwind` (as opposed
2690+
// to `panic=abort`).
2691+
//
2692+
// Eventually the check against `c_unwind` here will ideally get removed and
2693+
// this'll be a little cleaner as it'll be a straightforward check of the
2694+
// ABI.
2695+
//
2696+
// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
2697+
use SpecAbi::*;
2698+
match abi {
2699+
C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
2700+
unwind
2701+
|| (!tcx.features().c_unwind && tcx.sess.panic_strategy() == PanicStrategy::Unwind)
26622702
}
2703+
Cdecl
2704+
| Fastcall
2705+
| Vectorcall
2706+
| Aapcs
2707+
| Win64
2708+
| SysV64
2709+
| PtxKernel
2710+
| Msp430Interrupt
2711+
| X86Interrupt
2712+
| AmdGpuKernel
2713+
| EfiApi
2714+
| AvrInterrupt
2715+
| AvrNonBlockingInterrupt
2716+
| CCmseNonSecureCall
2717+
| Wasm
2718+
| RustIntrinsic
2719+
| PlatformIntrinsic
2720+
| Unadjusted => false,
2721+
Rust | RustCall => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
26632722
}
26642723
}
26652724

@@ -2695,11 +2754,6 @@ pub fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi) -> Conv {
26952754
}
26962755
}
26972756

2698-
pub fn fn_ptr_codegen_fn_attr_flags() -> CodegenFnAttrFlags {
2699-
// Assume that fn pointers may always unwind
2700-
CodegenFnAttrFlags::UNWIND
2701-
}
2702-
27032757
impl<'tcx, C> FnAbiExt<'tcx, C> for call::FnAbi<'tcx, Ty<'tcx>>
27042758
where
27052759
C: LayoutOf<Ty = Ty<'tcx>, TyAndLayout = TyAndLayout<'tcx>>
@@ -2709,7 +2763,7 @@ where
27092763
+ HasParamEnv<'tcx>,
27102764
{
27112765
fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self {
2712-
call::FnAbi::new_internal(cx, sig, extra_args, None, fn_ptr_codegen_fn_attr_flags(), false)
2766+
call::FnAbi::new_internal(cx, sig, extra_args, None, CodegenFnAttrFlags::empty(), false)
27132767
}
27142768

27152769
fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self {
@@ -2901,12 +2955,7 @@ where
29012955
c_variadic: sig.c_variadic,
29022956
fixed_count: inputs.len(),
29032957
conv,
2904-
can_unwind: fn_can_unwind(
2905-
cx.tcx().sess.panic_strategy(),
2906-
codegen_fn_attr_flags,
2907-
conv,
2908-
sig.abi,
2909-
),
2958+
can_unwind: fn_can_unwind(cx.tcx(), codegen_fn_attr_flags, sig.abi),
29102959
};
29112960
fn_abi.adjust_for_abi(cx, sig.abi);
29122961
debug!("FnAbi::new_internal = {:?}", fn_abi);

compiler/rustc_mir/src/interpret/terminator.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,7 @@ use super::{
1818

1919
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
2020
fn fn_can_unwind(&self, attrs: CodegenFnAttrFlags, abi: Abi) -> bool {
21-
layout::fn_can_unwind(
22-
self.tcx.sess.panic_strategy(),
23-
attrs,
24-
layout::conv_from_spec_abi(*self.tcx, abi),
25-
abi,
26-
)
21+
layout::fn_can_unwind(*self.tcx, attrs, abi)
2722
}
2823

2924
pub(super) fn eval_terminator(
@@ -77,7 +72,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
7772
(
7873
fn_val,
7974
caller_abi,
80-
self.fn_can_unwind(layout::fn_ptr_codegen_fn_attr_flags(), caller_abi),
75+
self.fn_can_unwind(CodegenFnAttrFlags::empty(), caller_abi),
8176
)
8277
}
8378
ty::FnDef(def_id, substs) => {

compiler/rustc_mir/src/shim.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::fmt;
1616
use std::iter;
1717

1818
use crate::transform::{
19-
add_call_guards, add_moves_for_packed_drops, no_landing_pads, remove_noop_landing_pads,
19+
abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, remove_noop_landing_pads,
2020
run_passes, simplify,
2121
};
2222
use crate::util::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
@@ -81,10 +81,10 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
8181
MirPhase::Const,
8282
&[&[
8383
&add_moves_for_packed_drops::AddMovesForPackedDrops,
84-
&no_landing_pads::NoLandingPads,
8584
&remove_noop_landing_pads::RemoveNoopLandingPads,
8685
&simplify::SimplifyCfg::new("make_shim"),
8786
&add_call_guards::CriticalCallEdges,
87+
&abort_unwinding_calls::AbortUnwindingCalls,
8888
]],
8989
);
9090

0 commit comments

Comments
 (0)