Skip to content

Commit 0bd9d16

Browse files
committed
fix(http2): Remove unsafe from proto::h2
Back in #2523, @nox introduced the notion of an UpgradedSendStream, to support the CONNECT method of HTTP/2. This used `unsafe {}` to support `http_body::Body`, where `Body::Data` did not implement `Send`, since the `Data` type wouldn't be sent across the stream once upgraded. Unfortunately, according to this [thread], I think this may be undefined behavior, because this relies on us requiring the transmute to execute. This patch fixes the potential UB by adding the unncessary `Send` constraints. It appears that all the internal users of `UpgradeSendStream` already work with `http_body::Body` types that have `Send`-able `Data` constraints. We can add this constraint without breaking any external APIs, which lets us remove the `unsafe {}` blocks. [thread]: https://users.rust-lang.org/t/is-a-reference-to-impossible-value-considered-ub/31383
1 parent faf24c6 commit 0bd9d16

File tree

3 files changed

+13
-45
lines changed

3 files changed

+13
-45
lines changed

src/proto/h2/client.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ where
341341
let (pending, on_upgrade) = crate::upgrade::pending();
342342
let io = H2Upgraded {
343343
ping,
344-
send_stream: unsafe { UpgradedSendStream::new(send_stream) },
344+
send_stream: UpgradedSendStream::new(send_stream),
345345
recv_stream,
346346
buf: Bytes::new(),
347347
};

src/proto/h2/mod.rs

+9-41
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use http::HeaderMap;
55
use pin_project_lite::pin_project;
66
use std::error::Error as StdError;
77
use std::io::{self, Cursor, IoSlice};
8-
use std::mem;
98
use std::task::Context;
109
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
1110
use tracing::{debug, trace, warn};
@@ -409,63 +408,32 @@ fn h2_to_io_error(e: h2::Error) -> io::Error {
409408
}
410409
}
411410

412-
struct UpgradedSendStream<B>(SendStream<SendBuf<Neutered<B>>>);
411+
struct UpgradedSendStream<B: Buf>(SendStream<SendBuf<B>>);
413412

414413
impl<B> UpgradedSendStream<B>
415414
where
416415
B: Buf,
417416
{
418-
unsafe fn new(inner: SendStream<SendBuf<B>>) -> Self {
419-
assert_eq!(mem::size_of::<B>(), mem::size_of::<Neutered<B>>());
420-
Self(mem::transmute(inner))
417+
fn new(inner: SendStream<SendBuf<B>>) -> Self {
418+
Self(inner)
421419
}
422420

423421
fn reserve_capacity(&mut self, cnt: usize) {
424-
unsafe { self.as_inner_unchecked().reserve_capacity(cnt) }
422+
self.0.reserve_capacity(cnt)
425423
}
426424

427425
fn poll_capacity(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<usize, h2::Error>>> {
428-
unsafe { self.as_inner_unchecked().poll_capacity(cx) }
426+
self.0.poll_capacity(cx)
429427
}
430428

431429
fn poll_reset(&mut self, cx: &mut Context<'_>) -> Poll<Result<h2::Reason, h2::Error>> {
432-
unsafe { self.as_inner_unchecked().poll_reset(cx) }
430+
self.0.poll_reset(cx)
433431
}
434432

435433
fn write(&mut self, buf: &[u8], end_of_stream: bool) -> Result<(), io::Error> {
436434
let send_buf = SendBuf::Cursor(Cursor::new(buf.into()));
437-
unsafe {
438-
self.as_inner_unchecked()
439-
.send_data(send_buf, end_of_stream)
440-
.map_err(h2_to_io_error)
441-
}
442-
}
443-
444-
unsafe fn as_inner_unchecked(&mut self) -> &mut SendStream<SendBuf<B>> {
445-
&mut *(&mut self.0 as *mut _ as *mut _)
446-
}
447-
}
448-
449-
#[repr(transparent)]
450-
struct Neutered<B> {
451-
_inner: B,
452-
impossible: Impossible,
453-
}
454-
455-
enum Impossible {}
456-
457-
unsafe impl<B> Send for Neutered<B> {}
458-
459-
impl<B> Buf for Neutered<B> {
460-
fn remaining(&self) -> usize {
461-
match self.impossible {}
462-
}
463-
464-
fn chunk(&self) -> &[u8] {
465-
match self.impossible {}
466-
}
467-
468-
fn advance(&mut self, _cnt: usize) {
469-
match self.impossible {}
435+
self.0
436+
.send_data(send_buf, end_of_stream)
437+
.map_err(h2_to_io_error)
470438
}
471439
}

src/proto/h2/server.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ impl<F, B, E> H2Stream<F, B>
433433
where
434434
F: Future<Output = Result<Response<B>, E>>,
435435
B: HttpBody,
436-
B::Data: 'static,
436+
B::Data: Send + 'static,
437437
B::Error: Into<Box<dyn StdError + Send + Sync>>,
438438
E: Into<Box<dyn StdError + Send + Sync>>,
439439
{
@@ -489,7 +489,7 @@ where
489489
H2Upgraded {
490490
ping: connect_parts.ping,
491491
recv_stream: connect_parts.recv_stream,
492-
send_stream: unsafe { UpgradedSendStream::new(send_stream) },
492+
send_stream: UpgradedSendStream::new(send_stream),
493493
buf: Bytes::new(),
494494
},
495495
Bytes::new(),
@@ -527,7 +527,7 @@ impl<F, B, E> Future for H2Stream<F, B>
527527
where
528528
F: Future<Output = Result<Response<B>, E>>,
529529
B: HttpBody,
530-
B::Data: 'static,
530+
B::Data: Send + 'static,
531531
B::Error: Into<Box<dyn StdError + Send + Sync>>,
532532
E: Into<Box<dyn StdError + Send + Sync>>,
533533
{

0 commit comments

Comments
 (0)