-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove unsafe from proto::h2 #2831
Conversation
Back in hyperium#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
struct Neutered<B> { | ||
_inner: B, | ||
impossible: Impossible, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, there have even been proposals that would make Neutered<B>
have size 0, since it cannot ever be inhabited anyway. So this is definitely an incorrect pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is checked in UpgradedSendStream
's new
, so I think that should be okay. It's not ideal but it shouldn't produce any UB.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, you are guarding against the layout optimization there, okay.
Creating an instance of Neutered<T>
is still most definitely UB though. In fact if there is any uncontroversial UB in Rust, unreachable_unchecked
would be first on that list, and creating an instance of an uninhabited type is second. The reference is also clear about this and never was there any controversy in the UCG that this code should be UB.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is never a Neutered<T>
being created anywhere, that's the thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see, that makes sense then that Miri didn't complain about hyper. :)
Let's go for #2830 instead, so that we avoid the breaking change. I'm not sure I understand why you think there is UB though. The transmute doesn't happen on an uninhabited type, it happens on an enum of which one of the variants has an uninhabited field. |
@nox - we talked about this more on rust-lang/unsafe-code-guidelines#337. I now think this is sound. The UB I thought we had was that directly transmuting a value to something that contains an Impossible type is UB. But instead you are instead ultimately transmuting a pointer to an Impossible type, which is sound. The one concern though that’s brought up in the thread is that there’s no guarantee of the layout of a type that includes an impossible type. There’s talk about always treating those types as zero sized.
Sure, that’s fine with me. My main interest is to at least document it to save other people’s time when auditing hyper. There’s a chance though this isn’t a breaking change (at least for external users). I traced that the proto::h2::client already requires |
As long as it's a raw pointer, that's okay. |
There is never, ever, a reference to an uninhabited value, or an uninhabited value, being created by my code. As for |
You may be right. |
All right, sorry for the confusion.
Yeah, rust-lang/rust#96921 was recently created to discuss this. |
Ah, nuts. So I finished tracing, and
|
Ok, so to recap:
|
I think it breaks using custom executors with non- |
You are correct, updating |
Back in #2523, @nox introduced the notion of an UpgradedSendStream, to support the CONNECT method of HTTP/2. This used
unsafe {}
to supporthttp_body::Body
, whereBody::Data
did not implementSend
, since theData
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 ofUpgradeSendStream
already work withhttp_body::Body
types that haveSend
-ableData
constraints. We can add this constraint without breaking any external APIs, which lets us remove theunsafe {}
blocks.I filed rust-lang/unsafe-code-guidelines#337 for formal guidance on this pattern.
Fixes #2829