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 <xry111@xxxxxxxxxxx> School of Aerospace Science and Technology, Xidian University