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 <[email protected]>
School of Aerospace Science and Technology, Xidian University