aboutsummaryrefslogtreecommitdiff
path: root/embassy-extras
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2021-07-29 13:08:30 +0200
committerGitHub <[email protected]>2021-07-29 13:08:30 +0200
commitc8a48d726a7cc92ef989a519fdf55ec1f9fffbcd (patch)
treec4d55f123620b68ed41cd517b437f61210fe6cfb /embassy-extras
parent61340d8c654abfb1e809042f9deceeaec0424d05 (diff)
parentcd1a3fcff34943117f446e1afeb9e6d531ee577b (diff)
Merge pull request #277 from Liamolucko/fix-peripheral-ub
extras: Fix UB in `Peripheral`
Diffstat (limited to 'embassy-extras')
-rw-r--r--embassy-extras/src/peripheral.rs97
-rw-r--r--embassy-extras/src/peripheral_shared.rs75
-rw-r--r--embassy-extras/src/usb/mod.rs36
-rw-r--r--embassy-extras/src/usb/usb_serial.rs6
4 files changed, 181 insertions, 33 deletions
diff --git a/embassy-extras/src/peripheral.rs b/embassy-extras/src/peripheral.rs
index 68972c543..92512a0f6 100644
--- a/embassy-extras/src/peripheral.rs
+++ b/embassy-extras/src/peripheral.rs
@@ -2,9 +2,19 @@ use core::cell::UnsafeCell;
2use core::marker::{PhantomData, PhantomPinned}; 2use core::marker::{PhantomData, PhantomPinned};
3use core::pin::Pin; 3use core::pin::Pin;
4 4
5use cortex_m::peripheral::scb::VectActive;
6use cortex_m::peripheral::{NVIC, SCB};
5use embassy::interrupt::{Interrupt, InterruptExt}; 7use embassy::interrupt::{Interrupt, InterruptExt};
6 8
7pub trait PeripheralState { 9/// A type which can be used as state with `PeripheralMutex`.
10///
11/// It needs to be `Send` because `&mut` references are sent back and forth between the 'thread' which owns the `PeripheralMutex` and the interrupt,
12/// and `&mut T` is only `Send` where `T: Send`.
13///
14/// It also requires `'static` to be used safely with `PeripheralMutex::register_interrupt`,
15/// because although `Pin` guarantees that the memory of the state won't be invalidated,
16/// it doesn't guarantee that the lifetime will last.
17pub trait PeripheralState: Send {
8 type Interrupt: Interrupt; 18 type Interrupt: Interrupt;
9 fn on_interrupt(&mut self); 19 fn on_interrupt(&mut self);
10} 20}
@@ -19,8 +29,51 @@ pub struct PeripheralMutex<S: PeripheralState> {
19 _pinned: PhantomPinned, 29 _pinned: PhantomPinned,
20} 30}
21 31
32/// Whether `irq` can be preempted by the current interrupt.
33pub(crate) fn can_be_preempted(irq: &impl Interrupt) -> bool {
34 match SCB::vect_active() {
35 // Thread mode can't preempt anything.
36 VectActive::ThreadMode => false,
37 // Exceptions don't always preempt interrupts,
38 // but there isn't much of a good reason to be keeping a `PeripheralMutex` in an exception anyway.
39 VectActive::Exception(_) => true,
40 VectActive::Interrupt { irqn } => {
41 #[derive(Clone, Copy)]
42 struct NrWrap(u16);
43 unsafe impl cortex_m::interrupt::InterruptNumber for NrWrap {
44 fn number(self) -> u16 {
45 self.0
46 }
47 }
48 NVIC::get_priority(NrWrap(irqn.into())) < irq.get_priority().into()
49 }
50 }
51}
52
53impl<S: PeripheralState + 'static> PeripheralMutex<S> {
54 /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it.
55 ///
56 /// This requires this `PeripheralMutex`'s `PeripheralState` to live for `'static`,
57 /// because `Pin` only guarantees that it's memory won't be repurposed,
58 /// not that it's lifetime will last.
59 ///
60 /// To use non-`'static` `PeripheralState`, use the unsafe `register_interrupt_unchecked`.
61 ///
62 /// Note: `'static` doesn't mean it _has_ to live for the entire program, like an `&'static T`;
63 /// it just means it _can_ live for the entire program - for example, `u8` lives for `'static`.
64 pub fn register_interrupt(self: Pin<&mut Self>) {
65 // SAFETY: `S: 'static`, so there's no way it's lifetime can expire.
66 unsafe { self.register_interrupt_unchecked() }
67 }
68}
69
22impl<S: PeripheralState> PeripheralMutex<S> { 70impl<S: PeripheralState> PeripheralMutex<S> {
71 /// Create a new `PeripheralMutex` wrapping `irq`, with the initial state `state`.
23 pub fn new(state: S, irq: S::Interrupt) -> Self { 72 pub fn new(state: S, irq: S::Interrupt) -> Self {
73 if can_be_preempted(&irq) {
74 panic!("`PeripheralMutex` cannot be created in an interrupt with higher priority than the interrupt it wraps");
75 }
76
24 Self { 77 Self {
25 irq, 78 irq,
26 irq_setup_done: false, 79 irq_setup_done: false,
@@ -31,8 +84,18 @@ impl<S: PeripheralState> PeripheralMutex<S> {
31 } 84 }
32 } 85 }
33 86
34 pub fn register_interrupt(self: Pin<&mut Self>) { 87 /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it.
35 let this = unsafe { self.get_unchecked_mut() }; 88 ///
89 /// # Safety
90 /// The lifetime of any data in `PeripheralState` that is accessed by the interrupt handler
91 /// must not end without `Drop` being called on this `PeripheralMutex`.
92 ///
93 /// This can be accomplished by either not accessing any data with a lifetime in `on_interrupt`,
94 /// or making sure that nothing like `mem::forget` is used on the `PeripheralMutex`.
95
96 // TODO: this name isn't the best.
97 pub unsafe fn register_interrupt_unchecked(self: Pin<&mut Self>) {
98 let this = self.get_unchecked_mut();
36 if this.irq_setup_done { 99 if this.irq_setup_done {
37 return; 100 return;
38 } 101 }
@@ -40,7 +103,9 @@ impl<S: PeripheralState> PeripheralMutex<S> {
40 this.irq.disable(); 103 this.irq.disable();
41 this.irq.set_handler(|p| { 104 this.irq.set_handler(|p| {
42 // Safety: it's OK to get a &mut to the state, since 105 // Safety: it's OK to get a &mut to the state, since
43 // - We're in the IRQ, no one else can't preempt us 106 // - We checked that the thread owning the `PeripheralMutex` can't preempt us in `new`.
107 // Interrupts' priorities can only be changed with raw embassy `Interrupts`,
108 // which can't safely store a `PeripheralMutex` across invocations.
44 // - We can't have preempted a with() call because the irq is disabled during it. 109 // - We can't have preempted a with() call because the irq is disabled during it.
45 let state = unsafe { &mut *(p as *mut S) }; 110 let state = unsafe { &mut *(p as *mut S) };
46 state.on_interrupt(); 111 state.on_interrupt();
@@ -52,19 +117,39 @@ impl<S: PeripheralState> PeripheralMutex<S> {
52 this.irq_setup_done = true; 117 this.irq_setup_done = true;
53 } 118 }
54 119
55 pub fn with<R>(self: Pin<&mut Self>, f: impl FnOnce(&mut S, &mut S::Interrupt) -> R) -> R { 120 pub fn with<R>(self: Pin<&mut Self>, f: impl FnOnce(&mut S) -> R) -> R {
56 let this = unsafe { self.get_unchecked_mut() }; 121 let this = unsafe { self.get_unchecked_mut() };
57 122
58 this.irq.disable(); 123 this.irq.disable();
59 124
60 // Safety: it's OK to get a &mut to the state, since the irq is disabled. 125 // Safety: it's OK to get a &mut to the state, since the irq is disabled.
61 let state = unsafe { &mut *this.state.get() }; 126 let state = unsafe { &mut *this.state.get() };
62 let r = f(state, &mut this.irq); 127 let r = f(state);
63 128
64 this.irq.enable(); 129 this.irq.enable();
65 130
66 r 131 r
67 } 132 }
133
134 /// Returns whether the wrapped interrupt is currently in a pending state.
135 pub fn is_pending(&self) -> bool {
136 self.irq.is_pending()
137 }
138
139 /// Forces the wrapped interrupt into a pending state.
140 pub fn pend(&self) {
141 self.irq.pend()
142 }
143
144 /// Forces the wrapped interrupt out of a pending state.
145 pub fn unpend(&self) {
146 self.irq.unpend()
147 }
148
149 /// Gets the priority of the wrapped interrupt.
150 pub fn priority(&self) -> <S::Interrupt as Interrupt>::Priority {
151 self.irq.get_priority()
152 }
68} 153}
69 154
70impl<S: PeripheralState> Drop for PeripheralMutex<S> { 155impl<S: PeripheralState> Drop for PeripheralMutex<S> {
diff --git a/embassy-extras/src/peripheral_shared.rs b/embassy-extras/src/peripheral_shared.rs
index c62113396..71d746341 100644
--- a/embassy-extras/src/peripheral_shared.rs
+++ b/embassy-extras/src/peripheral_shared.rs
@@ -1,16 +1,24 @@
1use core::cell::UnsafeCell;
2use core::marker::{PhantomData, PhantomPinned}; 1use core::marker::{PhantomData, PhantomPinned};
3use core::pin::Pin; 2use core::pin::Pin;
4 3
5use embassy::interrupt::{Interrupt, InterruptExt}; 4use embassy::interrupt::{Interrupt, InterruptExt};
6 5
7pub trait PeripheralState { 6use crate::peripheral::can_be_preempted;
7
8/// A type which can be used as state with `Peripheral`.
9///
10/// It needs to be `Sync` because references are shared between the 'thread' which owns the `Peripheral` and the interrupt.
11///
12/// It also requires `'static` to be used safely with `Peripheral::register_interrupt`,
13/// because although `Pin` guarantees that the memory of the state won't be invalidated,
14/// it doesn't guarantee that the lifetime will last.
15pub trait PeripheralState: Sync {
8 type Interrupt: Interrupt; 16 type Interrupt: Interrupt;
9 fn on_interrupt(&self); 17 fn on_interrupt(&self);
10} 18}
11 19
12pub struct Peripheral<S: PeripheralState> { 20pub struct Peripheral<S: PeripheralState> {
13 state: UnsafeCell<S>, 21 state: S,
14 22
15 irq_setup_done: bool, 23 irq_setup_done: bool,
16 irq: S::Interrupt, 24 irq: S::Interrupt,
@@ -19,26 +27,58 @@ pub struct Peripheral<S: PeripheralState> {
19 _pinned: PhantomPinned, 27 _pinned: PhantomPinned,
20} 28}
21 29
30impl<S: PeripheralState + 'static> Peripheral<S> {
31 /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it.
32 ///
33 /// This requires this `Peripheral`'s `PeripheralState` to live for `'static`,
34 /// because `Pin` only guarantees that it's memory won't be repurposed,
35 /// not that it's lifetime will last.
36 ///
37 /// To use non-`'static` `PeripheralState`, use the unsafe `register_interrupt_unchecked`.
38 ///
39 /// Note: `'static` doesn't mean it _has_ to live for the entire program, like an `&'static T`;
40 /// it just means it _can_ live for the entire program - for example, `u8` lives for `'static`.
41 pub fn register_interrupt(self: Pin<&mut Self>) {
42 // SAFETY: `S: 'static`, so there's no way it's lifetime can expire.
43 unsafe { self.register_interrupt_unchecked() }
44 }
45}
46
22impl<S: PeripheralState> Peripheral<S> { 47impl<S: PeripheralState> Peripheral<S> {
23 pub fn new(irq: S::Interrupt, state: S) -> Self { 48 pub fn new(irq: S::Interrupt, state: S) -> Self {
49 if can_be_preempted(&irq) {
50 panic!("`Peripheral` cannot be created in an interrupt with higher priority than the interrupt it wraps");
51 }
52
24 Self { 53 Self {
25 irq, 54 irq,
26 irq_setup_done: false, 55 irq_setup_done: false,
27 56
28 state: UnsafeCell::new(state), 57 state,
29 _not_send: PhantomData, 58 _not_send: PhantomData,
30 _pinned: PhantomPinned, 59 _pinned: PhantomPinned,
31 } 60 }
32 } 61 }
33 62
34 pub fn register_interrupt(self: Pin<&mut Self>) { 63 /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it.
35 let this = unsafe { self.get_unchecked_mut() }; 64 ///
65 /// # Safety
66 /// The lifetime of any data in `PeripheralState` that is accessed by the interrupt handler
67 /// must not end without `Drop` being called on this `Peripheral`.
68 ///
69 /// This can be accomplished by either not accessing any data with a lifetime in `on_interrupt`,
70 /// or making sure that nothing like `mem::forget` is used on the `Peripheral`.
71 pub unsafe fn register_interrupt_unchecked(self: Pin<&mut Self>) {
72 let this = self.get_unchecked_mut();
36 if this.irq_setup_done { 73 if this.irq_setup_done {
37 return; 74 return;
38 } 75 }
39 76
40 this.irq.disable(); 77 this.irq.disable();
41 this.irq.set_handler(|p| { 78 this.irq.set_handler(|p| {
79 // The state can't have been dropped, otherwise the interrupt would have been disabled.
80 // We checked in `new` that the thread owning the `Peripheral` can't preempt the interrupt,
81 // so someone can't have preempted us before this point and dropped the `Peripheral`.
42 let state = unsafe { &*(p as *const S) }; 82 let state = unsafe { &*(p as *const S) };
43 state.on_interrupt(); 83 state.on_interrupt();
44 }); 84 });
@@ -50,8 +90,27 @@ impl<S: PeripheralState> Peripheral<S> {
50 } 90 }
51 91
52 pub fn state(self: Pin<&mut Self>) -> &S { 92 pub fn state(self: Pin<&mut Self>) -> &S {
53 let this = unsafe { self.get_unchecked_mut() }; 93 &self.into_ref().get_ref().state
54 unsafe { &*this.state.get() } 94 }
95
96 /// Returns whether the wrapped interrupt is currently in a pending state.
97 pub fn is_pending(&self) -> bool {
98 self.irq.is_pending()
99 }
100
101 /// Forces the wrapped interrupt into a pending state.
102 pub fn pend(&self) {
103 self.irq.pend()
104 }
105
106 /// Forces the wrapped interrupt out of a pending state.
107 pub fn unpend(&self) {
108 self.irq.unpend()
109 }
110
111 /// Gets the priority of the wrapped interrupt.
112 pub fn priority(&self) -> <S::Interrupt as Interrupt>::Priority {
113 self.irq.get_priority()
55 } 114 }
56} 115}
57 116
diff --git a/embassy-extras/src/usb/mod.rs b/embassy-extras/src/usb/mod.rs
index 182cd87d0..1fb501d7f 100644
--- a/embassy-extras/src/usb/mod.rs
+++ b/embassy-extras/src/usb/mod.rs
@@ -14,7 +14,7 @@ use embassy::interrupt::Interrupt;
14use usb_serial::{ReadInterface, UsbSerial, WriteInterface}; 14use usb_serial::{ReadInterface, UsbSerial, WriteInterface};
15 15
16/// Marker trait to mark an interrupt to be used with the [`Usb`] abstraction. 16/// Marker trait to mark an interrupt to be used with the [`Usb`] abstraction.
17pub unsafe trait USBInterrupt: Interrupt {} 17pub unsafe trait USBInterrupt: Interrupt + Send {}
18 18
19pub(crate) struct State<'bus, B, T, I> 19pub(crate) struct State<'bus, B, T, I>
20where 20where
@@ -55,13 +55,17 @@ where
55 } 55 }
56 } 56 }
57 57
58 pub fn start(self: Pin<&mut Self>) { 58 /// # Safety
59 let this = unsafe { self.get_unchecked_mut() }; 59 /// The `UsbDevice` passed to `Self::new` must not be dropped without calling `Drop` on this `Usb` first.
60 pub unsafe fn start(self: Pin<&mut Self>) {
61 let this = self.get_unchecked_mut();
60 let mut mutex = this.inner.borrow_mut(); 62 let mut mutex = this.inner.borrow_mut();
61 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; 63 let mutex = Pin::new_unchecked(&mut *mutex);
62 64
63 // Use inner to register the irq 65 // Use inner to register the irq
64 mutex.register_interrupt(); 66 // SAFETY: the safety contract of this function makes sure the `UsbDevice` won't be invalidated
67 // without the `PeripheralMutex` being dropped.
68 mutex.register_interrupt_unchecked();
65 } 69 }
66} 70}
67 71
@@ -137,7 +141,7 @@ where
137 } 141 }
138} 142}
139 143
140pub trait ClassSet<B: UsbBus> { 144pub trait ClassSet<B: UsbBus>: Send {
141 fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool; 145 fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool;
142} 146}
143 147
@@ -173,8 +177,8 @@ pub struct Index1;
173 177
174impl<B, C1> ClassSet<B> for ClassSet1<B, C1> 178impl<B, C1> ClassSet<B> for ClassSet1<B, C1>
175where 179where
176 B: UsbBus, 180 B: UsbBus + Send,
177 C1: UsbClass<B>, 181 C1: UsbClass<B> + Send,
178{ 182{
179 fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool { 183 fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool {
180 device.poll(&mut [&mut self.class]) 184 device.poll(&mut [&mut self.class])
@@ -183,9 +187,9 @@ where
183 187
184impl<B, C1, C2> ClassSet<B> for ClassSet2<B, C1, C2> 188impl<B, C1, C2> ClassSet<B> for ClassSet2<B, C1, C2>
185where 189where
186 B: UsbBus, 190 B: UsbBus + Send,
187 C1: UsbClass<B>, 191 C1: UsbClass<B> + Send,
188 C2: UsbClass<B>, 192 C2: UsbClass<B> + Send,
189{ 193{
190 fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool { 194 fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool {
191 device.poll(&mut [&mut self.class1, &mut self.class2]) 195 device.poll(&mut [&mut self.class1, &mut self.class2])
@@ -194,8 +198,8 @@ where
194 198
195impl<B, C1> IntoClassSet<B, ClassSet1<B, C1>> for C1 199impl<B, C1> IntoClassSet<B, ClassSet1<B, C1>> for C1
196where 200where
197 B: UsbBus, 201 B: UsbBus + Send,
198 C1: UsbClass<B>, 202 C1: UsbClass<B> + Send,
199{ 203{
200 fn into_class_set(self) -> ClassSet1<B, C1> { 204 fn into_class_set(self) -> ClassSet1<B, C1> {
201 ClassSet1 { 205 ClassSet1 {
@@ -207,9 +211,9 @@ where
207 211
208impl<B, C1, C2> IntoClassSet<B, ClassSet2<B, C1, C2>> for (C1, C2) 212impl<B, C1, C2> IntoClassSet<B, ClassSet2<B, C1, C2>> for (C1, C2)
209where 213where
210 B: UsbBus, 214 B: UsbBus + Send,
211 C1: UsbClass<B>, 215 C1: UsbClass<B> + Send,
212 C2: UsbClass<B>, 216 C2: UsbClass<B> + Send,
213{ 217{
214 fn into_class_set(self) -> ClassSet2<B, C1, C2> { 218 fn into_class_set(self) -> ClassSet2<B, C1, C2> {
215 ClassSet2 { 219 ClassSet2 {
diff --git a/embassy-extras/src/usb/usb_serial.rs b/embassy-extras/src/usb/usb_serial.rs
index 9cbfb2da4..a229b2000 100644
--- a/embassy-extras/src/usb/usb_serial.rs
+++ b/embassy-extras/src/usb/usb_serial.rs
@@ -55,7 +55,7 @@ where
55 let this = self.get_mut(); 55 let this = self.get_mut();
56 let mut mutex = this.inner.borrow_mut(); 56 let mut mutex = this.inner.borrow_mut();
57 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; 57 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) };
58 mutex.with(|state, _irq| { 58 mutex.with(|state| {
59 let serial = state.classes.get_serial(); 59 let serial = state.classes.get_serial();
60 let serial = Pin::new(serial); 60 let serial = Pin::new(serial);
61 61
@@ -77,7 +77,7 @@ where
77 let this = self.get_mut(); 77 let this = self.get_mut();
78 let mut mutex = this.inner.borrow_mut(); 78 let mut mutex = this.inner.borrow_mut();
79 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; 79 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) };
80 mutex.with(|state, _irq| { 80 mutex.with(|state| {
81 let serial = state.classes.get_serial(); 81 let serial = state.classes.get_serial();
82 let serial = Pin::new(serial); 82 let serial = Pin::new(serial);
83 83
@@ -101,7 +101,7 @@ where
101 let this = self.get_mut(); 101 let this = self.get_mut();
102 let mut mutex = this.inner.borrow_mut(); 102 let mut mutex = this.inner.borrow_mut();
103 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; 103 let mutex = unsafe { Pin::new_unchecked(&mut *mutex) };
104 mutex.with(|state, _irq| { 104 mutex.with(|state| {
105 let serial = state.classes.get_serial(); 105 let serial = state.classes.get_serial();
106 let serial = Pin::new(serial); 106 let serial = Pin::new(serial);
107 107