From 46904d710b5e75857529b63616b679c9e2318b2d Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 21 Nov 2019 17:22:00 -0500 Subject: [PATCH] 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. --- src/base/iter.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/base/iter.rs b/src/base/iter.rs index bad4e0be..65cdb20a 100644 --- a/src/base/iter.rs +++ b/src/base/iter.rs @@ -74,7 +74,12 @@ macro_rules! iterator { // Jump to the next outer dimension if needed. if self.ptr == self.inner_end { 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.inner_ptr = self.ptr; } @@ -83,8 +88,13 @@ macro_rules! iterator { let old = self.ptr; let stride = self.strides.0.value() as isize; - self.ptr = self.ptr.offset(stride); - + // 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); + } Some(mem::transmute(old)) } }