Skip to content

Commit 0ac54ca

Browse files
authored
Guarantee address in split_off/split_to for empty slices (#740)
Signed-off-by: Alice Ryhl <[email protected]>
1 parent d7c1d65 commit 0ac54ca

File tree

3 files changed

+111
-6
lines changed

3 files changed

+111
-6
lines changed

src/bytes.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,22 @@ impl Bytes {
184184
}
185185
}
186186

187+
/// Creates a new `Bytes` with length zero and the given pointer as the address.
188+
fn new_empty_with_ptr(ptr: *const u8) -> Self {
189+
debug_assert!(!ptr.is_null());
190+
191+
// Detach this pointer's provenance from whichever allocation it came from, and reattach it
192+
// to the provenance of the fake ZST [u8;0] at the same address.
193+
let ptr = without_provenance(ptr as usize);
194+
195+
Bytes {
196+
ptr,
197+
len: 0,
198+
data: AtomicPtr::new(ptr::null_mut()),
199+
vtable: &STATIC_VTABLE,
200+
}
201+
}
202+
187203
/// Returns the number of bytes contained in this `Bytes`.
188204
///
189205
/// # Examples
@@ -366,7 +382,9 @@ impl Bytes {
366382
/// Splits the bytes into two at the given index.
367383
///
368384
/// Afterwards `self` contains elements `[0, at)`, and the returned `Bytes`
369-
/// contains elements `[at, len)`.
385+
/// contains elements `[at, len)`. It's guaranteed that the memory does not
386+
/// move, that is, the address of `self` does not change, and the address of
387+
/// the returned slice is `at` bytes after that.
370388
///
371389
/// This is an `O(1)` operation that just increases the reference count and
372390
/// sets a few indices.
@@ -389,11 +407,11 @@ impl Bytes {
389407
#[must_use = "consider Bytes::truncate if you don't need the other half"]
390408
pub fn split_off(&mut self, at: usize) -> Self {
391409
if at == self.len() {
392-
return Bytes::new();
410+
return Bytes::new_empty_with_ptr(self.ptr.wrapping_add(at));
393411
}
394412

395413
if at == 0 {
396-
return mem::replace(self, Bytes::new());
414+
return mem::replace(self, Bytes::new_empty_with_ptr(self.ptr));
397415
}
398416

399417
assert!(
@@ -438,11 +456,12 @@ impl Bytes {
438456
#[must_use = "consider Bytes::advance if you don't need the other half"]
439457
pub fn split_to(&mut self, at: usize) -> Self {
440458
if at == self.len() {
441-
return mem::replace(self, Bytes::new());
459+
let end_ptr = self.ptr.wrapping_add(at);
460+
return mem::replace(self, Bytes::new_empty_with_ptr(end_ptr));
442461
}
443462

444463
if at == 0 {
445-
return Bytes::new();
464+
return Bytes::new_empty_with_ptr(self.ptr);
446465
}
447466

448467
assert!(
@@ -1426,6 +1445,10 @@ where
14261445
new_addr as *mut u8
14271446
}
14281447

1448+
fn without_provenance(ptr: usize) -> *const u8 {
1449+
core::ptr::null::<u8>().wrapping_add(ptr)
1450+
}
1451+
14291452
// compile-fails
14301453

14311454
/// ```compile_fail

src/bytes_mut.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,9 @@ impl BytesMut {
291291
/// Splits the bytes into two at the given index.
292292
///
293293
/// Afterwards `self` contains elements `[0, at)`, and the returned
294-
/// `BytesMut` contains elements `[at, capacity)`.
294+
/// `BytesMut` contains elements `[at, capacity)`. It's guaranteed that the
295+
/// memory does not move, that is, the address of `self` does not change,
296+
/// and the address of the returned slice is `at` bytes after that.
295297
///
296298
/// This is an `O(1)` operation that just increases the reference count
297299
/// and sets a few indices.

tests/test_bytes.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,3 +1399,83 @@ fn try_reclaim_arc() {
13991399
buf.advance(2);
14001400
assert_eq!(true, buf.try_reclaim(6));
14011401
}
1402+
1403+
#[test]
1404+
fn split_off_empty_addr() {
1405+
let mut buf = Bytes::from(vec![0; 1024]);
1406+
1407+
let ptr_start = buf.as_ptr();
1408+
let ptr_end = ptr_start.wrapping_add(1024);
1409+
1410+
let empty_end = buf.split_off(1024);
1411+
assert_eq!(empty_end.len(), 0);
1412+
assert_eq!(empty_end.as_ptr(), ptr_end);
1413+
1414+
let _ = buf.split_off(0);
1415+
assert_eq!(buf.len(), 0);
1416+
assert_eq!(buf.as_ptr(), ptr_start);
1417+
1418+
// Is miri happy about the provenance?
1419+
let _ = &empty_end[..];
1420+
let _ = &buf[..];
1421+
}
1422+
1423+
#[test]
1424+
fn split_to_empty_addr() {
1425+
let mut buf = Bytes::from(vec![0; 1024]);
1426+
1427+
let ptr_start = buf.as_ptr();
1428+
let ptr_end = ptr_start.wrapping_add(1024);
1429+
1430+
let empty_start = buf.split_to(0);
1431+
assert_eq!(empty_start.len(), 0);
1432+
assert_eq!(empty_start.as_ptr(), ptr_start);
1433+
1434+
let _ = buf.split_to(1024);
1435+
assert_eq!(buf.len(), 0);
1436+
assert_eq!(buf.as_ptr(), ptr_end);
1437+
1438+
// Is miri happy about the provenance?
1439+
let _ = &empty_start[..];
1440+
let _ = &buf[..];
1441+
}
1442+
1443+
#[test]
1444+
fn split_off_empty_addr_mut() {
1445+
let mut buf = BytesMut::from([0; 1024].as_slice());
1446+
1447+
let ptr_start = buf.as_ptr();
1448+
let ptr_end = ptr_start.wrapping_add(1024);
1449+
1450+
let empty_end = buf.split_off(1024);
1451+
assert_eq!(empty_end.len(), 0);
1452+
assert_eq!(empty_end.as_ptr(), ptr_end);
1453+
1454+
let _ = buf.split_off(0);
1455+
assert_eq!(buf.len(), 0);
1456+
assert_eq!(buf.as_ptr(), ptr_start);
1457+
1458+
// Is miri happy about the provenance?
1459+
let _ = &empty_end[..];
1460+
let _ = &buf[..];
1461+
}
1462+
1463+
#[test]
1464+
fn split_to_empty_addr_mut() {
1465+
let mut buf = BytesMut::from([0; 1024].as_slice());
1466+
1467+
let ptr_start = buf.as_ptr();
1468+
let ptr_end = ptr_start.wrapping_add(1024);
1469+
1470+
let empty_start = buf.split_to(0);
1471+
assert_eq!(empty_start.len(), 0);
1472+
assert_eq!(empty_start.as_ptr(), ptr_start);
1473+
1474+
let _ = buf.split_to(1024);
1475+
assert_eq!(buf.len(), 0);
1476+
assert_eq!(buf.as_ptr(), ptr_end);
1477+
1478+
// Is miri happy about the provenance?
1479+
let _ = &empty_start[..];
1480+
let _ = &buf[..];
1481+
}

0 commit comments

Comments
 (0)