Re: bind() to ::1 fails with EADDRNOTAVAIL / clang vs gcc
Date: Wed, 24 Nov 2021 02:53:45 UTC
On Tue, Nov 23, 2021 at 09:08:39PM -0500, Jan Schaumann via freebsd-net wrote:
> Hello,
>
> I'm observing the following strange behavior, where
> trying to bind(2) a socket on "::1" fails with
> EADDRNOTAVAIL, but binding in6addr_any will succeed
> (and then yield a bound ::1). What's more, the
> behavior is inconsistent depending on the compiler
> used.
>
> Here's my sample program a.c:
>
> ----
> #include <arpa/inet.h>
> #include <sys/socket.h>
>
> #include <netinet/in.h>
>
> #include <err.h>
> #include <stdlib.h>
>
> #define PORT 12345
>
> int
> main(int argc, char **argv) {
> int sock;
> socklen_t a, b;
> struct sockaddr_storage server;
> struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&server;
>
> if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
> err(EXIT_FAILURE, "socket");
> /* NOTREACHED */
> }
>
> if (inet_pton(PF_INET6, "::1", &(sin->sin6_addr)) != 1) {
> err(EXIT_FAILURE, "inet_pton");
> /* NOTREACHED */
> }
>
> sin->sin6_family = PF_INET6;
> sin->sin6_port = htons(PORT);
>
> if (bind(sock, (struct sockaddr *)sin, sizeof(*sin)) != 0) {
> err(EXIT_FAILURE, "bind");
> /* NOTREACHED */
> }
>
> return 0;
> }
> ----
>
> This program succeeds in binding and returns 0, as
> expected. However, note the declaration of the unused
> socklen_t a and b. If I remove that declaration, the
> call to bind(2) will fail:
>
> $ diff -bu [ab].c
> --- a.c 2021-11-23 19:49:05.926884000 +0000
> +++ b.c 2021-11-23 19:48:51.409173000 +0000
> @@ -11,7 +11,6 @@
> int
> main(int argc, char **argv) {
> int sock;
> - socklen_t a, b;
> struct sockaddr_storage server;
> struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&server;
>
> $ ./a.out
> $ echo $?
> 0
> $ cc b.c
> $ ./a.out
> a.out: bind: Can't assign requested address
> $
>
> This is on FreeBSD 13.0-RELEASE with FreeBSD clang
> version 11.0.1.
>
> Compiling either with gcc version 10.3.0 fails.
>
>
> So my questions are:
>
> - Why does _any_ of those fail?
> - Why does a.c succeed when compiled with clang, but
> b.c does not?
Most likely because you did not fully initialized *sin. struct
sockaddr_in6 is more complex than just address/port/family. There is
also at least scope_id, and something called flow_id. You did not
initialized these fields.
By adding unused stack variables, you changed the place on stack where
sockaddr_in6 structure was laid out, systematically chaning assigned
values to these fields.
Really it is UB, try to memset(sin, 0, sizeof(*sin)).