Skip to content

Commit 7d2bf6a

Browse files
committed
Fix MidiIter underflow past the end and handle null buffers
1 parent 7c4135e commit 7d2bf6a

2 files changed

Lines changed: 67 additions & 14 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
of `Err(Error::PortDisconnectionError)`.
99
- `Client::register_port` with the `Unowned` spec now returns `Err(Error::PortRegistrationError)`.
1010
- `Unowned::jack_buffer_size` now returns `0` instead of panicking.
11+
- `MidiIter` no longer dereferences a null port buffer.
12+
- `MidiIter` `next`, `nth`, `count`and `size_hint` no longer overflow or
13+
underflow when iterating past the end.
1114

1215
## 0.9.0
1316

‎src/port/midi.rs‎

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use jack_sys as j;
22
use std::marker::PhantomData;
3+
use std::ptr::NonNull;
34
use std::{mem, slice};
45

56
use crate::{Error, Frames, Port, PortFlags, PortSpec, ProcessScope};
@@ -49,8 +50,9 @@ impl Port<MidiIn> {
4950
/// Get an iterator over midi events.
5051
pub fn iter<'a>(&'a self, ps: &'a ProcessScope) -> MidiIter<'a> {
5152
assert_eq!(self.client_ptr(), ps.client_ptr());
53+
let buffer = NonNull::new(unsafe { self.buffer(ps.n_frames()) });
5254
MidiIter {
53-
buffer: unsafe { self.buffer(ps.n_frames()) },
55+
buffer,
5456
index: 0,
5557
_phantom: PhantomData,
5658
}
@@ -60,7 +62,7 @@ impl Port<MidiIn> {
6062
/// Iterate through Midi Messages within a [`Port<MidiIn>`].
6163
#[derive(Debug, Clone)]
6264
pub struct MidiIter<'a> {
63-
buffer: *mut ::libc::c_void,
65+
buffer: Option<NonNull<::libc::c_void>>,
6466
index: usize,
6567
_phantom: PhantomData<&'a ()>,
6668
}
@@ -86,42 +88,48 @@ impl<'a> MidiIter<'a> {
8688
}
8789

8890
fn absolute_nth(&self, n: u32) -> Option<RawMidi<'a>> {
91+
let buffer = self.buffer?.as_ptr();
8992
let mut ev = mem::MaybeUninit::<j::jack_midi_event_t>::uninit();
90-
let res = unsafe { j::jack_midi_event_get(ev.as_mut_ptr(), self.buffer, n) };
93+
let res = unsafe { j::jack_midi_event_get(ev.as_mut_ptr(), buffer, n) };
9194
if res != 0 {
9295
return None;
9396
}
9497
let ev = unsafe { ev.assume_init() };
9598
let time = ev.time;
96-
let bytes: &[u8] = unsafe { slice::from_raw_parts(ev.buffer as *const u8, ev.size) };
99+
// `slice::from_raw_parts` requires a non-null pointer, even for an empty slice.
100+
let bytes: &[u8] = if ev.buffer.is_null() || ev.size == 0 {
101+
&[]
102+
} else {
103+
unsafe { slice::from_raw_parts(ev.buffer as *const u8, ev.size) }
104+
};
97105
Some(RawMidi { time, bytes })
98106
}
99107

100108
fn absolute_len(&self) -> usize {
101-
if self.buffer.is_null() {
102-
0
103-
} else {
104-
unsafe { j::jack_midi_get_event_count(self.buffer) as usize }
105-
}
109+
let Some(buffer) = self.buffer else {
110+
return 0;
111+
};
112+
113+
unsafe { j::jack_midi_get_event_count(buffer.as_ptr()) as usize }
106114
}
107115
}
108116

109117
impl<'a> Iterator for MidiIter<'a> {
110118
type Item = RawMidi<'a>;
111119

112120
fn next(&mut self) -> Option<Self::Item> {
113-
let ret = self.peek();
121+
let ret = self.peek()?;
114122
self.index += 1;
115-
ret
123+
Some(ret)
116124
}
117125

118126
fn size_hint(&self) -> (usize, Option<usize>) {
119-
let len = self.absolute_len() - self.index;
127+
let len = self.absolute_len().saturating_sub(self.index);
120128
(len, Some(len))
121129
}
122130

123131
fn count(self) -> usize {
124-
self.absolute_len() - self.index
132+
self.absolute_len().saturating_sub(self.index)
125133
}
126134

127135
fn last(self) -> Option<Self::Item> {
@@ -134,7 +142,7 @@ impl<'a> Iterator for MidiIter<'a> {
134142
}
135143

136144
fn nth(&mut self, n: usize) -> Option<Self::Item> {
137-
self.index += n;
145+
self.index = self.index.saturating_add(n).min(self.absolute_len());
138146
self.next()
139147
}
140148
}
@@ -212,3 +220,45 @@ impl MidiWriter<'_> {
212220
unsafe { j::jack_midi_max_event_size(self.buffer) }
213221
}
214222
}
223+
224+
#[cfg(test)]
225+
mod tests {
226+
use super::*;
227+
228+
fn empty_iter() -> MidiIter<'static> {
229+
MidiIter {
230+
buffer: None,
231+
index: 0,
232+
_phantom: PhantomData,
233+
}
234+
}
235+
236+
#[test]
237+
fn empty_iter_has_no_events() {
238+
let mut iter = empty_iter();
239+
assert_eq!(iter.size_hint(), (0, Some(0)));
240+
assert_eq!(iter.peek(), None);
241+
assert_eq!(iter.next(), None);
242+
assert_eq!(iter.clone().count(), 0);
243+
assert_eq!(iter.last(), None);
244+
}
245+
246+
#[test]
247+
fn nth_past_the_end_does_not_underflow() {
248+
let mut iter = empty_iter();
249+
assert_eq!(iter.nth(5), None);
250+
assert_eq!(iter.size_hint(), (0, Some(0)));
251+
assert_eq!(iter.clone().count(), 0);
252+
assert_eq!(iter.nth(usize::MAX), None);
253+
assert_eq!(iter.size_hint(), (0, Some(0)));
254+
}
255+
256+
#[test]
257+
fn next_past_the_end_does_not_underflow() {
258+
let mut iter = empty_iter();
259+
for _ in 0..3 {
260+
assert_eq!(iter.next(), None);
261+
}
262+
assert_eq!(iter.size_hint(), (0, Some(0)));
263+
}
264+
}

0 commit comments

Comments
 (0)