Skip to content

Commit acd252b

Browse files
authored
Remove aliasing &mut references to DMA buffers (possible UB) (#41)
* Remove aliasing &mut references * Fix Cargo.toml section name format * New &raw mut syntax for pointer to static * Document &'static mut and suppress warning * MSRV to 1.82.0
1 parent 7d1b873 commit acd252b

File tree

6 files changed

+60
-23
lines changed

6 files changed

+60
-23
lines changed

.github/bors.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ block_labels = ["wip"]
22
delete_merged_branches = true
33
status = [
44
"Rustfmt",
5-
"ci (1.68.2, log-rtt)",
6-
"ci (1.68.2, log-itm)",
7-
"ci (1.68.2, log-semihosting)",
5+
"ci (1.82.0, log-rtt)",
6+
"ci (1.82.0, log-itm)",
7+
"ci (1.82.0, log-semihosting)",
88
"ci (stable, log-rtt)",
99
"ci (stable, log-itm))",
1010
"ci (stable, log-semihosting)",

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
strategy:
1515
matrix: # All permutations of {rust, mcu}
1616
rust:
17-
- 1.70.0 # MSRV
17+
- 1.82.0 # MSRV
1818
- stable
1919
logger:
2020
- log-rtt

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ cortex-m-log = { version = "~0.8", features = ["itm", "semihosting", "log-integr
2828
panic-itm = { version = "~0.4.2", optional = true }
2929
panic-semihosting = { version = "0.6.0", optional = true }
3030
cortex-m-semihosting = { version = "0.5.0", optional = true }
31+
stable_deref_trait = { version = "1.2.0", default-features = false }
3132

3233
[features]
3334
default = []
@@ -55,7 +56,7 @@ debug = true # symbols are nice and they don't increase the size in flash
5556
lto = true # better optimizations
5657
opt-level = "s" # optimize for binary size
5758

58-
[dev_dependencies]
59+
[dev-dependencies]
5960
embedded-sdmmc = "0.5.0"
6061
usbd-midi = "0.3.0"
6162
num_enum = { version = "0.7.3", default-features = false }

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ cargo objcopy --example passthru --release -- -O binary passthru.bin
3838
[cargo-binutils-url]: https://github.com/rust-embedded/cargo-binutils
3939

4040
# Minimum supported Rust version
41-
The Minimum Supported Rust Version (MSRV) at the moment is 1.70.0
41+
The Minimum Supported Rust Version (MSRV) at the moment is 1.82.0
4242
# Demos
4343

4444
[Looper](https://github.com/mtthw-meyer/daisy-looper) - Basic one button looper.

examples/usb_midi.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ mod app {
3434
midi_device::MidiClass,
3535
};
3636

37+
// Warning: EP_MEMORY may only be used for the UsbBusAllocator. Any
38+
// additional references are UB.
3739
static mut EP_MEMORY: [u32; 1024] = [0; 1024];
3840

3941
#[shared]
@@ -68,7 +70,7 @@ mod app {
6870
let mut timer2 = device.TIM2.timer(
6971
MilliSeconds::from_ticks(200).into_rate(),
7072
ccdr.peripheral.TIM2,
71-
&mut ccdr.clocks,
73+
&ccdr.clocks,
7274
);
7375
timer2.listen(Event::TimeOut);
7476

@@ -127,6 +129,7 @@ mod app {
127129
&ccdr.clocks,
128130
);
129131

132+
#[allow(static_mut_refs)]
130133
let usb_bus = cortex_m::singleton!(
131134
: usb_device::class_prelude::UsbBusAllocator<UsbBus<USB2>> =
132135
UsbBus::new(usb, unsafe { &mut EP_MEMORY })

src/audio.rs

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
//! Audio module. Handles audio startup and I/O.
22
//! As well as converting between the S24 input and f32 for processing.
3+
use core::ops::{Deref, DerefMut};
4+
35
use log::info;
46

57
use stm32h7xx_hal::{
68
dma,
79
gpio::{gpioe, Analog},
8-
pac, rcc,
9-
rcc::rec,
10-
sai,
11-
sai::*,
12-
stm32,
13-
stm32::rcc::d2ccip1r::SAI1SEL_A,
10+
pac::{self},
11+
rcc::{self, rec},
12+
sai::{self, *},
13+
stm32::{self, rcc::d2ccip1r::SAI1SEL_A},
1414
traits::i2s::FullDuplex,
1515
};
1616

@@ -41,19 +41,44 @@ pub const MAX_TRANSFER_SIZE: usize = BLOCK_SIZE_MAX * 2;
4141

4242
pub type AudioBuffer = [(f32, f32); BLOCK_SIZE_MAX];
4343

44+
/// Raw pointer backed reference to the DMA buffers. It exists to avoid storing multiple aliasing
45+
/// `&mut` references to `TX_BUFFER` and `RX_BUFFER`, which is UB.
46+
/// # Safety
47+
/// References are created whenever the underlying buffer is accessed, but since the access is single
48+
/// threaded and the references are short lived this should be fine.
49+
/// This wrapper is only, and may only be, pointing to memory with a 'static lifetime.
50+
struct DmaBufferRawRef {
51+
ptr: *mut DmaBuffer,
52+
}
53+
impl Deref for DmaBufferRawRef {
54+
type Target = DmaBuffer;
55+
56+
fn deref(&self) -> &Self::Target {
57+
unsafe { &*self.ptr }
58+
}
59+
}
60+
impl DerefMut for DmaBufferRawRef {
61+
fn deref_mut(&mut self) -> &mut Self::Target {
62+
unsafe { &mut *self.ptr }
63+
}
64+
}
65+
unsafe impl stable_deref_trait::StableDeref for DmaBufferRawRef {}
66+
// Required for using the buffer in practice. No more dangerous than sending a `&mut DmaBuffer`
67+
unsafe impl Send for DmaBufferRawRef {}
68+
4469
type DmaInputStream = dma::Transfer<
4570
dma::dma::Stream1<stm32::DMA1>,
4671
stm32::SAI1,
4772
dma::PeripheralToMemory,
48-
&'static mut [u32; DMA_BUFFER_SIZE],
73+
DmaBufferRawRef,
4974
dma::DBTransfer,
5075
>;
5176

5277
type DmaOutputStream = dma::Transfer<
5378
dma::dma::Stream0<stm32::DMA1>,
5479
stm32::SAI1,
5580
dma::MemoryToPeripheral,
56-
&'static mut [u32; DMA_BUFFER_SIZE],
81+
DmaBufferRawRef,
5782
dma::DBTransfer,
5883
>;
5984

@@ -138,7 +163,9 @@ impl Audio {
138163
let dma1_streams = dma::dma::StreamsTuple::new(dma1_d, dma1_p);
139164

140165
// dma1 stream 0
141-
let tx_buffer: &'static mut [u32; DMA_BUFFER_SIZE] = unsafe { &mut TX_BUFFER };
166+
let tx_buffer = DmaBufferRawRef {
167+
ptr: &raw mut TX_BUFFER,
168+
};
142169
let dma_config = dma::dma::DmaConfig::default()
143170
.priority(dma::config::Priority::High)
144171
.memory_increment(true)
@@ -155,7 +182,9 @@ impl Audio {
155182
);
156183

157184
// dma1 stream 1
158-
let rx_buffer: &'static mut [u32; DMA_BUFFER_SIZE] = unsafe { &mut RX_BUFFER };
185+
let rx_buffer = DmaBufferRawRef {
186+
ptr: &raw mut RX_BUFFER,
187+
};
159188
let dma_config = dma_config
160189
.transfer_complete_interrupt(true)
161190
.half_transfer_interrupt(true);
@@ -207,8 +236,12 @@ impl Audio {
207236
sai.enable();
208237
sai.try_send(0, 0).unwrap();
209238
});
210-
let input = Input::new(unsafe { &mut RX_BUFFER });
211-
let output = Output::new(unsafe { &mut TX_BUFFER });
239+
let input = Input::new(DmaBufferRawRef {
240+
ptr: &raw mut RX_BUFFER,
241+
});
242+
let output = Output::new(DmaBufferRawRef {
243+
ptr: &raw mut TX_BUFFER,
244+
});
212245
info!(
213246
"{:?}, {:?}",
214247
&input.buffer[0] as *const u32, &output.buffer[0] as *const u32
@@ -289,12 +322,12 @@ impl Audio {
289322

290323
struct Input {
291324
index: usize,
292-
buffer: &'static DmaBuffer,
325+
buffer: DmaBufferRawRef,
293326
}
294327

295328
impl Input {
296329
/// Create a new Input from a DmaBuffer
297-
fn new(buffer: &'static DmaBuffer) -> Self {
330+
fn new(buffer: DmaBufferRawRef) -> Self {
298331
Self { index: 0, buffer }
299332
}
300333

@@ -310,12 +343,12 @@ impl Input {
310343

311344
struct Output {
312345
index: usize,
313-
buffer: &'static mut DmaBuffer,
346+
buffer: DmaBufferRawRef,
314347
}
315348

316349
impl Output {
317350
/// Create a new Input from a DmaBuffer
318-
fn new(buffer: &'static mut DmaBuffer) -> Self {
351+
fn new(buffer: DmaBufferRawRef) -> Self {
319352
Self { index: 0, buffer }
320353
}
321354

0 commit comments

Comments
 (0)