Skip to content

Commit 38add0b

Browse files
authored
Fix potential undoundness with Storage::as_slice and Storage::as_mut_slice (#905)
1 parent d64e799 commit 38add0b

9 files changed

Lines changed: 85 additions & 65 deletions

File tree

src/base/array_storage.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ where
7979
}
8080

8181
#[inline]
82-
fn is_contiguous(&self) -> bool {
82+
unsafe fn is_contiguous(&self) -> bool {
8383
true
8484
}
8585

@@ -101,8 +101,8 @@ where
101101
}
102102

103103
#[inline]
104-
fn as_slice(&self) -> &[T] {
105-
unsafe { std::slice::from_raw_parts(self.ptr(), R * C) }
104+
unsafe fn as_slice_unchecked(&self) -> &[T] {
105+
std::slice::from_raw_parts(self.ptr(), R * C)
106106
}
107107
}
108108

@@ -118,8 +118,8 @@ where
118118
}
119119

120120
#[inline]
121-
fn as_mut_slice(&mut self) -> &mut [T] {
122-
unsafe { std::slice::from_raw_parts_mut(self.ptr_mut(), R * C) }
121+
unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
122+
std::slice::from_raw_parts_mut(self.ptr_mut(), R * C)
123123
}
124124
}
125125

src/base/blas.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -344,13 +344,17 @@ where
344344
let rstride1 = self.strides().0;
345345
let rstride2 = x.strides().0;
346346

347-
let y = self.data.as_mut_slice();
348-
let x = x.data.as_slice();
347+
unsafe {
348+
// SAFETY: the conversion to slices is OK because we access the
349+
// elements taking the strides into account.
350+
let y = self.data.as_mut_slice_unchecked();
351+
let x = x.data.as_slice_unchecked();
349352

350-
if !b.is_zero() {
351-
array_axcpy(y, a, x, c, b, rstride1, rstride2, x.len());
352-
} else {
353-
array_axc(y, a, x, c, rstride1, rstride2, x.len());
353+
if !b.is_zero() {
354+
array_axcpy(y, a, x, c, b, rstride1, rstride2, x.len());
355+
} else {
356+
array_axc(y, a, x, c, rstride1, rstride2, x.len());
357+
}
354358
}
355359
}
356360

src/base/default_allocator.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::base::array_storage::ArrayStorage;
1616
#[cfg(any(feature = "alloc", feature = "std"))]
1717
use crate::base::dimension::Dynamic;
1818
use crate::base::dimension::{Dim, DimName};
19-
use crate::base::storage::{Storage, StorageMut};
19+
use crate::base::storage::{ContiguousStorageMut, Storage, StorageMut};
2020
#[cfg(any(feature = "std", feature = "alloc"))]
2121
use crate::base::vec_storage::VecStorage;
2222
use crate::base::Scalar;

src/base/edition.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::base::constraint::{DimEq, SameNumberOfColumns, SameNumberOfRows, Shap
1111
#[cfg(any(feature = "std", feature = "alloc"))]
1212
use crate::base::dimension::Dynamic;
1313
use crate::base::dimension::{Const, Dim, DimAdd, DimDiff, DimMin, DimMinimum, DimSub, DimSum, U1};
14-
use crate::base::storage::{ReshapableStorage, Storage, StorageMut};
14+
use crate::base::storage::{ContiguousStorageMut, ReshapableStorage, Storage, StorageMut};
1515
use crate::base::{DefaultAllocator, Matrix, OMatrix, RowVector, Scalar, Vector};
1616

1717
/// # Rows and columns extraction

src/base/matrix_slice.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ macro_rules! storage_impl(
132132
}
133133

134134
#[inline]
135-
fn is_contiguous(&self) -> bool {
135+
unsafe fn is_contiguous(&self) -> bool {
136136
// Common cases that can be deduced at compile-time even if one of the dimensions
137137
// is Dynamic.
138138
if (RStride::is::<U1>() && C::is::<U1>()) || // Column vector.
@@ -162,14 +162,14 @@ macro_rules! storage_impl(
162162
}
163163

164164
#[inline]
165-
fn as_slice(&self) -> &[T] {
165+
unsafe fn as_slice_unchecked(&self) -> &[T] {
166166
let (nrows, ncols) = self.shape();
167167
if nrows.value() != 0 && ncols.value() != 0 {
168168
let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1);
169-
unsafe { slice::from_raw_parts(self.ptr, sz + 1) }
169+
slice::from_raw_parts(self.ptr, sz + 1)
170170
}
171171
else {
172-
unsafe { slice::from_raw_parts(self.ptr, 0) }
172+
slice::from_raw_parts(self.ptr, 0)
173173
}
174174
}
175175
}
@@ -187,13 +187,13 @@ unsafe impl<'a, T: Scalar, R: Dim, C: Dim, RStride: Dim, CStride: Dim> StorageMu
187187
}
188188

189189
#[inline]
190-
fn as_mut_slice(&mut self) -> &mut [T] {
190+
unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
191191
let (nrows, ncols) = self.shape();
192192
if nrows.value() != 0 && ncols.value() != 0 {
193193
let sz = self.linear_index(nrows.value() - 1, ncols.value() - 1);
194-
unsafe { slice::from_raw_parts_mut(self.ptr, sz + 1) }
194+
slice::from_raw_parts_mut(self.ptr, sz + 1)
195195
} else {
196-
unsafe { slice::from_raw_parts_mut(self.ptr, 0) }
196+
slice::from_raw_parts_mut(self.ptr, 0)
197197
}
198198
}
199199
}

src/base/ops.rs

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -158,20 +158,17 @@ macro_rules! componentwise_binop_impl(
158158

159159
// This is the most common case and should be deduced at compile-time.
160160
// TODO: use specialization instead?
161-
if self.data.is_contiguous() && rhs.data.is_contiguous() && out.data.is_contiguous() {
162-
let arr1 = self.data.as_slice();
163-
let arr2 = rhs.data.as_slice();
164-
let out = out.data.as_mut_slice();
165-
for i in 0 .. arr1.len() {
166-
unsafe {
161+
unsafe {
162+
if self.data.is_contiguous() && rhs.data.is_contiguous() && out.data.is_contiguous() {
163+
let arr1 = self.data.as_slice_unchecked();
164+
let arr2 = rhs.data.as_slice_unchecked();
165+
let out = out.data.as_mut_slice_unchecked();
166+
for i in 0 .. arr1.len() {
167167
*out.get_unchecked_mut(i) = arr1.get_unchecked(i).inlined_clone().$method(arr2.get_unchecked(i).inlined_clone());
168168
}
169-
}
170-
}
171-
else {
172-
for j in 0 .. self.ncols() {
173-
for i in 0 .. self.nrows() {
174-
unsafe {
169+
} else {
170+
for j in 0 .. self.ncols() {
171+
for i in 0 .. self.nrows() {
175172
let val = self.get_unchecked((i, j)).inlined_clone().$method(rhs.get_unchecked((i, j)).inlined_clone());
176173
*out.get_unchecked_mut((i, j)) = val;
177174
}
@@ -191,19 +188,17 @@ macro_rules! componentwise_binop_impl(
191188

192189
// This is the most common case and should be deduced at compile-time.
193190
// TODO: use specialization instead?
194-
if self.data.is_contiguous() && rhs.data.is_contiguous() {
195-
let arr1 = self.data.as_mut_slice();
196-
let arr2 = rhs.data.as_slice();
197-
for i in 0 .. arr2.len() {
198-
unsafe {
191+
unsafe {
192+
if self.data.is_contiguous() && rhs.data.is_contiguous() {
193+
let arr1 = self.data.as_mut_slice_unchecked();
194+
let arr2 = rhs.data.as_slice_unchecked();
195+
196+
for i in 0 .. arr2.len() {
199197
arr1.get_unchecked_mut(i).$method_assign(arr2.get_unchecked(i).inlined_clone());
200198
}
201-
}
202-
}
203-
else {
204-
for j in 0 .. rhs.ncols() {
205-
for i in 0 .. rhs.nrows() {
206-
unsafe {
199+
} else {
200+
for j in 0 .. rhs.ncols() {
201+
for i in 0 .. rhs.nrows() {
207202
self.get_unchecked_mut((i, j)).$method_assign(rhs.get_unchecked((i, j)).inlined_clone())
208203
}
209204
}
@@ -221,20 +216,18 @@ macro_rules! componentwise_binop_impl(
221216

222217
// This is the most common case and should be deduced at compile-time.
223218
// TODO: use specialization instead?
224-
if self.data.is_contiguous() && rhs.data.is_contiguous() {
225-
let arr1 = self.data.as_slice();
226-
let arr2 = rhs.data.as_mut_slice();
227-
for i in 0 .. arr1.len() {
228-
unsafe {
219+
unsafe {
220+
if self.data.is_contiguous() && rhs.data.is_contiguous() {
221+
let arr1 = self.data.as_slice_unchecked();
222+
let arr2 = rhs.data.as_mut_slice_unchecked();
223+
224+
for i in 0 .. arr1.len() {
229225
let res = arr1.get_unchecked(i).inlined_clone().$method(arr2.get_unchecked(i).inlined_clone());
230226
*arr2.get_unchecked_mut(i) = res;
231227
}
232-
}
233-
}
234-
else {
235-
for j in 0 .. self.ncols() {
236-
for i in 0 .. self.nrows() {
237-
unsafe {
228+
} else {
229+
for j in 0 .. self.ncols() {
230+
for i in 0 .. self.nrows() {
238231
let r = rhs.get_unchecked_mut((i, j));
239232
*r = self.get_unchecked((i, j)).inlined_clone().$method(r.inlined_clone())
240233
}

src/base/storage.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,19 @@ pub unsafe trait Storage<T: Scalar, R: Dim, C: Dim = U1>: Debug + Sized {
9494
}
9595

9696
/// Indicates whether this data buffer stores its elements contiguously.
97-
fn is_contiguous(&self) -> bool;
97+
///
98+
/// This method is unsafe because unsafe code relies on this properties to performe
99+
/// some low-lever optimizations.
100+
unsafe fn is_contiguous(&self) -> bool;
98101

99102
/// Retrieves the data buffer as a contiguous slice.
100103
///
101104
/// The matrix components may not be stored in a contiguous way, depending on the strides.
102-
fn as_slice(&self) -> &[T];
105+
/// This method is unsafe because this can yield to invalid aliasing when called on some pairs
106+
/// of matrix slices originating from the same matrix with strides.
107+
///
108+
/// Call the safe alternative `matrix.as_slice()` instead.
109+
unsafe fn as_slice_unchecked(&self) -> &[T];
103110

104111
/// Builds a matrix data storage that does not contain any reference.
105112
fn into_owned(self) -> Owned<T, R, C>
@@ -165,8 +172,12 @@ pub unsafe trait StorageMut<T: Scalar, R: Dim, C: Dim = U1>: Storage<T, R, C> {
165172

166173
/// Retrieves the mutable data buffer as a contiguous slice.
167174
///
168-
/// Matrix components may not be contiguous, depending on its strides.
169-
fn as_mut_slice(&mut self) -> &mut [T];
175+
/// Matrix components may not be contiguous, depending on its strides.
176+
///
177+
/// The matrix components may not be stored in a contiguous way, depending on the strides.
178+
/// This method is unsafe because this can yield to invalid aliasing when called on some pairs
179+
/// of matrix slices originating from the same matrix with strides.
180+
unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T];
170181
}
171182

172183
/// 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> {
177188
pub unsafe trait ContiguousStorage<T: Scalar, R: Dim, C: Dim = U1>:
178189
Storage<T, R, C>
179190
{
191+
/// Converts this data storage to a contiguous slice.
192+
fn as_slice(&self) -> &[T] {
193+
// SAFETY: this is safe because this trait guarantees the fact
194+
// that the data is stored contiguously.
195+
unsafe { self.as_slice_unchecked() }
196+
}
180197
}
181198

182199
/// 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>:
187204
pub unsafe trait ContiguousStorageMut<T: Scalar, R: Dim, C: Dim = U1>:
188205
ContiguousStorage<T, R, C> + StorageMut<T, R, C>
189206
{
207+
/// Converts this data storage to a contiguous mutable slice.
208+
fn as_mut_slice(&mut self) -> &mut [T] {
209+
// SAFETY: this is safe because this trait guarantees the fact
210+
// that the data is stored contiguously.
211+
unsafe { self.as_mut_slice_unchecked() }
212+
}
190213
}
191214

192215
/// A matrix storage that can be reshaped in-place.

src/base/vec_storage.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ where
178178
}
179179

180180
#[inline]
181-
fn is_contiguous(&self) -> bool {
181+
unsafe fn is_contiguous(&self) -> bool {
182182
true
183183
}
184184

@@ -199,7 +199,7 @@ where
199199
}
200200

201201
#[inline]
202-
fn as_slice(&self) -> &[T] {
202+
unsafe fn as_slice_unchecked(&self) -> &[T] {
203203
&self.data
204204
}
205205
}
@@ -227,7 +227,7 @@ where
227227
}
228228

229229
#[inline]
230-
fn is_contiguous(&self) -> bool {
230+
unsafe fn is_contiguous(&self) -> bool {
231231
true
232232
}
233233

@@ -248,7 +248,7 @@ where
248248
}
249249

250250
#[inline]
251-
fn as_slice(&self) -> &[T] {
251+
unsafe fn as_slice_unchecked(&self) -> &[T] {
252252
&self.data
253253
}
254254
}
@@ -268,7 +268,7 @@ where
268268
}
269269

270270
#[inline]
271-
fn as_mut_slice(&mut self) -> &mut [T] {
271+
unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
272272
&mut self.data[..]
273273
}
274274
}
@@ -329,7 +329,7 @@ where
329329
}
330330

331331
#[inline]
332-
fn as_mut_slice(&mut self) -> &mut [T] {
332+
unsafe fn as_mut_slice_unchecked(&mut self) -> &mut [T] {
333333
&mut self.data[..]
334334
}
335335
}

src/linalg/inverse.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ fn do_inverse4<T: ComplexField, D: Dim, S: StorageMut<T, D, D>>(
127127
where
128128
DefaultAllocator: Allocator<T, D, D>,
129129
{
130-
let m = m.data.as_slice();
130+
let m = m.as_slice();
131131

132132
out[(0, 0)] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15]
133133
+ m[9] * m[7] * m[14]

0 commit comments

Comments
 (0)