Skip to content

Commit 42968fc

Browse files
committed
forbid sending interrupt tokens across interrupts
which would break the `ctxt::Local` abstraction by making `Mutex` `Sync` only if the protected data is `Send`. See the CHANGELOG for details. To fully fix the memory unsafety, svd2rust needs to be updated to mark interrupt tokens as `!Send`
1 parent 9d2d0a1 commit 42968fc

File tree

3 files changed

+115
-16
lines changed

3 files changed

+115
-16
lines changed

CHANGELOG.md

Lines changed: 105 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,97 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99

10+
## [v0.2.6] - 2017-05-08
11+
12+
### Fixed
13+
14+
- [breaking-change]. MEMORY UNSAFETY. `Mutex` could be used as a channel to send
15+
interrupt tokens from one interrupt to other thus breaking the context `Local`
16+
abstraction. See reproduction case below. This has been fixed by making
17+
`Mutex` `Sync` only if the protected data is `Send`.
18+
19+
``` rust
20+
#![feature(const_fn)]
21+
#![feature(used)]
22+
#![no_std]
23+
24+
extern crate cortex_m;
25+
extern crate cortex_m_rt;
26+
extern crate stm32f30x;
27+
28+
use core::cell::RefCell;
29+
30+
use cortex_m::ctxt::Local;
31+
use cortex_m::interrupt::Mutex;
32+
use stm32f30x::interrupt::{self, Exti0, Exti1};
33+
34+
fn main() {
35+
// ..
36+
37+
// trigger exti0
38+
// then trigger exti0 again
39+
}
40+
41+
static CHANNEL: Mutex<RefCell<Option<Exti0>>> = Mutex::new(RefCell::new(None));
42+
// Supposedly task *local* data
43+
static LOCAL: Local<i32, Exti0> = Local::new(0);
44+
45+
extern "C" fn exti0(mut ctxt: Exti0) {
46+
static FIRST: Local<bool, Exti0> = Local::new(true);
47+
48+
let first = *FIRST.borrow(&ctxt);
49+
50+
// toggle
51+
if first {
52+
*FIRST.borrow_mut(&mut ctxt) = false;
53+
}
54+
55+
if first {
56+
cortex_m::interrupt::free(
57+
|cs| {
58+
let channel = CHANNEL.borrow(cs);
59+
60+
// BAD: transfer interrupt token to another interrupt
61+
*channel.borrow_mut() = Some(ctxt);
62+
},
63+
);
64+
65+
return;
66+
}
67+
let _local = LOCAL.borrow_mut(&mut ctxt);
68+
69+
// ..
70+
71+
// trigger exti1 here
72+
73+
// ..
74+
75+
// `LOCAL` mutably borrowed up to this point
76+
}
77+
78+
extern "C" fn exti1(_ctxt: Exti1) {
79+
cortex_m::interrupt::free(|cs| {
80+
let channel = CHANNEL.borrow(cs);
81+
let mut channel = channel.borrow_mut();
82+
83+
if let Some(mut other_task) = channel.take() {
84+
// BAD: `exti1` has access to `exti0`'s interrupt token
85+
// so it can now mutably access local while `exti0` is also using it
86+
let _local = LOCAL.borrow_mut(&mut other_task);
87+
}
88+
});
89+
}
90+
91+
#[allow(dead_code)]
92+
#[used]
93+
#[link_section = ".rodata.interrupts"]
94+
static INTERRUPTS: interrupt::Handlers = interrupt::Handlers {
95+
Exti0: exti0,
96+
Exti1: exti1,
97+
..interrupt::DEFAULT_HANDLERS
98+
};
99+
```
100+
10101
## [v0.2.5] - 2017-05-07
11102

12103
### Added
@@ -15,17 +106,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
15106

16107
### Fixed
17108

18-
- MEMORY SAFETY. `interrupt::enable` was safe to call inside an
19-
`interrupt::free` critical section thus breaking the preemption protection.
20-
The `interrupt::enable` method is now `unsafe`.
109+
- [breaking-change]. MEMORY UNSAFETY. `interrupt::enable` was safe to call
110+
inside an `interrupt::free` critical section thus breaking the preemption
111+
protection. The `interrupt::enable` method is now `unsafe`.
21112

22113
## [v0.2.4] - 2017-04-20 - YANKED
23114

24115
### Fixed
25116

26-
- MEMORY SAFETY. `interrupt::free` leaked the critical section making it
27-
possible to access a `Mutex` when interrupts are enabled (see below). This has
28-
been fixed by changing the signature of `interrupt::free`.
117+
- [breaking-change]. MEMORY UNSAFETY. `interrupt::free` leaked the critical
118+
section making it possible to access a `Mutex` when interrupts are enabled
119+
(see below). This has been fixed by changing the signature of
120+
`interrupt::free`.
29121

30122
``` rust
31123
static FOO: Mutex<bool> = Mutex::new(false);
@@ -41,18 +133,18 @@ fn main() {
41133

42134
### Fixed
43135

44-
- MEMORY SAFETY. Some concurrency models that use "partial" critical sections
45-
(cf. BASEPRI) can be broken by changing the priority of interrupts or by
46-
changing BASEPRI in some scenarios. For this reason `NVIC.set_priority` and
47-
`register::basepri::write` are now `unsafe`.
136+
- [breaking-change]. MEMORY UNSAFETY. Some concurrency models that use "partial"
137+
critical sections (cf. BASEPRI) can be broken by changing the priority of
138+
interrupts or by changing BASEPRI in some scenarios. For this reason
139+
`NVIC.set_priority` and `register::basepri::write` are now `unsafe`.
48140

49141
## [v0.2.2] - 2017-04-08 - YANKED
50142

51143
### Fixed
52144

53-
- MEMORY SAFETY BUG. The `Mutex.borrow_mut` method has been removed as it can be
54-
used to bypass Rust's borrow checker and get, for example, two mutable
55-
references to the same data.
145+
- [breaking-change]. MEMORY UNSAFETY. The `Mutex.borrow_mut` method has been
146+
removed as it can be used to bypass Rust's borrow checker and get, for
147+
example, two mutable references to the same data.
56148

57149
``` rust
58150
static FOO: Mutex<bool> = Mutex::new(false);

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ keywords = ["arm", "cortex-m", "register", "peripheral"]
66
license = "MIT OR Apache-2.0"
77
name = "cortex-m"
88
repository = "https://github.com/japaric/cortex-m"
9-
version = "0.2.5"
9+
version = "0.2.6"
1010

1111
[dependencies]
1212
volatile-register = "0.2.0"

src/interrupt.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,14 @@ pub unsafe trait Nr {
2727
fn nr(&self) -> u8;
2828
}
2929

30-
unsafe impl<T> Sync for Mutex<T> {}
30+
// NOTE `Mutex` can be used as a channel so, the protected data must be `Send`
31+
// to prevent sending non-Sendable stuff (e.g. interrupt tokens) across
32+
// different execution contexts (e.g. interrupts)
33+
unsafe impl<T> Sync for Mutex<T>
34+
where
35+
T: Send,
36+
{
37+
}
3138

3239
/// Disables all interrupts
3340
#[inline(always)]
@@ -61,7 +68,7 @@ pub unsafe fn enable() {
6168
:
6269
:
6370
: "volatile");
64-
},
71+
}
6572
#[cfg(not(target_arch = "arm"))]
6673
() => {}
6774
}

0 commit comments

Comments
 (0)