Skip to content

Commit e0d8413

Browse files
authored
Switch BufMut::bytes_mut to&mut UninitSlice (#433)
The way BufMut uses MaybeUninit can lead to unsoundness. This replaces MaybeUnit with a type owned by bytes so we can ensure the usage patterns are sound. Refs: #328
1 parent 5a11c78 commit e0d8413

File tree

8 files changed

+241
-47
lines changed

8 files changed

+241
-47
lines changed

src/buf/buf_mut.rs

+21-32
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1-
use crate::buf::{limit, Chain, Limit};
1+
use crate::buf::{limit, Chain, Limit, UninitSlice};
22
#[cfg(feature = "std")]
33
use crate::buf::{writer, Writer};
44

5-
use core::{
6-
cmp,
7-
mem::{self, MaybeUninit},
8-
ptr, usize,
9-
};
5+
use core::{cmp, mem, ptr, usize};
106

117
use alloc::{boxed::Box, vec::Vec};
128

@@ -73,19 +69,14 @@ pub unsafe trait BufMut {
7369
///
7470
/// let mut buf = Vec::with_capacity(16);
7571
///
76-
/// unsafe {
77-
/// // MaybeUninit::as_mut_ptr
78-
/// buf.bytes_mut()[0].as_mut_ptr().write(b'h');
79-
/// buf.bytes_mut()[1].as_mut_ptr().write(b'e');
72+
/// // Write some data
73+
/// buf.bytes_mut()[0..2].copy_from_slice(b"he");
74+
/// unsafe { buf.advance_mut(2) };
8075
///
81-
/// buf.advance_mut(2);
76+
/// // write more bytes
77+
/// buf.bytes_mut()[0..3].copy_from_slice(b"llo");
8278
///
83-
/// buf.bytes_mut()[0].as_mut_ptr().write(b'l');
84-
/// buf.bytes_mut()[1].as_mut_ptr().write(b'l');
85-
/// buf.bytes_mut()[2].as_mut_ptr().write(b'o');
86-
///
87-
/// buf.advance_mut(3);
88-
/// }
79+
/// unsafe { buf.advance_mut(3); }
8980
///
9081
/// assert_eq!(5, buf.len());
9182
/// assert_eq!(buf, b"hello");
@@ -144,14 +135,14 @@ pub unsafe trait BufMut {
144135
///
145136
/// unsafe {
146137
/// // MaybeUninit::as_mut_ptr
147-
/// buf.bytes_mut()[0].as_mut_ptr().write(b'h');
148-
/// buf.bytes_mut()[1].as_mut_ptr().write(b'e');
138+
/// buf.bytes_mut()[0..].as_mut_ptr().write(b'h');
139+
/// buf.bytes_mut()[1..].as_mut_ptr().write(b'e');
149140
///
150141
/// buf.advance_mut(2);
151142
///
152-
/// buf.bytes_mut()[0].as_mut_ptr().write(b'l');
153-
/// buf.bytes_mut()[1].as_mut_ptr().write(b'l');
154-
/// buf.bytes_mut()[2].as_mut_ptr().write(b'o');
143+
/// buf.bytes_mut()[0..].as_mut_ptr().write(b'l');
144+
/// buf.bytes_mut()[1..].as_mut_ptr().write(b'l');
145+
/// buf.bytes_mut()[2..].as_mut_ptr().write(b'o');
155146
///
156147
/// buf.advance_mut(3);
157148
/// }
@@ -167,7 +158,7 @@ pub unsafe trait BufMut {
167158
/// `bytes_mut` returning an empty slice implies that `remaining_mut` will
168159
/// return 0 and `remaining_mut` returning 0 implies that `bytes_mut` will
169160
/// return an empty slice.
170-
fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>];
161+
fn bytes_mut(&mut self) -> &mut UninitSlice;
171162

172163
/// Transfer bytes into `self` from `src` and advance the cursor by the
173164
/// number of bytes written.
@@ -922,7 +913,7 @@ macro_rules! deref_forward_bufmut {
922913
(**self).remaining_mut()
923914
}
924915

925-
fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
916+
fn bytes_mut(&mut self) -> &mut UninitSlice {
926917
(**self).bytes_mut()
927918
}
928919

@@ -1007,9 +998,9 @@ unsafe impl BufMut for &mut [u8] {
1007998
}
1008999

10091000
#[inline]
1010-
fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
1011-
// MaybeUninit is repr(transparent), so safe to transmute
1012-
unsafe { mem::transmute(&mut **self) }
1001+
fn bytes_mut(&mut self) -> &mut UninitSlice {
1002+
// UninitSlice is repr(transparent), so safe to transmute
1003+
unsafe { &mut *(*self as *mut [u8] as *mut _) }
10131004
}
10141005

10151006
#[inline]
@@ -1042,18 +1033,16 @@ unsafe impl BufMut for Vec<u8> {
10421033
}
10431034

10441035
#[inline]
1045-
fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
1046-
use core::slice;
1047-
1036+
fn bytes_mut(&mut self) -> &mut UninitSlice {
10481037
if self.capacity() == self.len() {
10491038
self.reserve(64); // Grow the vec
10501039
}
10511040

10521041
let cap = self.capacity();
10531042
let len = self.len();
10541043

1055-
let ptr = self.as_mut_ptr() as *mut MaybeUninit<u8>;
1056-
unsafe { &mut slice::from_raw_parts_mut(ptr, cap)[len..] }
1044+
let ptr = self.as_mut_ptr();
1045+
unsafe { &mut UninitSlice::from_raw_parts_mut(ptr, cap)[len..] }
10571046
}
10581047

10591048
// Specialize these methods so they can skip checking `remaining_mut`

src/buf/chain.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
use crate::buf::IntoIter;
1+
use crate::buf::{IntoIter, UninitSlice};
22
use crate::{Buf, BufMut};
33

4-
use core::mem::MaybeUninit;
5-
64
#[cfg(feature = "std")]
75
use std::io::IoSlice;
86

@@ -183,7 +181,7 @@ where
183181
self.a.remaining_mut() + self.b.remaining_mut()
184182
}
185183

186-
fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
184+
fn bytes_mut(&mut self) -> &mut UninitSlice {
187185
if self.a.has_remaining_mut() {
188186
self.a.bytes_mut()
189187
} else {

src/buf/limit.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
use crate::buf::UninitSlice;
12
use crate::BufMut;
23

3-
use core::{cmp, mem::MaybeUninit};
4+
use core::cmp;
45

56
/// A `BufMut` adapter which limits the amount of bytes that can be written
67
/// to an underlying buffer.
@@ -60,7 +61,7 @@ unsafe impl<T: BufMut> BufMut for Limit<T> {
6061
cmp::min(self.inner.remaining_mut(), self.limit)
6162
}
6263

63-
fn bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
64+
fn bytes_mut(&mut self) -> &mut UninitSlice {
6465
let bytes = self.inner.bytes_mut();
6566
let end = cmp::min(bytes.len(), self.limit);
6667
&mut bytes[..end]

src/buf/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod limit;
2424
#[cfg(feature = "std")]
2525
mod reader;
2626
mod take;
27+
mod uninit_slice;
2728
mod vec_deque;
2829
#[cfg(feature = "std")]
2930
mod writer;
@@ -34,6 +35,7 @@ pub use self::chain::Chain;
3435
pub use self::iter::IntoIter;
3536
pub use self::limit::Limit;
3637
pub use self::take::Take;
38+
pub use self::uninit_slice::UninitSlice;
3739

3840
#[cfg(feature = "std")]
3941
pub use self::{reader::Reader, writer::Writer};

src/buf/uninit_slice.rs

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
use core::fmt;
2+
use core::mem::MaybeUninit;
3+
use core::ops::{
4+
Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
5+
};
6+
7+
/// Uninitialized byte slice.
8+
///
9+
/// Returned by `BufMut::bytes_mut()`, the referenced byte slice may be
10+
/// uninitialized. The wrapper provides safe access without introducing
11+
/// undefined behavior.
12+
///
13+
/// The safety invariants of this wrapper are:
14+
///
15+
/// 1. Reading from an `UninitSlice` is undefined behavior.
16+
/// 2. Writing uninitialized bytes to an `UninitSlice` is undefined behavior.
17+
///
18+
/// The difference between `&mut UninitSlice` and `&mut [MaybeUninit<u8>]` is
19+
/// that it is possible in safe code to write uninitialized bytes to an
20+
/// `&mut [MaybeUninit<u8>]`, which this type prohibits.
21+
#[repr(transparent)]
22+
pub struct UninitSlice([MaybeUninit<u8>]);
23+
24+
impl UninitSlice {
25+
/// Create a `&mut UninitSlice` from a pointer and a length.
26+
///
27+
/// # Safety
28+
///
29+
/// The caller must ensure that `ptr` references a valid memory region owned
30+
/// by the caller representing a byte slice for the duration of `'a`.
31+
///
32+
/// # Examples
33+
///
34+
/// ```
35+
/// use bytes::buf::UninitSlice;
36+
///
37+
/// let bytes = b"hello world".to_vec();
38+
/// let ptr = bytes.as_ptr() as *mut _;
39+
/// let len = bytes.len();
40+
///
41+
/// let slice = unsafe { UninitSlice::from_raw_parts_mut(ptr, len) };
42+
/// ```
43+
pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut UninitSlice {
44+
let maybe_init: &mut [MaybeUninit<u8>] =
45+
core::slice::from_raw_parts_mut(ptr as *mut _, len);
46+
&mut *(maybe_init as *mut [MaybeUninit<u8>] as *mut UninitSlice)
47+
}
48+
49+
/// Write a single byte at the specified offset.
50+
///
51+
/// # Panics
52+
///
53+
/// The function panics if `index` is out of bounds.
54+
///
55+
/// # Examples
56+
///
57+
/// ```
58+
/// use bytes::buf::UninitSlice;
59+
///
60+
/// let mut data = [b'f', b'o', b'o'];
61+
/// let slice = unsafe { UninitSlice::from_raw_parts_mut(data.as_mut_ptr(), 3) };
62+
///
63+
/// slice.write_byte(0, b'b');
64+
///
65+
/// assert_eq!(b"boo", &data[..]);
66+
/// ```
67+
pub fn write_byte(&mut self, index: usize, byte: u8) {
68+
assert!(index < self.len());
69+
70+
unsafe { self[index..].as_mut_ptr().write(byte) }
71+
}
72+
73+
/// Copies bytes from `src` into `self`.
74+
///
75+
/// The length of `src` must be the same as `self`.
76+
///
77+
/// # Panics
78+
///
79+
/// The function panics if `src` has a different length than `self`.
80+
///
81+
/// # Examples
82+
///
83+
/// ```
84+
/// use bytes::buf::UninitSlice;
85+
///
86+
/// let mut data = [b'f', b'o', b'o'];
87+
/// let slice = unsafe { UninitSlice::from_raw_parts_mut(data.as_mut_ptr(), 3) };
88+
///
89+
/// slice.copy_from_slice(b"bar");
90+
///
91+
/// assert_eq!(b"bar", &data[..]);
92+
/// ```
93+
pub fn copy_from_slice(&mut self, src: &[u8]) {
94+
use core::ptr;
95+
96+
assert_eq!(self.len(), src.len());
97+
98+
unsafe {
99+
ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len());
100+
}
101+
}
102+
103+
/// Return a raw pointer to the slice's buffer.
104+
///
105+
/// # Safety
106+
///
107+
/// The caller **must not** read from the referenced memory and **must not**
108+
/// write **uninitialized** bytes to the slice either.
109+
///
110+
/// # Examples
111+
///
112+
/// ```
113+
/// use bytes::BufMut;
114+
///
115+
/// let mut data = [0, 1, 2];
116+
/// let mut slice = &mut data[..];
117+
/// let ptr = BufMut::bytes_mut(&mut slice).as_mut_ptr();
118+
/// ```
119+
pub fn as_mut_ptr(&mut self) -> *mut u8 {
120+
self.0.as_mut_ptr() as *mut _
121+
}
122+
123+
/// Returns the number of bytes in the slice.
124+
///
125+
/// # Examples
126+
///
127+
/// ```
128+
/// use bytes::BufMut;
129+
///
130+
/// let mut data = [0, 1, 2];
131+
/// let mut slice = &mut data[..];
132+
/// let len = BufMut::bytes_mut(&mut slice).len();
133+
///
134+
/// assert_eq!(len, 3);
135+
/// ```
136+
pub fn len(&self) -> usize {
137+
self.0.len()
138+
}
139+
}
140+
141+
impl fmt::Debug for UninitSlice {
142+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
143+
fmt.debug_struct("UninitSlice[...]").finish()
144+
}
145+
}
146+
147+
macro_rules! impl_index {
148+
($($t:ty),*) => {
149+
$(
150+
impl Index<$t> for UninitSlice {
151+
type Output = UninitSlice;
152+
153+
fn index(&self, index: $t) -> &UninitSlice {
154+
let maybe_uninit: &[MaybeUninit<u8>] = &self.0[index];
155+
unsafe { &*(maybe_uninit as *const [MaybeUninit<u8>] as *const UninitSlice) }
156+
}
157+
}
158+
159+
impl IndexMut<$t> for UninitSlice {
160+
fn index_mut(&mut self, index: $t) -> &mut UninitSlice {
161+
let maybe_uninit: &mut [MaybeUninit<u8>] = &mut self.0[index];
162+
unsafe { &mut *(maybe_uninit as *mut [MaybeUninit<u8>] as *mut UninitSlice) }
163+
}
164+
}
165+
)*
166+
};
167+
}
168+
169+
impl_index!(
170+
Range<usize>,
171+
RangeFrom<usize>,
172+
RangeFull,
173+
RangeInclusive<usize>,
174+
RangeTo<usize>,
175+
RangeToInclusive<usize>
176+
);

src/bytes_mut.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use alloc::{
1111
vec::Vec,
1212
};
1313

14-
use crate::buf::IntoIter;
14+
use crate::buf::{IntoIter, UninitSlice};
1515
use crate::bytes::Vtable;
1616
#[allow(unused)]
1717
use crate::loom::sync::atomic::AtomicMut;
@@ -684,7 +684,7 @@ impl BytesMut {
684684
self.reserve(cnt);
685685

686686
unsafe {
687-
let dst = self.maybe_uninit_bytes();
687+
let dst = self.uninit_slice();
688688
// Reserved above
689689
debug_assert!(dst.len() >= cnt);
690690

@@ -910,12 +910,12 @@ impl BytesMut {
910910
}
911911

912912
#[inline]
913-
fn maybe_uninit_bytes(&mut self) -> &mut [mem::MaybeUninit<u8>] {
913+
fn uninit_slice(&mut self) -> &mut UninitSlice {
914914
unsafe {
915915
let ptr = self.ptr.as_ptr().offset(self.len as isize);
916916
let len = self.cap - self.len;
917917

918-
slice::from_raw_parts_mut(ptr as *mut mem::MaybeUninit<u8>, len)
918+
UninitSlice::from_raw_parts_mut(ptr, len)
919919
}
920920
}
921921
}
@@ -985,11 +985,11 @@ unsafe impl BufMut for BytesMut {
985985
}
986986

987987
#[inline]
988-
fn bytes_mut(&mut self) -> &mut [mem::MaybeUninit<u8>] {
988+
fn bytes_mut(&mut self) -> &mut UninitSlice {
989989
if self.capacity() == self.len() {
990990
self.reserve(64);
991991
}
992-
self.maybe_uninit_bytes()
992+
self.uninit_slice()
993993
}
994994

995995
// Specialize these methods so they can skip checking `remaining_mut`

0 commit comments

Comments
 (0)