Does casting `sockaddr_in` to `sockaddr` result in UB when reading from the result

Consider this example:

fn socket_addr_to_sockaddr() -> libc::sockaddr_storage {
    let mut storage: libc::sockaddr_storage = unsafe { std::mem::zeroed() };
    let sin: libc::sockaddr_in = unsafe { std::mem::zeroed() };

    unsafe {
        let sin_ptr = &sin as *const libc::sockaddr_in as *const libc::sockaddr;
        let storage_ptr = &mut storage as *mut libc::sockaddr_storage as *mut libc::sockaddr;
        std::ptr::copy_nonoverlapping(sin_ptr, storage_ptr, 1);  // #1
    }
    storage
}
fn main() {
    socket_addr_to_sockaddr();
}

The layout of libc::sockaddr_in, libc::sockaddr_storage, and libc::sockaddr are respectively:

// size = 16 (0x10), align = 0x4
pub struct sockaddr_in {
    pub sin_len: u8,
    pub sin_family: u8,
    pub sin_port: u16,
    pub sin_addr: in_addr,
    pub sin_zero: [i8; 8],
}

// size = 128 (0x80), align = 0x8
pub struct sockaddr_storage {
    pub ss_len: u8,
    pub ss_family: u8,
    __ss_pad1: [u8; 6],
    __ss_align: i64,
    __ss_pad2: [u8; 112],
}

// size = 16 (0x10), align = 0x1
pub struct sockaddr {
    pub sa_len: u8,
    pub sa_family: u8,
    pub sa_data: [i8; 14],
}

The alignment of sockaddr is weaker than that of sockaddr_in and sockaddr_storage, so converting the pointer to the latter types to the pointer to sockaddr does not result in misaligned pointers.

Q1:

Does #1 cause UB?

Q2:

if change #1 to *storage_ptr = *sin_ptr, does it cause UB?

In C standard, it says:

A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements.

So, the result of the pointer cast is already ill-formed. I don't know whether such casting is valid in Rust.

1 Like

No.

No.

C has a rule called strict aliasing which says that you can't have different pointer types to the same memory. Rust has no such rule, and pointer casts between different types are not inherently a problem. As long as the pointers are sufficiently aligned and you don't deal with uninitialized memory (i.e. padding) incorrectly, you'll be fine.

3 Likes

In Rust, convert a pointer pointing to T to a pointer pointing to U where T has a stricter alignment than U, even though dereferencing the resulting pointer and reading or writing to the place, it does not cause UB?

No. If a pointer is sufficiently aligned, you may dereference it. It can't be too aligned.

What does it mean? what a pointer is considered sufficiently aligned? For example * mut T to * mut U, if T has alignment 16 while U has alignment 2, is this sufficiently aligned?

Yes, if a pointer's value is a multiple of 16, then it is also a multiple of 2.

Ok, so saying a target pointer is aligned to source one means the source's pointer type has stricter alignment or is equal to the target's pointee type, right?

It can't be too aligned.

what does it mean?

They just meant it is ok/fine if the needed alignment is less than the actual alignment.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.