Le vendredi 20 janvier 2023, 13:40:44 UTC Alejandro Colomar a écrit : > The historical design of `sockaddr_storage` makes it impossible to use > without breaking strict aliasing rules. Not only this type is unusable, > but even the use of other `sockaddr_*` structures directly (when the > programmer only cares about a single address family) is also incorrect, > since at some point the structure will be accessed as a `sockaddr`, and > that breaks strict aliasing rules too. > > So, the only way for a programmer to not invoke Undefined Behavior is to > declare a union that includes `sockaddr` and any `sockaddr_*` structures > that are of interest, which allows later accessing as either the correct > structure or plain `sockaddr` for the sa_family. > > This patch fixes sockaddr_storage to remove UB on its uses and make it > that structure that everybody should be using. It also allows removing > many casts in code that needs to pass a sockaddr as a side effect. > > The following is an example of how this improves both existing code and > new code: > > void > foo(foo) > { > struct old_sockaddr_storage oss; > struct new_sockaddr_storage nss; > > // ... (initialize oss and nss) > > inet_sockaddr2str(&nss.sa); // correct (and has no casts) > inet_sockaddr2str((struct sockaddr *)&oss); // UB > inet_sockaddr2str((struct sockaddr *)&nss); // correct > } > > /* This function is correct, as far as the accessed object has the > * type we're using. That's only possible through a `union`, since > * we're accessing it with 2 different types: `sockaddr` for the > * `sa_family` and then the appropriate subtype for the address > * itself. > */ > const char * > inet_sockaddr2str(const struct sockaddr *sa) > { > struct sockaddr_in *sin; > struct sockaddr_in6 *sin6; > > static char buf[MAXHOSTNAMELEN]; > > switch (sa->sa_family) { > case AF_INET: > sin = (struct sockaddr_in *) sa; > inet_ntop(AF_INET, &sin->sin_addr, buf, NITEMS(buf)); > return buf; > case AF_INET6: > sin6 = (struct sockaddr_in6 *) sa; > inet_ntop(AF_INET6, &sin6->sin6_addr, buf, NITEMS(buf)); > return buf; > default: > errno = EAFNOSUPPORT; > return NULL; > } > } > > While it's not necessary to do the same for `sockaddr`, it might still > be interesting to so, since it will allow removing many casts in the > implementation of many libc functions. > > Link: > Link: > Link: > Link: > Cc: Bastien Roucariès > Cc: Eric Blake > Cc: Zack Weinberg > Cc: Stefan Puiu > Cc: Igor Sysoev > Signed-off-by: Alejandro Colomar > --- > > v2: > > - Fix incorrect cast in commit message. > > bits/socket.h | 11 ++++++++--- > 1 file changed, 8 insertions(+), 3 deletions(-) > > diff --git a/bits/socket.h b/bits/socket.h > index aac8c49b00..c0c23b4e84 100644 > --- a/bits/socket.h > +++ b/bits/socket.h > @@ -168,9 +168,14 @@ struct sockaddr > > struct sockaddr_storage > { > - __SOCKADDR_COMMON (ss_); /* Address family, etc. */ > - char __ss_padding[_SS_PADSIZE]; > - __ss_aligntype __ss_align; /* Force desired alignment. */ no this is not correct you break ABI by reducing size > + union > + { > + __SOCKADDR_COMMON (ss_); /* Address family, etc. */ > + struct sockaddr sa; > + struct sockaddr_in sin; > + struct sockaddr_in6 sin6; > + struct sockaddr_un sun; > + }; > }; Correct one structure is struct __private_sock_storage { __SOCKADDR_COMMON (ssprivate_); /* Address family, etc. */ char __ss_padding[_SS_PADSIZE]; __ss_aligntype __ss_align; /* Force desired alignment. */ } struct sockaddr_storage { union { __SOCKADDR_COMMON (ss_); /* Address family, etc. */ struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; struct sockaddr_un sun; struct __private_sock_storage _private; }; }; May it could be dropped later using align construct for modern C and padding Bastien > > >