Skip to content

Commit bf66005

Browse files
committed
Derive data pointer only after dissolving Box to avoid unsound aliasing.
1 parent 1bb9def commit bf66005

File tree

3 files changed

+47
-19
lines changed

3 files changed

+47
-19
lines changed

src/array.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -512,18 +512,17 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
512512
Self::from_owned_ptr(py, ptr)
513513
}
514514

515-
pub(crate) unsafe fn from_raw_parts<'py, ID, C>(
515+
pub(crate) unsafe fn from_raw_parts<'py, ID>(
516516
py: Python<'py>,
517517
dims: ID,
518518
strides: *const npy_intp,
519519
data_ptr: *const T,
520-
container: C,
520+
container: PySliceContainer,
521521
) -> &'py Self
522522
where
523523
ID: IntoDimension<Dim = D>,
524-
PySliceContainer: From<C>,
525524
{
526-
let container = PyClassInitializer::from(PySliceContainer::from(container))
525+
let container = PyClassInitializer::from(container)
527526
.create_cell(py)
528527
.expect("Failed to create slice container");
529528

@@ -679,7 +678,15 @@ impl<T: Element, D: Dimension> PyArray<T, D> {
679678
pub fn from_owned_array<'py>(py: Python<'py>, mut arr: Array<T, D>) -> &'py Self {
680679
let (strides, dims) = (arr.npy_strides(), arr.raw_dim());
681680
let data_ptr = arr.as_mut_ptr();
682-
unsafe { Self::from_raw_parts(py, dims, strides.as_ptr(), data_ptr, arr) }
681+
unsafe {
682+
Self::from_raw_parts(
683+
py,
684+
dims,
685+
strides.as_ptr(),
686+
data_ptr,
687+
PySliceContainer::from(arr),
688+
)
689+
}
683690
}
684691

685692
/// Get a reference of the specified element if the given index is valid.
@@ -1074,7 +1081,15 @@ impl<D: Dimension> PyArray<PyObject, D> {
10741081
pub fn from_owned_object_array<'py, T>(py: Python<'py>, mut arr: Array<Py<T>, D>) -> &'py Self {
10751082
let (strides, dims) = (arr.npy_strides(), arr.raw_dim());
10761083
let data_ptr = arr.as_mut_ptr() as *const PyObject;
1077-
unsafe { PyArray::from_raw_parts(py, dims, strides.as_ptr(), data_ptr, arr) }
1084+
unsafe {
1085+
Self::from_raw_parts(
1086+
py,
1087+
dims,
1088+
strides.as_ptr(),
1089+
data_ptr,
1090+
PySliceContainer::from(arr),
1091+
)
1092+
}
10781093
}
10791094
}
10801095

src/convert.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::dtype::Element;
1010
use crate::error::MAX_DIMENSIONALITY_ERR;
1111
use crate::npyffi::{self, npy_intp};
1212
use crate::sealed::Sealed;
13+
use crate::slice_container::PySliceContainer;
1314

1415
/// Conversion trait from owning Rust types into [`PyArray`].
1516
///
@@ -48,11 +49,15 @@ impl<T: Element> IntoPyArray for Box<[T]> {
4849
type Item = T;
4950
type Dim = Ix1;
5051

51-
fn into_pyarray<'py>(mut self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
52-
let dims = [self.len()];
52+
fn into_pyarray<'py>(self, py: Python<'py>) -> &'py PyArray<Self::Item, Self::Dim> {
53+
let container = PySliceContainer::from(self);
54+
let dims = [container.len];
5355
let strides = [mem::size_of::<T>() as npy_intp];
54-
let data_ptr = self.as_mut_ptr();
55-
unsafe { PyArray::from_raw_parts(py, dims, strides.as_ptr(), data_ptr, self) }
56+
// The data pointer is derived only after dissolving `Box` into `PySliceContainer`
57+
// to avoid unsound aliasing of Box<[T]> which is currently noalias,
58+
// c.f. https://github.com/rust-lang/unsafe-code-guidelines/issues/326
59+
let data_ptr = container.ptr as *mut T;
60+
unsafe { PyArray::from_raw_parts(py, dims, strides.as_ptr(), data_ptr, container) }
5661
}
5762
}
5863

@@ -64,7 +69,15 @@ impl<T: Element> IntoPyArray for Vec<T> {
6469
let dims = [self.len()];
6570
let strides = [mem::size_of::<T>() as npy_intp];
6671
let data_ptr = self.as_mut_ptr();
67-
unsafe { PyArray::from_raw_parts(py, dims, strides.as_ptr(), data_ptr, self) }
72+
unsafe {
73+
PyArray::from_raw_parts(
74+
py,
75+
dims,
76+
strides.as_ptr(),
77+
data_ptr,
78+
PySliceContainer::from(self),
79+
)
80+
}
6881
}
6982
}
7083

src/slice_container.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use pyo3::pyclass;
66
/// Utility type to safely store `Box<[_]>` or `Vec<_>` on the Python heap
77
#[pyclass]
88
pub(crate) struct PySliceContainer {
9-
ptr: *mut u8,
10-
len: usize,
9+
pub(crate) ptr: *mut u8,
10+
pub(crate) len: usize,
1111
cap: usize,
1212
drop: unsafe fn(*mut u8, usize, usize),
1313
}
@@ -22,13 +22,13 @@ impl<T: Send> From<Box<[T]>> for PySliceContainer {
2222

2323
// FIXME(adamreichold): Use `Box::into_raw` when
2424
// `*mut [T]::{as_mut_ptr, len}` become stable and compatible with our MSRV.
25-
let ptr = data.as_ptr() as *mut u8;
25+
let mut data = mem::ManuallyDrop::new(data);
26+
27+
let ptr = data.as_mut_ptr() as *mut u8;
2628
let len = data.len();
2729
let cap = 0;
2830
let drop = drop_boxed_slice::<T>;
2931

30-
mem::forget(data);
31-
3232
Self {
3333
ptr,
3434
len,
@@ -46,13 +46,13 @@ impl<T: Send> From<Vec<T>> for PySliceContainer {
4646

4747
// FIXME(adamreichold): Use `Vec::into_raw_parts`
4848
// when it becomes stable and compatible with our MSRV.
49-
let ptr = data.as_ptr() as *mut u8;
49+
let mut data = mem::ManuallyDrop::new(data);
50+
51+
let ptr = data.as_mut_ptr() as *mut u8;
5052
let len = data.len();
5153
let cap = data.capacity();
5254
let drop = drop_vec::<T>;
5355

54-
mem::forget(data);
55-
5656
Self {
5757
ptr,
5858
len,

0 commit comments

Comments
 (0)