Skip to content
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

UpgradedSendStream transmute seems to be super unsound #3500

Open
Manishearth opened this issue Dec 21, 2023 · 12 comments
Open

UpgradedSendStream transmute seems to be super unsound #3500

Manishearth opened this issue Dec 21, 2023 · 12 comments
Labels
C-bug Category: bug. Something is wrong. This is bad!

Comments

@Manishearth
Copy link
Contributor

Manishearth commented Dec 21, 2023

https://github.com/hyperium/hyper/blob/1d4ff3597b8e76818c8553dbfa4234cf4208c958/src/proto/h2/mod.rs#L389C8-L397

This isn't sound. Neutered<B> is:

#[repr(transparent)]
struct Neutered<B> {
    _inner: B,
    impossible: Impossible,
}

enum Impossible {}

Firstly, it's unclear what guarantees repr(transparent) confers here, impossible does have size_of == 0 but it's not exactly a zero-sized type (rust-lang/unsafe-code-guidelines#485). The repr(transparent) RFC is not clear about this, and it's quite shaky ground to rely on, especially since empty ("impossible") types are one place Rust does sometimes perform layout optimizations. There's an assertion guard against that for Neutered but it's not clear how transitive this stuff is (rust-lang/unsafe-code-guidelines#486)

Empty types can and do affect enum representations. SendBuf<B> has the following definition:

#[repr(usize)]
enum SendBuf<B> {
    Buf(B),
    Cursor(Cursor<Box<[u8]>>),
    None,
}

There's nothing preventing the compiler from realizing that Buf(Neutered<...>) is impossible and deciding to not include it as a discriminant choice. The asserts in this code are insufficient to check that.

This code seems to work fine under the current compiler but I wouldn't rely on that.

@seanmonstar
Copy link
Member

For historical reference, this was discussed in #2831 and determined it wasn't unsound. Could be we got it wrong in there, I'm just including it for context.

@Manishearth
Copy link
Contributor Author

Yeah, that discussion seems to focus on whether Neutered<B> is constructed; not about whether the transmute is safe.

@Manishearth
Copy link
Contributor Author

Also stepping back a bit this pattern here is extremely weird and has almost no comments explaining what it does or justifying its safety. It's worth considering moving away from this model anyway, if possible.

@seanmonstar
Copy link
Member

cc @nox, you'll remember this better than I.

@Manishearth Manishearth changed the title UpgradedSendStream transmute is super unsound UpgradedSendStream transmute seems to be super unsound Dec 21, 2023
@RalfJung
Copy link
Contributor

There's nothing preventing the compiler from realizing that Buf(Neutered<...>) is impossible and deciding to not include it as a discriminant choice. The asserts in this code are insufficient to check that.

Agreed.

@nox
Copy link
Contributor

nox commented Dec 23, 2023 via email

@RalfJung
Copy link
Contributor

Ah sorry I had missed that this enum is repr(usize). That indeed makes a difference.

@Manishearth
Copy link
Contributor Author

@RalfJung does that also work with the transitivity assumption?

@RalfJung
Copy link
Contributor

Not sure which assumption exactly you mean?

Basically the question is, for two repr($int) enum, if they have the same variants in the same order and all fields have the same layout, then do the two enums have the same layout? I don't see any way this could not be the case; AFAIK layout is completely laid down for repr(usize). So I think just like repr(transparent) is "transitive" through repr(C), it should also be "transitive" through repr($int). At least I'd say we should make such a guarantee; I don't know if we do.

Whether that's actually implemented properly for uninhabited types (rust-lang/unsafe-code-guidelines#485), I don't know.

@Manishearth
Copy link
Contributor Author

Okay, good to know, thanks!

@seanmonstar
Copy link
Member

So did that extra information mean that this is fine after all? Or do we need to adjust something?

@Manishearth
Copy link
Contributor Author

I think it's still potentially an issue given:

Whether that's actually implemented properly for uninhabited types (rust-lang/unsafe-code-guidelines#485), I don't know.

but the bigger issue is a non-issue it seems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: bug. Something is wrong. This is bad!
Projects
None yet
Development

No branches or pull requests

4 participants