Fix some out-of-bounds offset calls

After we yield the final element from the iterator, we don't offset
`ptr` agian, to avoid having it go out-of-bounds.

However, `inner_end` may be several elements out-of-bounds, depending on
the value of `size`. Therefore, we use `wrapping_offset` to avoid
undefined behavior.
This commit is contained in:
Aaron Hill 2019-11-21 17:22:00 -05:00 committed by Sébastien Crozet
parent e0db624031
commit 6bb355f4d0

View File

@ -74,7 +74,12 @@ macro_rules! iterator {
// Jump to the next outer dimension if needed. // Jump to the next outer dimension if needed.
if self.ptr == self.inner_end { if self.ptr == self.inner_end {
let stride = self.strides.1.value() as isize; let stride = self.strides.1.value() as isize;
self.inner_end = self.ptr.offset(stride); // This might go past the end of the allocation,
// depending on the value of 'size'. We use
// `wrapping_offset` to avoid UB
self.inner_end = self.ptr.wrapping_offset(stride);
// This will always be in bounds, since
// we're going to dereference it
self.ptr = self.inner_ptr.offset(stride); self.ptr = self.inner_ptr.offset(stride);
self.inner_ptr = self.ptr; self.inner_ptr = self.ptr;
} }
@ -83,8 +88,13 @@ macro_rules! iterator {
let old = self.ptr; let old = self.ptr;
let stride = self.strides.0.value() as isize; let stride = self.strides.0.value() as isize;
// Don't offset `self.ptr` for the last element,
// as this will be out of bounds. Iteration is done
// at this point (the next call to `next` will return `None`)
// so this is not observable.
if self.size != 0 {
self.ptr = self.ptr.offset(stride); self.ptr = self.ptr.offset(stride);
}
Some(mem::transmute(old)) Some(mem::transmute(old))
} }
} }