Fix potential undoundness with Storage::as_slice and Storage::as_mut_slice (#905)

This commit is contained in:
Sébastien Crozet 2021-06-17 09:46:49 +02:00 committed by GitHub
parent d64e799fc9
commit 38add0b00d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 85 additions and 65 deletions

View File

@ -79,7 +79,7 @@ where
} }
#[inline] #[inline]
fn is_contiguous(&self) -> bool { unsafe fn is_contiguous(&self) -> bool {
true true
} }
@ -101,8 +101,8 @@ where
} }
#[inline] #[inline]
fn as_slice(&self) -> &[T] { unsafe fn as_slice_unchecked(&self) -> &[T] {
unsafe { std::slice::from_raw_parts(self.ptr(), R * C) } std::slice::from_raw_parts(self.ptr(), R * C)
} }
} }
@ -118,8 +118,8 @@ where
} }
#[inline] #[inline]
fn as_mut_slice(&mut self) -> &mut [T] { unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
unsafe { std::slice::from_raw_parts_mut(self.ptr_mut(), R * C) } std::slice::from_raw_parts_mut(self.ptr_mut(), R * C)
} }
} }

View File

@ -344,13 +344,17 @@ where
let rstride1 = self.strides().0; let rstride1 = self.strides().0;
let rstride2 = x.strides().0; let rstride2 = x.strides().0;
let y = self.data.as_mut_slice(); unsafe {
let x = x.data.as_slice(); // SAFETY: the conversion to slices is OK because we access the
// elements taking the strides into account.
let y = self.data.as_mut_slice_unchecked();
let x = x.data.as_slice_unchecked();
if !b.is_zero() { if !b.is_zero() {
array_axcpy(y, a, x, c, b, rstride1, rstride2, x.len()); array_axcpy(y, a, x, c, b, rstride1, rstride2, x.len());
} else { } else {
array_axc(y, a, x, c, rstride1, rstride2, x.len()); array_axc(y, a, x, c, rstride1, rstride2, x.len());
}
} }
} }

View File

@ -16,7 +16,7 @@ use crate::base::array_storage::ArrayStorage;
#[cfg(any(feature = "alloc", feature = "std"))] #[cfg(any(feature = "alloc", feature = "std"))]
use crate::base::dimension::Dynamic; use crate::base::dimension::Dynamic;
use crate::base::dimension::{Dim, DimName}; use crate::base::dimension::{Dim, DimName};
use crate::base::storage::{Storage, StorageMut}; use crate::base::storage::{ContiguousStorageMut, Storage, StorageMut};
#[cfg(any(feature = "std", feature = "alloc"))] #[cfg(any(feature = "std", feature = "alloc"))]
use crate::base::vec_storage::VecStorage; use crate::base::vec_storage::VecStorage;
use crate::base::Scalar; use crate::base::Scalar;

View File

@ -11,7 +11,7 @@ use crate::base::constraint::{DimEq, SameNumberOfColumns, SameNumberOfRows, Shap
#[cfg(any(feature = "std", feature = "alloc"))] #[cfg(any(feature = "std", feature = "alloc"))]
use crate::base::dimension::Dynamic; use crate::base::dimension::Dynamic;
use crate::base::dimension::{Const, Dim, DimAdd, DimDiff, DimMin, DimMinimum, DimSub, DimSum, U1}; use crate::base::dimension::{Const, Dim, DimAdd, DimDiff, DimMin, DimMinimum, DimSub, DimSum, U1};
use crate::base::storage::{ReshapableStorage, Storage, StorageMut}; use crate::base::storage::{ContiguousStorageMut, ReshapableStorage, Storage, StorageMut};
use crate::base::{DefaultAllocator, Matrix, OMatrix, RowVector, Scalar, Vector}; use crate::base::{DefaultAllocator, Matrix, OMatrix, RowVector, Scalar, Vector};
/// # Rows and columns extraction /// # Rows and columns extraction

View File

@ -132,7 +132,7 @@ macro_rules! storage_impl(
} }
#[inline] #[inline]
fn is_contiguous(&self) -> bool { unsafe fn is_contiguous(&self) -> bool {
// Common cases that can be deduced at compile-time even if one of the dimensions // Common cases that can be deduced at compile-time even if one of the dimensions
// is Dynamic. // is Dynamic.
if (RStride::is::<U1>() && C::is::<U1>()) || // Column vector. if (RStride::is::<U1>() && C::is::<U1>()) || // Column vector.
@ -162,14 +162,14 @@ macro_rules! storage_impl(
} }
#[inline] #[inline]
fn as_slice(&self) -> &[T] { unsafe fn as_slice_unchecked(&self) -> &[T] {
let (nrows, ncols) = self.shape(); let (nrows, ncols) = self.shape();
if nrows.value() != 0 && ncols.value() != 0 { if nrows.value() != 0 && ncols.value() != 0 {
let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1); let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1);
unsafe { slice::from_raw_parts(self.ptr, sz + 1) } slice::from_raw_parts(self.ptr, sz + 1)
} }
else { else {
unsafe { slice::from_raw_parts(self.ptr, 0) } slice::from_raw_parts(self.ptr, 0)
} }
} }
} }
@ -187,13 +187,13 @@ unsafe impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> StorageMu
} }
#[inline] #[inline]
fn as_mut_slice(&mut self) -> &mut [T] { unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
let (nrows, ncols) = self.shape(); let (nrows, ncols) = self.shape();
if nrows.value() != 0 && ncols.value() != 0 { if nrows.value() != 0 && ncols.value() != 0 {
let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1); let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1);
unsafe { slice::from_raw_parts_mut(self.ptr, sz + 1) } slice::from_raw_parts_mut(self.ptr, sz + 1)
} else { } else {
unsafe { slice::from_raw_parts_mut(self.ptr, 0) } slice::from_raw_parts_mut(self.ptr, 0)
} }
} }
} }

View File

@ -158,20 +158,17 @@ macro_rules! componentwise_binop_impl(
// This is the most common case and should be deduced at compile-time. // This is the most common case and should be deduced at compile-time.
// TODO: use specialization instead? // TODO: use specialization instead?
if self.data.is_contiguous() && rhs.data.is_contiguous() && out.data.is_contiguous() { unsafe {
let arr1 = self.data.as_slice(); if self.data.is_contiguous() && rhs.data.is_contiguous() && out.data.is_contiguous() {
let arr2 = rhs.data.as_slice(); let arr1 = self.data.as_slice_unchecked();
let out = out.data.as_mut_slice(); let arr2 = rhs.data.as_slice_unchecked();
for i in 0 .. arr1.len() { let out = out.data.as_mut_slice_unchecked();
unsafe { for i in 0 .. arr1.len() {
*out.get_unchecked_mut(i) = arr1.get_unchecked(i).inlined_clone().$method(arr2.get_unchecked(i).inlined_clone()); *out.get_unchecked_mut(i) = arr1.get_unchecked(i).inlined_clone().$method(arr2.get_unchecked(i).inlined_clone());
} }
} } else {
} for j in 0 .. self.ncols() {
else { for i in 0 .. self.nrows() {
for j in 0 .. self.ncols() {
for i in 0 .. self.nrows() {
unsafe {
let val = self.get_unchecked((i, j)).inlined_clone().$method(rhs.get_unchecked((i, j)).inlined_clone()); let val = self.get_unchecked((i, j)).inlined_clone().$method(rhs.get_unchecked((i, j)).inlined_clone());
*out.get_unchecked_mut((i, j)) = val; *out.get_unchecked_mut((i, j)) = val;
} }
@ -191,19 +188,17 @@ macro_rules! componentwise_binop_impl(
// This is the most common case and should be deduced at compile-time. // This is the most common case and should be deduced at compile-time.
// TODO: use specialization instead? // TODO: use specialization instead?
if self.data.is_contiguous() && rhs.data.is_contiguous() { unsafe {
let arr1 = self.data.as_mut_slice(); if self.data.is_contiguous() && rhs.data.is_contiguous() {
let arr2 = rhs.data.as_slice(); let arr1 = self.data.as_mut_slice_unchecked();
for i in 0 .. arr2.len() { let arr2 = rhs.data.as_slice_unchecked();
unsafe {
for i in 0 .. arr2.len() {
arr1.get_unchecked_mut(i).$method_assign(arr2.get_unchecked(i).inlined_clone()); arr1.get_unchecked_mut(i).$method_assign(arr2.get_unchecked(i).inlined_clone());
} }
} } else {
} for j in 0 .. rhs.ncols() {
else { for i in 0 .. rhs.nrows() {
for j in 0 .. rhs.ncols() {
for i in 0 .. rhs.nrows() {
unsafe {
self.get_unchecked_mut((i, j)).$method_assign(rhs.get_unchecked((i, j)).inlined_clone()) self.get_unchecked_mut((i, j)).$method_assign(rhs.get_unchecked((i, j)).inlined_clone())
} }
} }
@ -221,20 +216,18 @@ macro_rules! componentwise_binop_impl(
// This is the most common case and should be deduced at compile-time. // This is the most common case and should be deduced at compile-time.
// TODO: use specialization instead? // TODO: use specialization instead?
if self.data.is_contiguous() && rhs.data.is_contiguous() { unsafe {
let arr1 = self.data.as_slice(); if self.data.is_contiguous() && rhs.data.is_contiguous() {
let arr2 = rhs.data.as_mut_slice(); let arr1 = self.data.as_slice_unchecked();
for i in 0 .. arr1.len() { let arr2 = rhs.data.as_mut_slice_unchecked();
unsafe {
for i in 0 .. arr1.len() {
let res = arr1.get_unchecked(i).inlined_clone().$method(arr2.get_unchecked(i).inlined_clone()); let res = arr1.get_unchecked(i).inlined_clone().$method(arr2.get_unchecked(i).inlined_clone());
*arr2.get_unchecked_mut(i) = res; *arr2.get_unchecked_mut(i) = res;
} }
} } else {
} for j in 0 .. self.ncols() {
else { for i in 0 .. self.nrows() {
for j in 0 .. self.ncols() {
for i in 0 .. self.nrows() {
unsafe {
let r = rhs.get_unchecked_mut((i, j)); let r = rhs.get_unchecked_mut((i, j));
*r = self.get_unchecked((i, j)).inlined_clone().$method(r.inlined_clone()) *r = self.get_unchecked((i, j)).inlined_clone().$method(r.inlined_clone())
} }

View File

@ -94,12 +94,19 @@ pub unsafe trait Storage<T: Scalar, R: Dim, C: Dim = U1>: Debug + Sized {
} }
/// Indicates whether this data buffer stores its elements contiguously. /// Indicates whether this data buffer stores its elements contiguously.
fn is_contiguous(&self) -> bool; ///
/// This method is unsafe because unsafe code relies on this properties to performe
/// some low-lever optimizations.
unsafe fn is_contiguous(&self) -> bool;
/// Retrieves the data buffer as a contiguous slice. /// Retrieves the data buffer as a contiguous slice.
/// ///
/// The matrix components may not be stored in a contiguous way, depending on the strides. /// The matrix components may not be stored in a contiguous way, depending on the strides.
fn as_slice(&self) -> &[T]; /// This method is unsafe because this can yield to invalid aliasing when called on some pairs
/// of matrix slices originating from the same matrix with strides.
///
/// Call the safe alternative `matrix.as_slice()` instead.
unsafe fn as_slice_unchecked(&self) -> &[T];
/// Builds a matrix data storage that does not contain any reference. /// Builds a matrix data storage that does not contain any reference.
fn into_owned(self) -> Owned<T, R, C> fn into_owned(self) -> Owned<T, R, C>
@ -166,7 +173,11 @@ pub unsafe trait StorageMut<T: Scalar, R: Dim, C: Dim = U1>: Storage<T, R, C> {
/// Retrieves the mutable data buffer as a contiguous slice. /// Retrieves the mutable data buffer as a contiguous slice.
/// ///
/// Matrix components may not be contiguous, depending on its strides. /// Matrix components may not be contiguous, depending on its strides.
fn as_mut_slice(&mut self) -> &mut [T]; ///
/// The matrix components may not be stored in a contiguous way, depending on the strides.
/// This method is unsafe because this can yield to invalid aliasing when called on some pairs
/// of matrix slices originating from the same matrix with strides.
unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T];
} }
/// A matrix storage that is stored contiguously in memory. /// A matrix storage that is stored contiguously in memory.
@ -177,6 +188,12 @@ pub unsafe trait StorageMut<T: Scalar, R: Dim, C: Dim = U1>: Storage<T, R, C> {
pub unsafe trait ContiguousStorage<T: Scalar, R: Dim, C: Dim = U1>: pub unsafe trait ContiguousStorage<T: Scalar, R: Dim, C: Dim = U1>:
Storage<T, R, C> Storage<T, R, C>
{ {
/// Converts this data storage to a contiguous slice.
fn as_slice(&self) -> &[T] {
// SAFETY: this is safe because this trait guarantees the fact
// that the data is stored contiguously.
unsafe { self.as_slice_unchecked() }
}
} }
/// A mutable matrix storage that is stored contiguously in memory. /// A mutable matrix storage that is stored contiguously in memory.
@ -187,6 +204,12 @@ pub unsafe trait ContiguousStorage<T: Scalar, R: Dim, C: Dim = U1>:
pub unsafe trait ContiguousStorageMut<T: Scalar, R: Dim, C: Dim = U1>: pub unsafe trait ContiguousStorageMut<T: Scalar, R: Dim, C: Dim = U1>:
ContiguousStorage<T, R, C> + StorageMut<T, R, C> ContiguousStorage<T, R, C> + StorageMut<T, R, C>
{ {
/// Converts this data storage to a contiguous mutable slice.
fn as_mut_slice(&mut self) -> &mut [T] {
// SAFETY: this is safe because this trait guarantees the fact
// that the data is stored contiguously.
unsafe { self.as_mut_slice_unchecked() }
}
} }
/// A matrix storage that can be reshaped in-place. /// A matrix storage that can be reshaped in-place.

View File

@ -178,7 +178,7 @@ where
} }
#[inline] #[inline]
fn is_contiguous(&self) -> bool { unsafe fn is_contiguous(&self) -> bool {
true true
} }
@ -199,7 +199,7 @@ where
} }
#[inline] #[inline]
fn as_slice(&self) -> &[T] { unsafe fn as_slice_unchecked(&self) -> &[T] {
&self.data &self.data
} }
} }
@ -227,7 +227,7 @@ where
} }
#[inline] #[inline]
fn is_contiguous(&self) -> bool { unsafe fn is_contiguous(&self) -> bool {
true true
} }
@ -248,7 +248,7 @@ where
} }
#[inline] #[inline]
fn as_slice(&self) -> &[T] { unsafe fn as_slice_unchecked(&self) -> &[T] {
&self.data &self.data
} }
} }
@ -268,7 +268,7 @@ where
} }
#[inline] #[inline]
fn as_mut_slice(&mut self) -> &mut [T] { unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
&mut self.data[..] &mut self.data[..]
} }
} }
@ -329,7 +329,7 @@ where
} }
#[inline] #[inline]
fn as_mut_slice(&mut self) -> &mut [T] { unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
&mut self.data[..] &mut self.data[..]
} }
} }

View File

@ -127,7 +127,7 @@ fn do_inverse4<T: ComplexField, D: Dim, S: StorageMut<T, D, D>>(
where where
DefaultAllocator: Allocator<T, D, D>, DefaultAllocator: Allocator<T, D, D>,
{ {
let m = m.data.as_slice(); let m = m.as_slice();
out[(0, 0)] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] out[(0, 0)] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15]
+ m[9] * m[7] * m[14] + m[9] * m[7] * m[14]