Observe the following strace of obviously invalid syscall sequence: [pid 7906] socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 4 [pid 7906] fcntl64(4, F_SETFL, O_RDONLY|O_NONBLOCK) = 0 [pid 7906] setsockopt(4, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0 [pid 7906] setsockopt(4, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 [pid 7906] write(1, "REBIND: ::ffff:192.168.0.8;0\n", 29REBIND: ::ffff:192.168.0.8;0 ) = 29 [pid 7906] bind(4, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::ffff:192.168.0.8", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0 [pid 7906] listen(4, 128) = 0 [pid 7906] getsockname(4, {sa_family=AF_INET, sin_port=htons(35031), sin_addr=inet_addr("0.0.0.0")}, [16]) = 0 I'm creating an IPv4 socket, but then I'm trying to bind an IPv6 address to it. This should not work. This should fail, yet bind() returns 0! To add insult to injury, getsockname() shows that the socket was bound to the port, but not to any IP address! I think that bind() should either fail and return -1, or convert the IPv4-mapped IPv6 address to the real IPv4 address, and bind that (192.168.0.8 is a valid IP address on this machine). This is kernel 2.6.9-1.6_FC2
I don't see why this was reassigned to glibc. socket/bind/setsockopt/listen are all simple wrappers around the syscalls (just putting arguments in the places where kernel expects them) and glibc doesn't have information wheter a filedescriptor is socket or not and what domain it has been created in readily available. Only the kernel can do something about this without performance penalty.
I'm mystified as to how this can even happen. The write() call causes inet_autobind() to automatically bind the socket to some port. This causes the inet_sk(sk)->num of the socket to be set non-zero if the write() returns success. This should cause the subsequent bind() call to return -EINVAL due to this check: err = -EINVAL; if (sk->sk_state != TCP_CLOSE || inet->num) goto out_release_sock; As stated above, inet->num should be non-zero and therefore we will return -EINVAL. Can you provide a simple test program to reproduce this problem instead of just an strace?
Created attachment 108196 [details] Test program The write() is to a different file descriptor, and has nothing to do with it. The strace from this test program is the same. Note that 192.168.0.8 is a valid IP4 IP address for this machine, which may be a factor: socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3 bind(3, {sa_family=AF_INET6, sin6_port=htons(0), inet_pton(AF_INET6, "::ffff:192.168.0.8", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0 getsockname(3, {sa_family=AF_INET, sin_port=htons(32809), sin_addr=inet_addr("0.0.0.0")}, [16]) = 0 The bind of an IPv6 address to an IPv4 socket must fail.
We can't change this behavior, it's too risky. I did some research and BSD, even modern BSD such as current FreeBSD CVS, does not enforce the sa_family value to be correct when binding. I was very suspicious about exactly something like this when I found that, upon scanning the entire Linux networking, ipv4 and ipv6 were basically the only two protocols not verifying the sa_family value at bind() time. I am certain this was a conscious decision. If BSD doesn't enforce it, and Linux never did, it is incredibly likely that there are many applications out there not setting up the sa_family field for bind() calls correctly to ipv4 and ipv6 sockets. "fixing" this, therefore, would break many applications, so we just have to live with the current behavior. If you would like to have a further discussion on this topic, I encourage you to start a thread on the netdev.com mailing lists so that opinions other than ours can be voiced about such a risky change.
One final comment, in the BSD sources, where the sin_family check would go, the code looks like this: #ifdef notdef /* * We should check the family, but old programs * incorrectly fail to initialize it. */ if (sin->sin_family != AF_INET) return (EAFNOSUPPORT); #endif Meaning the check is there, but never enabled in the sources. This further supports the idea that adding this check would cause more harm than good.