Skip to content

Commit f4a85dc

Browse files
Merge #289
289: Make it unsafe to define NMI handlers r=therealprof a=jonas-schievink Reverts rust-embedded/cortex-m-rt#257 Fixes rust-embedded/cortex-m-rt#269 Fixes #196 (see that thread for details) Co-authored-by: Jonas Schievink <[email protected]>
2 parents cbb3c4e + bb868f4 commit f4a85dc

12 files changed

+105
-59
lines changed

cortex-m-rt/examples/divergent-default-handler.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#![deny(unsafe_code)]
21
#![deny(warnings)]
32
#![no_main]
43
#![no_std]
@@ -14,6 +13,6 @@ fn foo() -> ! {
1413
}
1514

1615
#[exception]
17-
fn DefaultHandler(_irqn: i16) -> ! {
16+
unsafe fn DefaultHandler(_irqn: i16) -> ! {
1817
loop {}
1918
}

cortex-m-rt/examples/override-exception.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//! How to override the hard fault exception handler and the default exception handler
22
3-
#![deny(unsafe_code)]
43
#![deny(warnings)]
54
#![no_main]
65
#![no_std]
@@ -18,12 +17,12 @@ fn main() -> ! {
1817
}
1918

2019
#[exception]
21-
fn DefaultHandler(_irqn: i16) {
20+
unsafe fn DefaultHandler(_irqn: i16) {
2221
asm::bkpt();
2322
}
2423

2524
#[exception]
26-
fn HardFault(_ef: &ExceptionFrame) -> ! {
25+
unsafe fn HardFault(_ef: &ExceptionFrame) -> ! {
2726
asm::bkpt();
2827

2928
loop {}

cortex-m-rt/macros/src/lib.rs

+30-12
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
113113
.into()
114114
}
115115

116+
#[derive(Debug, PartialEq)]
117+
enum Exception {
118+
DefaultHandler,
119+
HardFault,
120+
NonMaskableInt,
121+
Other,
122+
}
123+
116124
#[proc_macro_attribute]
117125
pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
118126
let mut f = parse_macro_input!(input as ItemFn);
@@ -130,28 +138,38 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
130138
let fspan = f.span();
131139
let ident = f.sig.ident.clone();
132140

133-
enum Exception {
134-
DefaultHandler,
135-
HardFault,
136-
Other,
137-
}
138-
139141
let ident_s = ident.to_string();
140142
let exn = match &*ident_s {
141143
"DefaultHandler" => Exception::DefaultHandler,
142144
"HardFault" => Exception::HardFault,
145+
"NonMaskableInt" => Exception::NonMaskableInt,
143146
// NOTE that at this point we don't check if the exception is available on the target (e.g.
144147
// MemoryManagement is not available on Cortex-M0)
145-
"NonMaskableInt" | "MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault"
146-
| "SVCall" | "DebugMonitor" | "PendSV" | "SysTick" => Exception::Other,
148+
"MemoryManagement" | "BusFault" | "UsageFault" | "SecureFault" | "SVCall"
149+
| "DebugMonitor" | "PendSV" | "SysTick" => Exception::Other,
147150
_ => {
148151
return parse::Error::new(ident.span(), "This is not a valid exception name")
149152
.to_compile_error()
150153
.into();
151154
}
152155
};
153156

154-
// XXX should we blacklist other attributes?
157+
if f.sig.unsafety.is_none() {
158+
match exn {
159+
Exception::DefaultHandler | Exception::HardFault | Exception::NonMaskableInt => {
160+
// These are unsafe to define.
161+
let name = if exn == Exception::DefaultHandler {
162+
format!("`DefaultHandler`")
163+
} else {
164+
format!("`{:?}` handler", exn)
165+
};
166+
return parse::Error::new(ident.span(), format_args!("defining a {} is unsafe and requires an `unsafe fn` (see the cortex-m-rt docs)", name))
167+
.to_compile_error()
168+
.into();
169+
}
170+
Exception::Other => {}
171+
}
172+
}
155173

156174
match exn {
157175
Exception::DefaultHandler => {
@@ -174,7 +192,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
174192
if !valid_signature {
175193
return parse::Error::new(
176194
fspan,
177-
"`DefaultHandler` must have signature `[unsafe] fn(i16) [-> !]`",
195+
"`DefaultHandler` must have signature `unsafe fn(i16) [-> !]`",
178196
)
179197
.to_compile_error()
180198
.into();
@@ -231,7 +249,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
231249
if !valid_signature {
232250
return parse::Error::new(
233251
fspan,
234-
"`HardFault` handler must have signature `[unsafe] fn(&ExceptionFrame) -> !`",
252+
"`HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`",
235253
)
236254
.to_compile_error()
237255
.into();
@@ -257,7 +275,7 @@ pub fn exception(args: TokenStream, input: TokenStream) -> TokenStream {
257275
)
258276
.into()
259277
}
260-
Exception::Other => {
278+
Exception::NonMaskableInt | Exception::Other => {
261279
let valid_signature = f.sig.constness.is_none()
262280
&& f.vis == Visibility::Inherited
263281
&& f.sig.abi.is_none()

cortex-m-rt/src/lib.rs

+35-29
Original file line numberDiff line numberDiff line change
@@ -199,15 +199,14 @@
199199
//! won't find it.
200200
//!
201201
//! - `DefaultHandler`. This is the default handler. If not overridden using `#[exception] fn
202-
//! DefaultHandler(..` this will cause a panic with the message "DefaultHandler #`i`", where `i` is
203-
//! the number of the interrupt handler.
202+
//! DefaultHandler(..` this will be an infinite loop.
204203
//!
205204
//! - `HardFaultTrampoline`. This is the real hard fault handler. This function is simply a
206205
//! trampoline that jumps into the user defined hard fault handler named `HardFault`. The
207206
//! trampoline is required to set up the pointer to the stacked exception frame.
208207
//!
209208
//! - `HardFault`. This is the user defined hard fault handler. If not overridden using
210-
//! `#[exception] fn HardFault(..` it will default to a panic with message "HardFault".
209+
//! `#[exception] fn HardFault(..` it will default to an infinite loop.
211210
//!
212211
//! - `__STACK_START`. This is the first entry in the `.vector_table` section. This symbol contains
213212
//! the initial value of the stack pointer; this is where the stack will be located -- the stack
@@ -442,6 +441,7 @@ extern crate cortex_m_rt_macros as macros;
442441
extern crate r0;
443442

444443
use core::fmt;
444+
use core::sync::atomic::{self, Ordering};
445445

446446
/// Attribute to declare an interrupt (AKA device-specific exception) handler
447447
///
@@ -612,13 +612,13 @@ pub use macros::entry;
612612
///
613613
/// # Usage
614614
///
615-
/// `#[exception] fn HardFault(..` sets the hard fault handler. The handler must have signature
616-
/// `[unsafe] fn(&ExceptionFrame) -> !`. This handler is not allowed to return as that can cause
617-
/// undefined behavior.
615+
/// `#[exception] unsafe fn HardFault(..` sets the hard fault handler. The handler must have
616+
/// signature `unsafe fn(&ExceptionFrame) -> !`. This handler is not allowed to return as that can
617+
/// cause undefined behavior.
618618
///
619-
/// `#[exception] fn DefaultHandler(..` sets the *default* handler. All exceptions which have not
620-
/// been assigned a handler will be serviced by this handler. This handler must have signature
621-
/// `[unsafe] fn(irqn: i16) [-> !]`. `irqn` is the IRQ number (See CMSIS); `irqn` will be a negative
619+
/// `#[exception] unsafe fn DefaultHandler(..` sets the *default* handler. All exceptions which have
620+
/// not been assigned a handler will be serviced by this handler. This handler must have signature
621+
/// `unsafe fn(irqn: i16) [-> !]`. `irqn` is the IRQ number (See CMSIS); `irqn` will be a negative
622622
/// number when the handler is servicing a core exception; `irqn` will be a positive number when the
623623
/// handler is servicing a device specific exception (interrupt).
624624
///
@@ -637,31 +637,33 @@ pub use macros::entry;
637637
/// the attribute will help by making a transformation to the source code: for this reason a
638638
/// variable like `static mut FOO: u32` will become `let FOO: &mut u32;`.
639639
///
640-
/// # Examples
640+
/// # Safety
641641
///
642-
/// - Setting the `HardFault` handler
642+
/// It is not generally safe to register handlers for non-maskable interrupts. On Cortex-M,
643+
/// `HardFault` is non-maskable (at least in general), and there is an explicitly non-maskable
644+
/// interrupt `NonMaskableInt`.
643645
///
644-
/// ```
645-
/// # extern crate cortex_m_rt;
646-
/// # extern crate cortex_m_rt_macros;
647-
/// use cortex_m_rt::{ExceptionFrame, exception};
646+
/// The reason for that is that non-maskable interrupts will preempt any currently running function,
647+
/// even if that function executes within a critical section. Thus, if it was safe to define NMI
648+
/// handlers, critical sections wouldn't work safely anymore.
648649
///
649-
/// #[exception]
650-
/// fn HardFault(ef: &ExceptionFrame) -> ! {
651-
/// // prints the exception frame as a panic message
652-
/// panic!("{:#?}", ef);
653-
/// }
650+
/// This also means that defining a `DefaultHandler` must be unsafe, as that will catch
651+
/// `NonMaskableInt` and `HardFault` if no handlers for those are defined.
654652
///
655-
/// # fn main() {}
656-
/// ```
653+
/// The safety requirements on those handlers is as follows: The handler must not access any data
654+
/// that is protected via a critical section and shared with other interrupts that may be preempted
655+
/// by the NMI while holding the critical section. As long as this requirement is fulfilled, it is
656+
/// safe to handle NMIs.
657+
///
658+
/// # Examples
657659
///
658660
/// - Setting the default handler
659661
///
660662
/// ```
661663
/// use cortex_m_rt::exception;
662664
///
663665
/// #[exception]
664-
/// fn DefaultHandler(irqn: i16) {
666+
/// unsafe fn DefaultHandler(irqn: i16) {
665667
/// println!("IRQn = {}", irqn);
666668
/// }
667669
///
@@ -990,17 +992,21 @@ pub unsafe extern "C" fn Reset() -> ! {
990992
#[link_section = ".HardFault.default"]
991993
#[no_mangle]
992994
pub unsafe extern "C" fn HardFault_(ef: &ExceptionFrame) -> ! {
993-
panic!("HardFault");
995+
loop {
996+
// add some side effect to prevent this from turning into a UDF instruction
997+
// see rust-lang/rust#28728 for details
998+
atomic::compiler_fence(Ordering::SeqCst);
999+
}
9941000
}
9951001

9961002
#[doc(hidden)]
9971003
#[no_mangle]
9981004
pub unsafe extern "C" fn DefaultHandler_() -> ! {
999-
const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;
1000-
1001-
let irqn = core::ptr::read(SCB_ICSR) as u8 as i16 - 16;
1002-
1003-
panic!("DefaultHandler #{}", irqn);
1005+
loop {
1006+
// add some side effect to prevent this from turning into a UDF instruction
1007+
// see rust-lang/rust#28728 for details
1008+
atomic::compiler_fence(Ordering::SeqCst);
1009+
}
10041010
}
10051011

10061012
#[doc(hidden)]

cortex-m-rt/tests/compile-fail/default-handler-bad-signature-1.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ fn foo() -> ! {
1212
}
1313

1414
#[exception]
15-
fn DefaultHandler(_irqn: i16, undef: u32) {}
16-
//~^ ERROR `DefaultHandler` must have signature `[unsafe] fn(i16) [-> !]`
15+
unsafe fn DefaultHandler(_irqn: i16, undef: u32) {}
16+
//~^ ERROR `DefaultHandler` must have signature `unsafe fn(i16) [-> !]`

cortex-m-rt/tests/compile-fail/default-handler-bad-signature-2.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ fn foo() -> ! {
1212
}
1313

1414
#[exception]
15-
fn DefaultHandler(_irqn: i16) -> u32 {
16-
//~^ ERROR `DefaultHandler` must have signature `[unsafe] fn(i16) [-> !]`
15+
unsafe fn DefaultHandler(_irqn: i16) -> u32 {
16+
//~^ ERROR `DefaultHandler` must have signature `unsafe fn(i16) [-> !]`
1717
0
1818
}

cortex-m-rt/tests/compile-fail/default-handler-hidden.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ mod hidden {
1818
use cortex_m_rt::exception;
1919

2020
#[exception]
21-
fn DefaultHandler(_irqn: i16) {}
21+
unsafe fn DefaultHandler(_irqn: i16) {}
2222
}

cortex-m-rt/tests/compile-fail/default-handler-twice.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ fn foo() -> ! {
1212
}
1313

1414
#[exception]
15-
fn DefaultHandler(_irqn: i16) {}
15+
unsafe fn DefaultHandler(_irqn: i16) {}
1616

1717
pub mod reachable {
1818
use cortex_m_rt::exception;
1919

2020
#[exception] //~ ERROR symbol `DefaultHandler` is already defined
21-
fn DefaultHandler(_irqn: i16) {}
21+
unsafe fn DefaultHandler(_irqn: i16) {}
2222
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#![no_main]
2+
#![no_std]
3+
4+
extern crate cortex_m_rt;
5+
extern crate panic_halt;
6+
7+
use cortex_m_rt::{entry, exception};
8+
9+
#[entry]
10+
fn foo() -> ! {
11+
loop {}
12+
}
13+
14+
#[exception]
15+
fn DefaultHandler(_irq: i16) {}
16+
//~^ ERROR defining a `DefaultHandler` is unsafe and requires an `unsafe fn`
17+
18+
#[exception]
19+
fn HardFault() {}
20+
//~^ ERROR defining a `HardFault` handler is unsafe and requires an `unsafe fn`
21+
22+
#[exception]
23+
fn NonMaskableInt() {}
24+
//~^ ERROR defining a `NonMaskableInt` handler is unsafe and requires an `unsafe fn`

cortex-m-rt/tests/compile-fail/hard-fault-bad-signature-1.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ fn foo() -> ! {
1212
}
1313

1414
#[exception]
15-
fn HardFault(_ef: &ExceptionFrame, undef: u32) -> ! {
16-
//~^ ERROR `HardFault` handler must have signature `[unsafe] fn(&ExceptionFrame) -> !`
15+
unsafe fn HardFault(_ef: &ExceptionFrame, undef: u32) -> ! {
16+
//~^ ERROR `HardFault` handler must have signature `unsafe fn(&ExceptionFrame) -> !`
1717
loop {}
1818
}

cortex-m-rt/tests/compile-fail/hard-fault-twice.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ fn foo() -> ! {
1212
}
1313

1414
#[exception]
15-
fn HardFault(_ef: &ExceptionFrame) -> ! {
15+
unsafe fn HardFault(_ef: &ExceptionFrame) -> ! {
1616
loop {}
1717
}
1818

1919
pub mod reachable {
2020
use cortex_m_rt::{exception, ExceptionFrame};
2121

2222
#[exception] //~ ERROR symbol `HardFault` is already defined
23-
fn HardFault(_ef: &ExceptionFrame) -> ! {
23+
unsafe fn HardFault(_ef: &ExceptionFrame) -> ! {
2424
loop {}
2525
}
2626
}

cortex-m-rt/tests/compile-fail/unsafe-init-static.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ fn SVCall() {
2929
}
3030

3131
#[exception]
32-
fn DefaultHandler(_irq: i16) {
32+
unsafe fn DefaultHandler(_irq: i16) {
3333
static mut X: u32 = init(); //~ ERROR requires unsafe
3434
}
3535

3636
#[exception]
37-
fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! {
37+
unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! {
3838
static mut X: u32 = init(); //~ ERROR requires unsafe
3939
loop {}
4040
}

0 commit comments

Comments
 (0)