diff --git a/Cargo.toml b/Cargo.toml index 2c73a64f6e..0b44c3e99e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,7 +158,6 @@ single_char_lifetime_names = "allow" single_match_else = "allow" # TODO: easy fix struct_excessive_bools = "allow" # TODO: bogus lint? trivially_copy_pass_by_ref = "allow" -undocumented_unsafe_blocks = "allow" # TODO: fix me uninlined_format_args = "allow" # TODO: easy fix unnecessary_semicolon = "allow" # TODO: easy fix unnecessary_trailing_comma = "allow" diff --git a/src/common/io/compat.rs b/src/common/io/compat.rs index d026b6d38b..c390b130c1 100644 --- a/src/common/io/compat.rs +++ b/src/common/io/compat.rs @@ -23,6 +23,7 @@ impl tokio::io::AsyncRead for Compat where T: crate::rt::Read, { + /// `poll_read` fn implementation for `Compat`. fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -30,6 +31,13 @@ where ) -> Poll> { let init = tbuf.initialized().len(); let filled = tbuf.filled().len(); + // SAFETY: + // 1. `tbuf.inner_mut()` returns a raw pointer/mutable slice which we wrap into + // a `crate::rt::ReadBuf` that is layout-compatible with the source `tokio::io::ReadBuf`. + // 2. We explicitly restore the `init` and `filled` states from the original buffer + // to maintain the invariant that the new `ReadBuf` tracks the same progress. + // 3. The underlying memory remains valid and uniquely accessible via `tbuf` for + // the duration of this poll operation. let (new_init, new_filled) = unsafe { let mut buf = crate::rt::ReadBuf::uninit(tbuf.inner_mut()); buf.set_init(init); @@ -42,6 +50,11 @@ where }; let n_init = new_init - init; + // SAFETY: + // 1. `tbuf.assume_init(n_init)` is safe because `crate::rt::Read::poll_read` + // guarantees that the bytes written into the buffer were initialized. + // 2. `tbuf.set_filled(new_filled)` is safe because `new_filled` is derived + // directly from the buffer state after the successful read operation. unsafe { tbuf.assume_init(n_init); tbuf.set_filled(new_filled); diff --git a/src/proto/h1/role.rs b/src/proto/h1/role.rs index 544f16ef80..cf4ff8cc16 100644 --- a/src/proto/h1/role.rs +++ b/src/proto/h1/role.rs @@ -43,10 +43,16 @@ macro_rules! header_name { } }}; } - +/// construct `HeaderValue` from a maybe shared expression. macro_rules! header_value { ($bytes:expr) => {{ { + // unsafe used because of the call of `HeaderValue::from_maybe_shared_unchecked`. + // SAFETY: + // 1. The input `$bytes` must be a valid header value as per RFC 7230. + // 2. Specifically, it must not contain any prohibited characters (like `\r`, `\n`, or non-visible ASCII characters outside of allowed ranges). + // 3. This is safe because the caller is responsible for ensuring the byte content + // has been validated or is known to be a constant/static valid header value. unsafe { HeaderValue::from_maybe_shared_unchecked($bytes) } } }}; diff --git a/src/rt/timer.rs b/src/rt/timer.rs index 944e5d34f7..e577379352 100644 --- a/src/rt/timer.rs +++ b/src/rt/timer.rs @@ -116,6 +116,15 @@ impl dyn Sleep { T: Sleep + 'static, { if self.is::() { + // SAFETY: + // 1. `self.is::()` guarantees that the underlying object is indeed of type `T`. + // 2. We use `Pin::into_inner_unchecked` to gain mutable access to the underlying + // value because we are maintaining the pinning contract. + // 3. We convert the reference to `dyn Sleep` to a reference to `T` via raw pointers. + // Since we've verified the type, this is a sound downcast. + // 4. `Pin::new_unchecked` is safe here because the original object was already pinned, + // and by pinning the downcasted reference, we continue to uphold the pinning guarantee + // that the object will not be moved. unsafe { let inner = Pin::into_inner_unchecked(self); Some(Pin::new_unchecked( diff --git a/src/upgrade.rs b/src/upgrade.rs index 352ed5e1c7..8e1c33f6db 100644 --- a/src/upgrade.rs +++ b/src/upgrade.rs @@ -301,10 +301,20 @@ impl dyn Io + Send { let t = TypeId::of::(); self.__hyper_type_id() == t } - + /// downcast a Box wrapped Type to a Box + /// implemented by raw pointer cast. fn __hyper_downcast(self: Box) -> Result, Box> { if self.__hyper_is::() { // Taken from `std::error::Error::downcast()`. + // SAFETY: + // 1. `self.__hyper_is::()` performs a runtime type check (typically via `TypeId`), + // guaranteeing that the underlying concrete type is indeed `T`. + // 2. We use `Box::into_raw` to obtain a pointer to the trait object, which + // has the same memory layout as the underlying concrete type `T` at the + // location identified by the runtime check. + // 3. `Box::from_raw` is safe to call here because we are reconstructing the + // box from the pointer that was originally created by `Box::into_raw`, + // and the type `T` matches the original type of the allocated memory. unsafe { let raw: *mut dyn Io = Box::into_raw(self); Ok(Box::from_raw(raw as *mut T))