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

Invalid transmute from std::net::Ipv4Addr to libc::in_addr #2053

Closed
stevenengler opened this issue Jun 6, 2023 · 3 comments · Fixed by #2061
Closed

Invalid transmute from std::net::Ipv4Addr to libc::in_addr #2053

stevenengler opened this issue Jun 6, 2023 · 3 comments · Fixed by #2061

Comments

@stevenengler
Copy link
Contributor

The functions ipv4addr_to_libc and ipv6addr_to_libc transmute from std::net::Ipv4Addr to libc::in_addr, but this is unsound.

/// Convert a std::net::Ipv4Addr into the libc form.
#[cfg(feature = "net")]
pub(crate) const fn ipv4addr_to_libc(addr: net::Ipv4Addr) -> libc::in_addr {
static_assertions::assert_eq_size!(net::Ipv4Addr, libc::in_addr);
// Safe because both types have the same memory layout, and no fancy Drop
// impls.
unsafe { mem::transmute(addr) }
}
/// Convert a std::net::Ipv6Addr into the libc form.
#[cfg(feature = "net")]
pub(crate) const fn ipv6addr_to_libc(addr: &net::Ipv6Addr) -> libc::in6_addr {
static_assertions::assert_eq_size!(net::Ipv6Addr, libc::in6_addr);
// Safe because both are Newtype wrappers around the same libc type
unsafe { mem::transmute(*addr) }
}

The rust socket address types do not have the same layouts as their corresponding libc types, even if their size is the same.

See rust-lang/rust#78802 for details.

This release changes the memory layout of Ipv4Addr, Ipv6Addr, SocketAddrV4 and SocketAddrV6. The standard library no longer implements these as the corresponding libc structs (sockaddr_in, sockaddr_in6 etc.). This internal representation was never exposed, but some crates relied on it anyway by unsafely transmuting. This change will cause those crates to make invalid memory accesses.

This change obviously changes the memory layout of the types. And it turns out some libraries invalidly assumes the memory layout and does very dangerous pointer casts to convert them. These libraries will have undefined behaviour and perform invalid memory access until patched.

@WhyNotHugo
Copy link

Looks like these functions will have to be rewritten to perform a more expensive conversion rather than simply transmuting.

@asomers
Copy link
Member

asomers commented Jun 27, 2023

Annoying. I guess we'll have to change it. However, it looks like the standard library's new layout is still the same as the C layout, at least on common platforms. Do you know of any real platforms where it isn't?

asomers added a commit to asomers/nix that referenced this issue Jun 27, 2023
Rust's standard library no longer guarantees that Ipv4Addr and Ipv6Addr
are wrappers around the C types (though for now at least, they are
identical on all platforms I'm aware of).  So do the conversions
explicitly instead of transmuting.

Fixes nix-rust#2053
asomers added a commit to asomers/nix that referenced this issue Jun 27, 2023
Rust's standard library no longer guarantees that Ipv4Addr and Ipv6Addr
are wrappers around the C types (though for now at least, they are
identical on all platforms I'm aware of).  So do the conversions
explicitly instead of transmuting.

Fixes nix-rust#2053
@stevenengler
Copy link
Contributor Author

stevenengler commented Jun 27, 2023

Do you know of any real platforms where it isn't?

I don't know of any. Rust is free to do whatever it wants for the layout (and the layout isn't guaranteed to be the same even across builds), but it's probably unlikely that the rust representation of struct Ipv4Addr { octets: [u8; 4] } would ever be different from if the struct was repr(C). The Ipv6Addr and in6_addr have different alignments, but that shouldn't matter when transmuting the value rather than a reference.

But on the other hand, I would expect

libc::in_addr {
    s_addr: u32::from_be_bytes(addr.octets()).to_be()
}

to get optimized to essentially a no-op anyway without a need for transmute. For example on godbolt:

pub fn convert(addr: std::net::Ipv4Addr) -> u32 {
    u32::from_be_bytes(addr.octets()).to_be()
}

gets compiled to just

example::convert:
        mov     eax, edi
        ret

bors bot added a commit that referenced this issue Jun 29, 2023
2061: For invalid IP address conversions with future Rust versions r=asomers a=asomers

Rust's standard library no longer guarantees that Ipv4Addr and Ipv6Addr are wrappers around the C types (though for now at least, they are identical on all platforms I'm aware of).  So do the conversions explicitly instead of transmuting.

Fixes #2053

Co-authored-by: Alan Somers <[email protected]>
@bors bors bot closed this as completed in 1546857 Jun 29, 2023
asomers added a commit to asomers/nix that referenced this issue Aug 27, 2023
2061: For invalid IP address conversions with future Rust versions r=asomers a=asomers

Rust's standard library no longer guarantees that Ipv4Addr and Ipv6Addr are wrappers around the C types (though for now at least, they are identical on all platforms I'm aware of).  So do the conversions explicitly instead of transmuting.

Fixes nix-rust#2053

Co-authored-by: Alan Somers <[email protected]>
asomers added a commit to asomers/nix that referenced this issue Aug 27, 2023
2061: For invalid IP address conversions with future Rust versions r=asomers a=asomers

Rust's standard library no longer guarantees that Ipv4Addr and Ipv6Addr are wrappers around the C types (though for now at least, they are identical on all platforms I'm aware of).  So do the conversions explicitly instead of transmuting.

Fixes nix-rust#2053

Co-authored-by: Alan Somers <[email protected]>
asomers added a commit to asomers/nix that referenced this issue Aug 27, 2023
2061: For invalid IP address conversions with future Rust versions r=asomers a=asomers

Rust's standard library no longer guarantees that Ipv4Addr and Ipv6Addr are wrappers around the C types (though for now at least, they are identical on all platforms I'm aware of).  So do the conversions explicitly instead of transmuting.

Fixes nix-rust#2053

Co-authored-by: Alan Somers <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants