Skip to content

Commit 2d569c1

Browse files
committed
Backport 73261bd from 0.9.3
1 parent 0eea6da commit 2d569c1

2 files changed

Lines changed: 100 additions & 5 deletions

File tree

CHANGELOG.md

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

1414
### Fixed
1515

16+
- spsc: Fix integer overflow in iterators when N > usize::MAX/2 and the queue loops.
17+
- spsc: Fix integer overflow leading to a panic in `len` when N == usize::MAX and debug assertions are enabled.
18+
1619
## [v0.7.17] - 2023-12-04
1720

1821
### Added

src/spsc.rs

Lines changed: 97 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,10 @@ impl<T, const N: usize> Queue<T, N> {
110110

111111
#[inline]
112112
fn increment(val: usize) -> usize {
113-
(val + 1) % N
113+
// We know that N <= usize::MAX
114+
// So this can only overflow if N == usize::MAX
115+
// and in this case the overflow will be equivalent to the modulo N operation
116+
val.wrapping_add(1) % N
114117
}
115118

116119
/// Creates an empty queue with a fixed capacity of `N - 1`
@@ -363,7 +366,11 @@ impl<'a, T, const N: usize> Iterator for Iter<'a, T, N> {
363366
if self.index < self.len {
364367
let head = self.rb.head.load(Ordering::Relaxed);
365368

366-
let i = (head + self.index) % N;
369+
let i = match head.checked_add(self.index) {
370+
Some(i) if i >= N => i - N,
371+
Some(i) => i,
372+
None => head.wrapping_add(self.index).wrapping_sub(N),
373+
};
367374
self.index += 1;
368375

369376
Some(unsafe { &*(self.rb.buffer.get_unchecked(i).get() as *const T) })
@@ -380,7 +387,11 @@ impl<'a, T, const N: usize> Iterator for IterMut<'a, T, N> {
380387
if self.index < self.len {
381388
let head = self.rb.head.load(Ordering::Relaxed);
382389

383-
let i = (head + self.index) % N;
390+
let i = match head.checked_add(self.index) {
391+
Some(i) if i >= N => i - N,
392+
Some(i) => i,
393+
None => head.wrapping_add(self.index).wrapping_sub(N),
394+
};
384395
self.index += 1;
385396

386397
Some(unsafe { &mut *(self.rb.buffer.get_unchecked(i).get() as *mut T) })
@@ -396,7 +407,11 @@ impl<'a, T, const N: usize> DoubleEndedIterator for Iter<'a, T, N> {
396407
let head = self.rb.head.load(Ordering::Relaxed);
397408

398409
// self.len > 0, since it's larger than self.index > 0
399-
let i = (head + self.len - 1) % N;
410+
let i = match head.checked_add(self.len - 1) {
411+
Some(i) if i >= N => i - N,
412+
Some(i) => i,
413+
None => head.wrapping_add(self.len - 1).wrapping_sub(N),
414+
};
400415
self.len -= 1;
401416
Some(unsafe { &*(self.rb.buffer.get_unchecked(i).get() as *const T) })
402417
} else {
@@ -411,7 +426,11 @@ impl<'a, T, const N: usize> DoubleEndedIterator for IterMut<'a, T, N> {
411426
let head = self.rb.head.load(Ordering::Relaxed);
412427

413428
// self.len > 0, since it's larger than self.index > 0
414-
let i = (head + self.len - 1) % N;
429+
let i = match head.checked_add(self.len - 1) {
430+
Some(i) if i >= N => i - N,
431+
Some(i) => i,
432+
None => head.wrapping_add(self.len - 1).wrapping_sub(N),
433+
};
415434
self.len -= 1;
416435
Some(unsafe { &mut *(self.rb.buffer.get_unchecked(i).get() as *mut T) })
417436
} else {
@@ -590,7 +609,10 @@ impl<'a, T, const N: usize> Producer<'a, T, N> {
590609

591610
#[cfg(test)]
592611
mod tests {
612+
use super::AtomicUsize;
593613
use crate::spsc::Queue;
614+
use core::cell::UnsafeCell;
615+
use core::mem::MaybeUninit;
594616
use hash32::Hasher;
595617

596618
#[test]
@@ -904,4 +926,74 @@ mod tests {
904926
};
905927
assert_eq!(hash1, hash2);
906928
}
929+
930+
// Test for some integer overflow bugs. See
931+
// https://github.com/rust-embedded/heapless/pull/652#discussion_r3046630717
932+
// for more info
933+
#[test]
934+
#[cfg_attr(miri, ignore)] // too slow
935+
fn test_len_overflow() {
936+
let mut queue = Queue::<(), { usize::MAX }> {
937+
head: AtomicUsize::new(usize::MAX),
938+
tail: AtomicUsize::new(2),
939+
buffer: [const { UnsafeCell::new(MaybeUninit::new(())) }; usize::MAX],
940+
};
941+
queue.enqueue(()).unwrap();
942+
queue.enqueue(()).unwrap();
943+
944+
let collected: Vec<_> = queue.iter().collect();
945+
assert_eq!(&collected, &[&(); 4]);
946+
}
947+
948+
#[test]
949+
#[cfg_attr(miri, ignore)] // too slow
950+
fn test_usize_overflow_iter() {
951+
let queue = Queue::<(), { usize::MAX - 1 }> {
952+
head: AtomicUsize::new(usize::MAX - 3),
953+
tail: AtomicUsize::new(2),
954+
buffer: [const { UnsafeCell::new(MaybeUninit::new(())) }; usize::MAX - 1],
955+
};
956+
957+
let collected: Vec<_> = queue.iter().collect();
958+
assert_eq!(&collected, &[&(); 4]);
959+
}
960+
961+
#[test]
962+
#[cfg_attr(miri, ignore)] // too slow
963+
fn test_usize_overflow_iter_mut() {
964+
let mut queue = Queue::<(), { usize::MAX - 1 }> {
965+
head: AtomicUsize::new(usize::MAX - 3),
966+
tail: AtomicUsize::new(2),
967+
buffer: [const { UnsafeCell::new(MaybeUninit::new(())) }; usize::MAX - 1],
968+
};
969+
970+
let collected: Vec<_> = queue.iter_mut().collect();
971+
assert_eq!(&collected, &[&(); 4]);
972+
}
973+
974+
#[test]
975+
#[cfg_attr(miri, ignore)] // too slow
976+
fn test_usize_overflow_iter_rev() {
977+
let queue = Queue::<(), { usize::MAX - 1 }> {
978+
head: AtomicUsize::new(usize::MAX - 3),
979+
tail: AtomicUsize::new(2),
980+
buffer: [const { UnsafeCell::new(MaybeUninit::new(())) }; usize::MAX - 1],
981+
};
982+
983+
let collected: Vec<_> = queue.iter().rev().collect();
984+
assert_eq!(&collected, &[&(); 4]);
985+
}
986+
987+
#[test]
988+
#[cfg_attr(miri, ignore)] // too slow
989+
fn test_usize_overflow_iter_mut_rev() {
990+
let mut queue = Queue::<(), { usize::MAX - 1 }> {
991+
head: AtomicUsize::new(usize::MAX - 3),
992+
tail: AtomicUsize::new(2),
993+
buffer: [const { UnsafeCell::new(MaybeUninit::new(())) }; usize::MAX - 1],
994+
};
995+
996+
let collected: Vec<_> = queue.iter_mut().rev().collect();
997+
assert_eq!(&collected, &[&(); 4]);
998+
}
907999
}

0 commit comments

Comments
 (0)