Re: bind() to ::1 fails with EADDRNOTAVAIL / clang vs gcc

From: Konstantin Belousov <kostikbel_at_gmail.com>
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)).