mirror of
https://github.com/actix/actix-web.git
synced 2024-12-21 23:56:35 +00:00
document all remaining unsafe usages (#1642)
adds some debug assertions where appropriate
This commit is contained in:
parent
4e321595bc
commit
9a9d4b182e
7 changed files with 84 additions and 24 deletions
|
@ -17,7 +17,7 @@ const DATE_VALUE_LENGTH: usize = 29;
|
||||||
pub enum KeepAlive {
|
pub enum KeepAlive {
|
||||||
/// Keep alive in seconds
|
/// Keep alive in seconds
|
||||||
Timeout(usize),
|
Timeout(usize),
|
||||||
/// Relay on OS to shutdown tcp connection
|
/// Rely on OS to shutdown tcp connection
|
||||||
Os,
|
Os,
|
||||||
/// Disabled
|
/// Disabled
|
||||||
Disabled,
|
Disabled,
|
||||||
|
@ -209,6 +209,7 @@ impl Date {
|
||||||
date.update();
|
date.update();
|
||||||
date
|
date
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self) {
|
fn update(&mut self) {
|
||||||
self.pos = 0;
|
self.pos = 0;
|
||||||
write!(
|
write!(
|
||||||
|
|
|
@ -76,12 +76,14 @@ pub(crate) trait MessageType: Sized {
|
||||||
let name =
|
let name =
|
||||||
HeaderName::from_bytes(&slice[idx.name.0..idx.name.1]).unwrap();
|
HeaderName::from_bytes(&slice[idx.name.0..idx.name.1]).unwrap();
|
||||||
|
|
||||||
// SAFETY: httparse checks header value is valid UTF-8
|
// SAFETY: httparse already checks header value is only visible ASCII bytes
|
||||||
|
// from_maybe_shared_unchecked contains debug assertions so they are omitted here
|
||||||
let value = unsafe {
|
let value = unsafe {
|
||||||
HeaderValue::from_maybe_shared_unchecked(
|
HeaderValue::from_maybe_shared_unchecked(
|
||||||
slice.slice(idx.value.0..idx.value.1),
|
slice.slice(idx.value.0..idx.value.1),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
header::CONTENT_LENGTH => {
|
header::CONTENT_LENGTH => {
|
||||||
if let Ok(s) = value.to_str() {
|
if let Ok(s) = value.to_str() {
|
||||||
|
|
|
@ -314,11 +314,15 @@ where
|
||||||
Poll::Ready(Err(err)) => return Err(DispatchError::Io(err)),
|
Poll::Ready(Err(err)) => return Err(DispatchError::Io(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if written == write_buf.len() {
|
if written == write_buf.len() {
|
||||||
|
// SAFETY: setting length to 0 is safe
|
||||||
|
// skips one length check vs truncate
|
||||||
unsafe { write_buf.set_len(0) }
|
unsafe { write_buf.set_len(0) }
|
||||||
} else {
|
} else {
|
||||||
write_buf.advance(written);
|
write_buf.advance(written);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,89 +129,133 @@ pub(crate) trait MessageType: Sized {
|
||||||
.chain(extra_headers.inner.iter());
|
.chain(extra_headers.inner.iter());
|
||||||
|
|
||||||
// write headers
|
// write headers
|
||||||
let mut pos = 0;
|
|
||||||
let mut has_date = false;
|
let mut has_date = false;
|
||||||
let mut remaining = dst.capacity() - dst.len();
|
|
||||||
let mut buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
|
let mut buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
|
||||||
|
let mut remaining = dst.capacity() - dst.len();
|
||||||
|
|
||||||
|
// tracks bytes written since last buffer resize
|
||||||
|
// since buf is a raw pointer to a bytes container storage but is written to without the
|
||||||
|
// container's knowledge, this is used to sync the containers cursor after data is written
|
||||||
|
let mut pos = 0;
|
||||||
|
|
||||||
for (key, value) in headers {
|
for (key, value) in headers {
|
||||||
match *key {
|
match *key {
|
||||||
CONNECTION => continue,
|
CONNECTION => continue,
|
||||||
TRANSFER_ENCODING | CONTENT_LENGTH if skip_len => continue,
|
TRANSFER_ENCODING | CONTENT_LENGTH if skip_len => continue,
|
||||||
DATE => {
|
DATE => has_date = true,
|
||||||
has_date = true;
|
|
||||||
}
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let k = key.as_str().as_bytes();
|
let k = key.as_str().as_bytes();
|
||||||
|
let k_len = k.len();
|
||||||
|
|
||||||
match value {
|
match value {
|
||||||
map::Value::One(ref val) => {
|
map::Value::One(ref val) => {
|
||||||
let v = val.as_ref();
|
let v = val.as_ref();
|
||||||
let v_len = v.len();
|
let v_len = v.len();
|
||||||
let k_len = k.len();
|
|
||||||
|
// key length + value length + colon + space + \r\n
|
||||||
let len = k_len + v_len + 4;
|
let len = k_len + v_len + 4;
|
||||||
|
|
||||||
if len > remaining {
|
if len > remaining {
|
||||||
|
// not enough room in buffer for this header; reserve more space
|
||||||
|
|
||||||
|
// SAFETY: all the bytes written up to position "pos" are initialized
|
||||||
|
// the written byte count and pointer advancement are kept in sync
|
||||||
unsafe {
|
unsafe {
|
||||||
dst.advance_mut(pos);
|
dst.advance_mut(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = 0;
|
pos = 0;
|
||||||
dst.reserve(len * 2);
|
dst.reserve(len * 2);
|
||||||
remaining = dst.capacity() - dst.len();
|
remaining = dst.capacity() - dst.len();
|
||||||
|
|
||||||
|
// re-assign buf raw pointer since it's possible that the buffer was
|
||||||
|
// reallocated and/or resized
|
||||||
buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
|
buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
|
||||||
}
|
}
|
||||||
// use upper Camel-Case
|
|
||||||
|
// SAFETY: on each write, it is enough to ensure that the advancement of the
|
||||||
|
// cursor matches the number of bytes written
|
||||||
unsafe {
|
unsafe {
|
||||||
|
// use upper Camel-Case
|
||||||
if camel_case {
|
if camel_case {
|
||||||
write_camel_case(k, from_raw_parts_mut(buf, k_len))
|
write_camel_case(k, from_raw_parts_mut(buf, k_len))
|
||||||
} else {
|
} else {
|
||||||
write_data(k, buf, k_len)
|
write_data(k, buf, k_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = buf.add(k_len);
|
buf = buf.add(k_len);
|
||||||
|
|
||||||
write_data(b": ", buf, 2);
|
write_data(b": ", buf, 2);
|
||||||
buf = buf.add(2);
|
buf = buf.add(2);
|
||||||
|
|
||||||
write_data(v, buf, v_len);
|
write_data(v, buf, v_len);
|
||||||
buf = buf.add(v_len);
|
buf = buf.add(v_len);
|
||||||
|
|
||||||
write_data(b"\r\n", buf, 2);
|
write_data(b"\r\n", buf, 2);
|
||||||
buf = buf.add(2);
|
buf = buf.add(2);
|
||||||
pos += len;
|
|
||||||
remaining -= len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pos += len;
|
||||||
|
remaining -= len;
|
||||||
}
|
}
|
||||||
|
|
||||||
map::Value::Multi(ref vec) => {
|
map::Value::Multi(ref vec) => {
|
||||||
for val in vec {
|
for val in vec {
|
||||||
let v = val.as_ref();
|
let v = val.as_ref();
|
||||||
let v_len = v.len();
|
let v_len = v.len();
|
||||||
let k_len = k.len();
|
|
||||||
let len = k_len + v_len + 4;
|
let len = k_len + v_len + 4;
|
||||||
|
|
||||||
if len > remaining {
|
if len > remaining {
|
||||||
|
// SAFETY: all the bytes written up to position "pos" are initialized
|
||||||
|
// the written byte count and pointer advancement are kept in sync
|
||||||
unsafe {
|
unsafe {
|
||||||
dst.advance_mut(pos);
|
dst.advance_mut(pos);
|
||||||
}
|
}
|
||||||
pos = 0;
|
pos = 0;
|
||||||
dst.reserve(len * 2);
|
dst.reserve(len * 2);
|
||||||
remaining = dst.capacity() - dst.len();
|
remaining = dst.capacity() - dst.len();
|
||||||
|
|
||||||
|
// re-assign buf raw pointer since it's possible that the buffer was
|
||||||
|
// reallocated and/or resized
|
||||||
buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
|
buf = dst.bytes_mut().as_mut_ptr() as *mut u8;
|
||||||
}
|
}
|
||||||
// use upper Camel-Case
|
|
||||||
|
// SAFETY: on each write, it is enough to ensure that the advancement of
|
||||||
|
// the cursor matches the number of bytes written
|
||||||
unsafe {
|
unsafe {
|
||||||
if camel_case {
|
if camel_case {
|
||||||
write_camel_case(k, from_raw_parts_mut(buf, k_len));
|
write_camel_case(k, from_raw_parts_mut(buf, k_len));
|
||||||
} else {
|
} else {
|
||||||
write_data(k, buf, k_len);
|
write_data(k, buf, k_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = buf.add(k_len);
|
buf = buf.add(k_len);
|
||||||
|
|
||||||
write_data(b": ", buf, 2);
|
write_data(b": ", buf, 2);
|
||||||
buf = buf.add(2);
|
buf = buf.add(2);
|
||||||
|
|
||||||
write_data(v, buf, v_len);
|
write_data(v, buf, v_len);
|
||||||
buf = buf.add(v_len);
|
buf = buf.add(v_len);
|
||||||
|
|
||||||
write_data(b"\r\n", buf, 2);
|
write_data(b"\r\n", buf, 2);
|
||||||
buf = buf.add(2);
|
buf = buf.add(2);
|
||||||
};
|
};
|
||||||
|
|
||||||
pos += len;
|
pos += len;
|
||||||
remaining -= len;
|
remaining -= len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// final cursor synchronization with the bytes container
|
||||||
|
//
|
||||||
|
// SAFETY: all the bytes written up to position "pos" are initialized
|
||||||
|
// the written byte count and pointer advancement are kept in sync
|
||||||
unsafe {
|
unsafe {
|
||||||
dst.advance_mut(pos);
|
dst.advance_mut(pos);
|
||||||
}
|
}
|
||||||
|
@ -477,7 +521,10 @@ impl<'a> io::Write for Writer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// Callers must ensure that the given length matches given value length.
|
||||||
unsafe fn write_data(value: &[u8], buf: *mut u8, len: usize) {
|
unsafe fn write_data(value: &[u8], buf: *mut u8, len: usize) {
|
||||||
|
debug_assert_eq!(value.len(), len);
|
||||||
copy_nonoverlapping(value.as_ptr(), buf, len);
|
copy_nonoverlapping(value.as_ptr(), buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -227,9 +227,11 @@ where
|
||||||
if !has_date {
|
if !has_date {
|
||||||
let mut bytes = BytesMut::with_capacity(29);
|
let mut bytes = BytesMut::with_capacity(29);
|
||||||
self.config.set_date_header(&mut bytes);
|
self.config.set_date_header(&mut bytes);
|
||||||
res.headers_mut().insert(DATE, unsafe {
|
res.headers_mut().insert(
|
||||||
HeaderValue::from_maybe_shared_unchecked(bytes.freeze())
|
DATE,
|
||||||
});
|
// SAFETY: serialized date-times are known ASCII strings
|
||||||
|
unsafe { HeaderValue::from_maybe_shared_unchecked(bytes.freeze()) },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
|
|
|
@ -38,7 +38,7 @@ macro_rules! downcast {
|
||||||
/// Downcasts generic body to a specific type.
|
/// Downcasts generic body to a specific type.
|
||||||
pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> {
|
pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> {
|
||||||
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
|
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
|
||||||
// Safety: external crates cannot override the default
|
// SAFETY: external crates cannot override the default
|
||||||
// implementation of `__private_get_type_id__`, since
|
// implementation of `__private_get_type_id__`, since
|
||||||
// it requires returning a private type. We can therefore
|
// it requires returning a private type. We can therefore
|
||||||
// rely on the returned `TypeId`, which ensures that this
|
// rely on the returned `TypeId`, which ensures that this
|
||||||
|
@ -48,10 +48,11 @@ macro_rules! downcast {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Downcasts a generic body to a mutable specific type.
|
/// Downcasts a generic body to a mutable specific type.
|
||||||
pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> {
|
pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> {
|
||||||
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
|
if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
|
||||||
// Safety: external crates cannot override the default
|
// SAFETY: external crates cannot override the default
|
||||||
// implementation of `__private_get_type_id__`, since
|
// implementation of `__private_get_type_id__`, since
|
||||||
// it requires returning a private type. We can therefore
|
// it requires returning a private type. We can therefore
|
||||||
// rely on the returned `TypeId`, which ensures that this
|
// rely on the returned `TypeId`, which ensures that this
|
||||||
|
|
|
@ -7,6 +7,8 @@ use std::slice;
|
||||||
struct ShortSlice<'a>(&'a mut [u8]);
|
struct ShortSlice<'a>(&'a mut [u8]);
|
||||||
|
|
||||||
impl<'a> ShortSlice<'a> {
|
impl<'a> ShortSlice<'a> {
|
||||||
|
/// # Safety
|
||||||
|
/// Given slice must be shorter than 8 bytes.
|
||||||
unsafe fn new(slice: &'a mut [u8]) -> Self {
|
unsafe fn new(slice: &'a mut [u8]) -> Self {
|
||||||
// Sanity check for debug builds
|
// Sanity check for debug builds
|
||||||
debug_assert!(slice.len() < 8);
|
debug_assert!(slice.len() < 8);
|
||||||
|
@ -46,13 +48,13 @@ pub(crate) fn apply_mask(buf: &mut [u8], mask_u32: u32) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
// TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so
|
// TODO: copy_nonoverlapping here compiles to call memcpy. While it is not so
|
||||||
// inefficient, it could be done better. The compiler does not understand that
|
// inefficient, it could be done better. The compiler does not understand that
|
||||||
// a `ShortSlice` must be smaller than a u64.
|
// a `ShortSlice` must be smaller than a u64.
|
||||||
|
#[inline]
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
fn xor_short(buf: ShortSlice<'_>, mask: u64) {
|
fn xor_short(buf: ShortSlice<'_>, mask: u64) {
|
||||||
// Unsafe: we know that a `ShortSlice` fits in a u64
|
// SAFETY: we know that a `ShortSlice` fits in a u64
|
||||||
unsafe {
|
unsafe {
|
||||||
let (ptr, len) = (buf.0.as_mut_ptr(), buf.0.len());
|
let (ptr, len) = (buf.0.as_mut_ptr(), buf.0.len());
|
||||||
let mut b: u64 = 0;
|
let mut b: u64 = 0;
|
||||||
|
@ -64,8 +66,9 @@ fn xor_short(buf: ShortSlice<'_>, mask: u64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// Caller must ensure the buffer has the correct size and alignment.
|
||||||
#[inline]
|
#[inline]
|
||||||
// Unsafe: caller must ensure the buffer has the correct size and alignment
|
|
||||||
unsafe fn cast_slice(buf: &mut [u8]) -> &mut [u64] {
|
unsafe fn cast_slice(buf: &mut [u8]) -> &mut [u64] {
|
||||||
// Assert correct size and alignment in debug builds
|
// Assert correct size and alignment in debug builds
|
||||||
debug_assert!(buf.len().trailing_zeros() >= 3);
|
debug_assert!(buf.len().trailing_zeros() >= 3);
|
||||||
|
@ -74,9 +77,9 @@ unsafe fn cast_slice(buf: &mut [u8]) -> &mut [u64] {
|
||||||
slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u64, buf.len() >> 3)
|
slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u64, buf.len() >> 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
// Splits a slice into three parts: an unaligned short head and tail, plus an aligned
|
// Splits a slice into three parts: an unaligned short head and tail, plus an aligned
|
||||||
// u64 mid section.
|
// u64 mid section.
|
||||||
|
#[inline]
|
||||||
fn align_buf(buf: &mut [u8]) -> (ShortSlice<'_>, &mut [u64], ShortSlice<'_>) {
|
fn align_buf(buf: &mut [u8]) -> (ShortSlice<'_>, &mut [u64], ShortSlice<'_>) {
|
||||||
let start_ptr = buf.as_ptr() as usize;
|
let start_ptr = buf.as_ptr() as usize;
|
||||||
let end_ptr = start_ptr + buf.len();
|
let end_ptr = start_ptr + buf.len();
|
||||||
|
@ -91,13 +94,13 @@ fn align_buf(buf: &mut [u8]) -> (ShortSlice<'_>, &mut [u64], ShortSlice<'_>) {
|
||||||
let (tmp, tail) = buf.split_at_mut(end_aligned - start_ptr);
|
let (tmp, tail) = buf.split_at_mut(end_aligned - start_ptr);
|
||||||
let (head, mid) = tmp.split_at_mut(start_aligned - start_ptr);
|
let (head, mid) = tmp.split_at_mut(start_aligned - start_ptr);
|
||||||
|
|
||||||
// Unsafe: we know the middle section is correctly aligned, and the outer
|
// SAFETY: we know the middle section is correctly aligned, and the outer
|
||||||
// sections are smaller than 8 bytes
|
// sections are smaller than 8 bytes
|
||||||
unsafe { (ShortSlice::new(head), cast_slice(mid), ShortSlice(tail)) }
|
unsafe { (ShortSlice::new(head), cast_slice(mid), ShortSlice(tail)) }
|
||||||
} else {
|
} else {
|
||||||
// We didn't cross even one aligned boundary!
|
// We didn't cross even one aligned boundary!
|
||||||
|
|
||||||
// Unsafe: The outer sections are smaller than 8 bytes
|
// SAFETY: The outer sections are smaller than 8 bytes
|
||||||
unsafe { (ShortSlice::new(buf), &mut [], ShortSlice::new(&mut [])) }
|
unsafe { (ShortSlice::new(buf), &mut [], ShortSlice::new(&mut [])) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue