Repository: tokio-rs/bytes (Rust)

Issue:
Maybe `BufMut` trait should be `unsafe`?
I was thinking that it's likely that some `unsafe` code will rely on properties of `BufMut`. More specifically:

* That `remaining_mut` returns correct value
* `bytes_mut` always returns the same slice (apart from `advance()`)
* `has_remaining_mut` returns correct value
* `bytes_vectored` fills the correct data

Thus, I'd suggest making the trait `unsafe` to specify that `unsafe` code might rely on those properties and the implementors must ensure they hold.


Discussion:
What does making the trait unsafe gain over making individual fns unsafe?
`unsafe trait` basically declares "this trait is `unsafe` to *implement*", thus other `unsafe` code might rely on it working properly. `unsafe fn` means "this function is unsafe to call"

They are different things and I believe making `BufMut` `unsafe` would make it more powerul.
I don't know about making the trait itself `unsafe`. Seems heavy-handed. Are there actual situations that would require this?
I tried to search for them and I must say I didn't find anything that clearly, undeniably relies on correctness of the implementation. Or in other words, I didn't find any code in the wild that is safe and would cause UB if the trait wasn't implemented correctly. Yet. Keeping it safe seems like a footgun, but admittedly there being at least one `unsafe fn` is "heads up" sign.
I think it's reasonable to judge that the correctness of calling an unsafe trait method is subject to the implementation of that trait complying with the trait's documented interface, but this does seem a bit unclear; maybe something to raise at the rust guidelines level?
According to https://doc.rust-lang.org/nomicon/safe-unsafe-meaning.html

> The decision of whether to mark your own traits `unsafe` depends on the same sort of consideration. If `unsafe` code can't reasonably expect to defend against a broken implementation of the trait, then marking the trait `unsafe` is a reasonable choice.

So it seems that the question boils down to can `unsafe` code reasonably defend against e.g. `bytes_mut` returning a different buffer each time? I guess that might be pretty difficult.

One thing that occurred to me is that while a method is `unsafe`, implementor could forget about other methods having special restrictions. But maybe I'm too paranoid?
What is an example of unsafe code that relies/would rely on those requirements?
Let's assume for instance that the ability to rewind gets implemented by suggested traits as discussed in #330. Naturally, the desire to add `rewindable()` combinator, which creates a wrapper type around the original `BufMut` to add rewinable features comes. Without the requirements being fulfilled, such wrapper might cause UB because of relying on the fact that `bytes_mut` returns the same bufer every time its called.
What is some specific code that relies on that specific requirement?
```rust
pub struct Rewindable<B: BufMut> {
    buf: B,
    offset: usize,
}

impl<B: BufMut> Rewindable<B> {
    pub fn written(&mut self) -> &mut [u8] {
        unsafe {
                // Safety: we track how much data was written in `offset` field,
                // So unless `bytes_mut` returns a different buffer, then that memory is initialized.
                self.buf.bytes_mut()[..self.offset].assume_init_mut()
        }
    }
}

impl<B: BufMut> BufMut for Rewindable<B> {
// ...
unsafe fn advance(&mut self, amount: usize) {
self.offset += amount;
}
}
```
That won't work with non-contiguous BufMut implementations anyway.
Double checking again, `Chain` [relies](https://github.com/tokio-rs/bytes/blob/96268a80d44db04142ff168506e9bff8682d9a15/src/buf/ext/chain.rs#L197) on `remaining_mut` to have correct return value. If `remaining_mut` of `a` returns `0` despite still having remaining data, then `advance_mut` will be called on `b` without data being written there.
But it is possible to implement `BufMut` completely safely... The `BufMut` implementation can only expose UB when it, itself, uses `unsafe`. There is nothing inherently unsafe about implementing `BufMut`.
In that case `Chain` is unsound, right?
How is `Chain` unsound?
>  If `remaining_mut` of `a` returns `0` despite still having remaining data, then `advance_mut` will be called on `b` without data being written there.
@Kixunil You are correct that a caller of `BufMut` is not able to defend against a broken implementation. According to the snippet you pasted, `BufMut` should be `unsafe`.



Patch:
diff --git a/src/buf/buf_mut.rs b/src/buf/buf_mut.rs
index 4f6e47d32..026bcad02 100644
--- a/src/buf/buf_mut.rs
+++ b/src/buf/buf_mut.rs
@@ -30,7 +30,7 @@ use alloc::{boxed::Box, vec::Vec};
 ///
 /// assert_eq!(buf, b"hello world");
 /// ```
-pub trait BufMut {
+pub unsafe trait BufMut {
     /// Returns the number of bytes that can be written from the current
     /// position until the end of the buffer is reached.
     ///
@@ -992,15 +992,15 @@ macro_rules! deref_forward_bufmut {
     };
 }
 
-impl<T: BufMut + ?Sized> BufMut for &mut T {
+unsafe impl<T: BufMut + ?Sized> BufMut for &mut T {
     deref_forward_bufmut!();
 }
 
-impl<T: BufMut + ?Sized> BufMut for Box<T> {
+unsafe impl<T: BufMut + ?Sized> BufMut for Box<T> {
     deref_forward_bufmut!();
 }
 
-impl BufMut for &mut [u8] {
+unsafe impl BufMut for &mut [u8] {
     #[inline]
     fn remaining_mut(&self) -> usize {
         self.len()
@@ -1020,7 +1020,7 @@ impl BufMut for &mut [u8] {
     }
 }
 
-impl BufMut for Vec<u8> {
+unsafe impl BufMut for Vec<u8> {
     #[inline]
     fn remaining_mut(&self) -> usize {
         usize::MAX - self.len()
diff --git a/src/buf/chain.rs b/src/buf/chain.rs
index 020bf085e..cc2c944b7 100644
--- a/src/buf/chain.rs
+++ b/src/buf/chain.rs
@@ -174,7 +174,7 @@ where
     }
 }
 
-impl<T, U> BufMut for Chain<T, U>
+unsafe impl<T, U> BufMut for Chain<T, U>
 where
     T: BufMut,
     U: BufMut,
diff --git a/src/buf/limit.rs b/src/buf/limit.rs
index a36eceeef..c6ed3c7b1 100644
--- a/src/buf/limit.rs
+++ b/src/buf/limit.rs
@@ -55,7 +55,7 @@ impl<T> Limit<T> {
     }
 }
 
-impl<T: BufMut> BufMut for Limit<T> {
+unsafe impl<T: BufMut> BufMut for Limit<T> {
     fn remaining_mut(&self) -> usize {
         cmp::min(self.inner.remaining_mut(), self.limit)
     }
diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs
index a7a8e5798..16cb72c2b 100644
--- a/src/bytes_mut.rs
+++ b/src/bytes_mut.rs
@@ -966,7 +966,7 @@ impl Buf for BytesMut {
     }
 }
 
-impl BufMut for BytesMut {
+unsafe impl BufMut for BytesMut {
     #[inline]
     fn remaining_mut(&self) -> usize {
         usize::MAX - self.len()


Test patch:
diff --git a/tests/test_buf_mut.rs b/tests/test_buf_mut.rs
index 10c526d11..e9948839a 100644
--- a/tests/test_buf_mut.rs
+++ b/tests/test_buf_mut.rs
@@ -75,7 +75,7 @@ fn test_mut_slice() {
 fn test_deref_bufmut_forwards() {
     struct Special;
 
-    impl BufMut for Special {
+    unsafe impl BufMut for Special {
         fn remaining_mut(&self) -> usize {
             unreachable!("remaining_mut");
         }