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

From: Jan Schaumann via freebsd-net <freebsd-net_at_freebsd.org>
Date: Wed, 24 Nov 2021 02:08:39 UTC
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?

-Jan