aboutsummaryrefslogtreecommitdiff
path: root/embassy-extras/src/peripheral_shared.rs
diff options
context:
space:
mode:
authorLiam Murphy <[email protected]>2021-07-05 17:42:43 +1000
committerLiam Murphy <[email protected]>2021-07-05 17:42:43 +1000
commit744e2cbb8a0b58eccf3a9708c8c28f1ef17e4771 (patch)
treee288df991575c86e5079bdb33b7c1fd7888e96cb /embassy-extras/src/peripheral_shared.rs
parented83b93b6dd34cf09d1f772ec32ebd036e8798a7 (diff)
extras: Fix UB in `Peripheral`
`Peripheral` assumed that interrupts can't be preempted, when they can be preempted by higher priority interrupts. So I put the interrupt handler inside a critical section, and also added checks for whether the state had been dropped before the critical section was entered. I also added a `'static` bound to `PeripheralState`, since `Pin` only guarantees that the memory it directly references will not be invalidated. It doesn't guarantee that memory its pointee references also won't be invalidated. There were already some implementations of `PeripheralState` that weren't `'static`, though, so I added an unsafe `PeripheralStateUnchecked` trait and forwarded the `unsafe` to the constructors of the implementors.
Diffstat (limited to 'embassy-extras/src/peripheral_shared.rs')
-rw-r--r--embassy-extras/src/peripheral_shared.rs45
1 files changed, 33 insertions, 12 deletions
diff --git a/embassy-extras/src/peripheral_shared.rs b/embassy-extras/src/peripheral_shared.rs
index c62113396..a9fca8ca6 100644
--- a/embassy-extras/src/peripheral_shared.rs
+++ b/embassy-extras/src/peripheral_shared.rs
@@ -1,16 +1,27 @@
1use core::cell::UnsafeCell;
2use core::marker::{PhantomData, PhantomPinned}; 1use core::marker::{PhantomData, PhantomPinned};
3use core::pin::Pin; 2use core::pin::Pin;
3use core::ptr;
4 4
5use embassy::interrupt::{Interrupt, InterruptExt}; 5use embassy::interrupt::{Interrupt, InterruptExt};
6 6
7pub trait PeripheralState { 7/// # Safety
8/// When types implementing this trait are used with `Peripheral` or `PeripheralMutex`,
9/// their lifetime must not end without first calling `Drop` on the `Peripheral` or `PeripheralMutex`.
10pub unsafe trait PeripheralStateUnchecked {
8 type Interrupt: Interrupt; 11 type Interrupt: Interrupt;
9 fn on_interrupt(&self); 12 fn on_interrupt(&self);
10} 13}
11 14
12pub struct Peripheral<S: PeripheralState> { 15// `Peripheral` is safe because `Pin` guarantees that the memory it references will not be invalidated or reused
13 state: UnsafeCell<S>, 16// without calling `Drop`. However, it provides no guarantees about references contained within the state still being valid,
17// so this `'static` bound is necessary.
18pub trait PeripheralState: 'static {
19 type Interrupt: Interrupt;
20 fn on_interrupt(&self);
21}
22
23pub struct Peripheral<S: PeripheralStateUnchecked> {
24 state: S,
14 25
15 irq_setup_done: bool, 26 irq_setup_done: bool,
16 irq: S::Interrupt, 27 irq: S::Interrupt,
@@ -19,13 +30,13 @@ pub struct Peripheral<S: PeripheralState> {
19 _pinned: PhantomPinned, 30 _pinned: PhantomPinned,
20} 31}
21 32
22impl<S: PeripheralState> Peripheral<S> { 33impl<S: PeripheralStateUnchecked> Peripheral<S> {
23 pub fn new(irq: S::Interrupt, state: S) -> Self { 34 pub fn new(irq: S::Interrupt, state: S) -> Self {
24 Self { 35 Self {
25 irq, 36 irq,
26 irq_setup_done: false, 37 irq_setup_done: false,
27 38
28 state: UnsafeCell::new(state), 39 state,
29 _not_send: PhantomData, 40 _not_send: PhantomData,
30 _pinned: PhantomPinned, 41 _pinned: PhantomPinned,
31 } 42 }
@@ -39,8 +50,16 @@ impl<S: PeripheralState> Peripheral<S> {
39 50
40 this.irq.disable(); 51 this.irq.disable();
41 this.irq.set_handler(|p| { 52 this.irq.set_handler(|p| {
42 let state = unsafe { &*(p as *const S) }; 53 // We need to be in a critical section so that no one can preempt us
43 state.on_interrupt(); 54 // and drop the state after we check whether `p.is_null()`.
55 critical_section::with(|_| {
56 if p.is_null() {
57 // The state was dropped, so we can't operate on it.
58 return;
59 }
60 let state = unsafe { &*(p as *const S) };
61 state.on_interrupt();
62 });
44 }); 63 });
45 this.irq 64 this.irq
46 .set_handler_context((&this.state) as *const _ as *mut ()); 65 .set_handler_context((&this.state) as *const _ as *mut ());
@@ -49,15 +68,17 @@ impl<S: PeripheralState> Peripheral<S> {
49 this.irq_setup_done = true; 68 this.irq_setup_done = true;
50 } 69 }
51 70
52 pub fn state(self: Pin<&mut Self>) -> &S { 71 pub fn state<'a>(self: Pin<&'a mut Self>) -> &'a S {
53 let this = unsafe { self.get_unchecked_mut() }; 72 &self.into_ref().get_ref().state
54 unsafe { &*this.state.get() }
55 } 73 }
56} 74}
57 75
58impl<S: PeripheralState> Drop for Peripheral<S> { 76impl<S: PeripheralStateUnchecked> Drop for Peripheral<S> {
59 fn drop(&mut self) { 77 fn drop(&mut self) {
60 self.irq.disable(); 78 self.irq.disable();
61 self.irq.remove_handler(); 79 self.irq.remove_handler();
80 // Set the context to null so that the interrupt will know we're dropped
81 // if we pre-empted it before it entered a critical section.
82 self.irq.set_handler_context(ptr::null_mut());
62 } 83 }
63} 84}