On Sun, 2023-02-05 at 16:31 +0100, Alejandro Colomar via Libc-alpha wrote:

> The only correct way to use  different  types  in  an  API  is
> through  a  union.

I don't think this statement is true (in general).  Technically we can
write something like this:

struct sockaddr { ... };
struct sockaddr_in { ... };
struct sockaddr_in6 { ... };

int bind(int fd, const struct sockaddr *addr, socklen_t addrlen)
{
    if (addrlen < sizeof(struct sockaddr) {
        errno = EINVAL;
        return -1;
    }

    /* cannot use "addr->sa_family" directly: it will be an UB */
    sa_family_t sa_family;
    memcpy(&sa_family, addr, sizeof(sa_family));

    switch (sa_family) {
        case AF_INET:
            return _do_bind_in(fd, (struct sockaddr_in *)addr, addrlen);
        case AF_INET6:
            return _do_bind_in6(fd, (struct sockaddr_in6 *)addr, addrlen);
        /* more cases follow here */
        default:
            errno = EINVAL;
            return -1;
        }
    }
}

In this way we can use sockaddr_{in,in6,...} for bind() safely, as long
as we can distinguish the "real" type of addr using the leading byte
sequence (and the caller uses it carefully).

But obviously sockaddr_storage can't be distinguished here, so casting a
struct sockaddr_stroage * to struct sockaddr * and passing it to bind()
will still be wrong (unless we make sockaddr_storage an union or add
[[gnu::may_alias]]).

-- 
Xi Ruoyao <xry...@xry111.site>
School of Aerospace Science and Technology, Xidian University

Reply via email to