Skip to content

Commit

Permalink
Avoid returning a slice with a null pointer from Iter.as_slice()
Browse files Browse the repository at this point in the history
core::slice::Iter.ptr can be null when iterating a slice of zero-sized
elements, but the pointer value used for the slice itself cannot. Handle
this case by always returning a dummy pointer for slices of zero-sized
elements.
  • Loading branch information
lilyball committed May 11, 2015
1 parent e1e34e9 commit f2614f5
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 22 deletions.
38 changes: 19 additions & 19 deletions src/libcore/slice.rs
Expand Up @@ -728,29 +728,29 @@ macro_rules! iterator {
}

macro_rules! make_slice {
($t: ty => $result: ty: $start: expr, $end: expr) => {{
let diff = ($end as usize).wrapping_sub($start as usize);
let len = if mem::size_of::<T>() == 0 {
diff
($start: expr, $end: expr) => {{
let start = $start;
let diff = ($end as usize).wrapping_sub(start as usize);
if size_from_ptr(start) == 0 {
// use a non-null pointer value
unsafe { from_raw_parts(1 as *const _, diff) }
} else {
diff / mem::size_of::<$t>()
};
unsafe {
from_raw_parts($start, len)
let len = diff / size_from_ptr(start);
unsafe { from_raw_parts(start, len) }
}
}}
}

macro_rules! make_mut_slice {
($t: ty => $result: ty: $start: expr, $end: expr) => {{
let diff = ($end as usize).wrapping_sub($start as usize);
let len = if mem::size_of::<T>() == 0 {
diff
($start: expr, $end: expr) => {{
let start = $start;
let diff = ($end as usize).wrapping_sub(start as usize);
if size_from_ptr(start) == 0 {
// use a non-null pointer value
unsafe { from_raw_parts_mut(1 as *mut _, diff) }
} else {
diff / mem::size_of::<$t>()
};
unsafe {
from_raw_parts_mut($start, len)
let len = diff / size_from_ptr(start);
unsafe { from_raw_parts_mut(start, len) }
}
}}
}
Expand All @@ -773,7 +773,7 @@ impl<'a, T> Iter<'a, T> {
/// iterator can continue to be used while this exists.
#[unstable(feature = "core")]
pub fn as_slice(&self) -> &'a [T] {
make_slice!(T => &'a [T]: self.ptr, self.end)
make_slice!(self.ptr, self.end)
}

// Helper function for Iter::nth
Expand Down Expand Up @@ -841,12 +841,12 @@ impl<'a, T> IterMut<'a, T> {
/// restricted lifetimes that do not consume the iterator.
#[unstable(feature = "core")]
pub fn into_slice(self) -> &'a mut [T] {
make_mut_slice!(T => &'a mut [T]: self.ptr, self.end)
make_mut_slice!(self.ptr, self.end)
}

// Helper function for IterMut::nth
fn iter_nth(&mut self, n: usize) -> Option<&'a mut T> {
match make_mut_slice!(T => &'a mut [T]: self.ptr, self.end).get_mut(n) {
match make_mut_slice!(self.ptr, self.end).get_mut(n) {
Some(elem_ref) => unsafe {
self.ptr = slice_offset!(self.ptr, (n as isize).wrapping_add(1));
Some(slice_ref!(elem_ref))
Expand Down
32 changes: 29 additions & 3 deletions src/test/run-pass/slice-of-zero-size-elements.rs
Expand Up @@ -10,8 +10,26 @@

// compile-flags: -C debug-assertions

#![feature(core)]

use std::slice;

fn foo<T>(v: &[T]) -> Option<&[T]> {
let mut it = v.iter();
for _ in 0..5 {
let _ = it.next();
}
Some(it.as_slice())
}

fn foo_mut<T>(v: &mut [T]) -> Option<&mut [T]> {
let mut it = v.iter_mut();
for _ in 0..5 {
let _ = it.next();
}
Some(it.into_slice())
}

pub fn main() {
// In a slice of zero-size elements the pointer is meaningless.
// Ensure iteration still works even if the pointer is at the end of the address space.
Expand All @@ -24,11 +42,19 @@ pub fn main() {
assert!(it.nth(5).is_some());
assert_eq!(it.count(), 4);

// Converting Iter to a slice should never have a null pointer
assert!(foo(slice).is_some());

// Test mutable iterators as well
let slice: &mut [()] = unsafe { slice::from_raw_parts_mut(-5isize as *mut (), 10) };
assert_eq!(slice.len(), 10);
assert_eq!(slice.iter_mut().count(), 10);

let mut it = slice.iter_mut();
assert!(it.nth(5).is_some());
assert_eq!(it.count(), 4);
{
let mut it = slice.iter_mut();
assert!(it.nth(5).is_some());
assert_eq!(it.count(), 4);
}

assert!(foo_mut(slice).is_some())
}

0 comments on commit f2614f5

Please sign in to comment.