Page MenuHomeFreeBSD

Fix incorrect EADDRINUSE from connect()
Needs ReviewPublic

Authored by firk_cantconnect.ru on Wed, Jul 27, 7:33 PM.
This revision needs review, but there are no reviewers specified.

Details

Reviewers
None
Summary

Busy port checking during port autoselection for wildcard-port bind() is incomplete, and this may lead to binding to already busy port and EADDRINUSE from connect().

The same problem for implicit bind when connecting from unbound socket was fixed in r361228.

There is two know ways to trigger this:

  1. make a connection from bind(0.0.0.0:0) to any address;

then repeat the same from a jail;
jailed bind() doesn't know about the port busy by first one and may erroneously try to reuse it, leading to EADDRINUSE from connect()

  1. make a connection from bind(ipv6 :::0) to ipv6-wrapper ipv4 address, for example ::ffff:127.0.0.1:10001;

then just repeat the same again;
second bind() doesn't know about first one, because first one became "ipv4" after connecting, so it may try reuse the same port and connect() will fail with EADDRINUSE

PR: 210726
PR: 265064

Test Plan

Test for jail-related bug:

Download test.c: https://bugs.freebsd.org/bugzilla/attachment.cgi?id=232951

First, reduce portrange to hit the problem faster:

sysctl net.inet.ip.portrange.first=10000
sysctl net.inet.ip.portrange.last=10004

Compile:
cc -o test test.c

Open two screens, first:

./test 10 1.2.3.4 0.0.0.0

Second:

jail / x your.real.ip.address csh
cd /your/dir
./test 10 1.2.3.4 0.0.0.0

with unpatched system, second program will show (EADDRINUSE)

connect() error 48 (Address already in use)

with patched, it will show the proper error (EADDRNOTAVAIL)

bind() error 49 (Can't assign requested address)


Test for ipv6-ipv4 related bug:

Download the test program (not mine): https://github.com/dmgk/localdial/blob/master/localdial.c

cc -o localdial localdial.c -lstdthreads
sysctl net.inet.ip.portrange.first=10000
sysctl net.inet.ip.portrange.last=10001
./localdial -M

It will fail with EADDRINUSE without patch, and with EADDRNOTAVAIL with patch

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Unit Tests Skipped