Skip to content

Commit cd0a59f

Browse files
miss-islingtonnirs
andauthored
gh-94821: Fix autobind of empty unix domain address (GH-94826) (GH-94875)
When binding a unix socket to an empty address on Linux, the socket is automatically bound to an available address in the abstract namespace. >>> s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) >>> s.bind("") >>> s.getsockname() b'\x0075499' Since python 3.9, the socket is bound to the one address: >>> s.getsockname() b'\x00' And trying to bind multiple sockets will fail with: Traceback (most recent call last): File "/home/nsoffer/src/cpython/Lib/test/test_socket.py", line 5553, in testAutobind s2.bind("") OSError: [Errno 98] Address already in use Added 2 tests: - Auto binding empty address on Linux - Failing to bind an empty address on other platforms Fixes f6b3a07 (bpo-44493: Add missing terminated NUL in sockaddr_un's length (GH-26866) (cherry picked from commit c22f134) Co-authored-by: Nir Soffer <[email protected]>
1 parent eff4aa5 commit cd0a59f

File tree

3 files changed

+25
-2
lines changed

3 files changed

+25
-2
lines changed

Lib/test/test_socket.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5417,6 +5417,20 @@ def testBytearrayName(self):
54175417
s.bind(bytearray(b"\x00python\x00test\x00"))
54185418
self.assertEqual(s.getsockname(), b"\x00python\x00test\x00")
54195419

5420+
def testAutobind(self):
5421+
# Check that binding to an empty string binds to an available address
5422+
# in the abstract namespace as specified in unix(7) "Autobind feature".
5423+
abstract_address = b"^\0[0-9a-f]{5}"
5424+
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s1:
5425+
s1.bind("")
5426+
self.assertRegex(s1.getsockname(), abstract_address)
5427+
# Each socket is bound to a different abstract address.
5428+
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s2:
5429+
s2.bind("")
5430+
self.assertRegex(s2.getsockname(), abstract_address)
5431+
self.assertNotEqual(s1.getsockname(), s2.getsockname())
5432+
5433+
54205434
@unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'test needs socket.AF_UNIX')
54215435
class TestUnixDomain(unittest.TestCase):
54225436

@@ -5486,6 +5500,11 @@ def testUnencodableAddr(self):
54865500
self.addCleanup(support.unlink, path)
54875501
self.assertEqual(self.sock.getsockname(), path)
54885502

5503+
@unittest.skipIf(sys.platform == 'linux', 'Linux specific test')
5504+
def testEmptyAddress(self):
5505+
# Test that binding empty address fails.
5506+
self.assertRaises(OSError, self.sock.bind, "")
5507+
54895508

54905509
class BufferIOTest(SocketConnectedTest):
54915510
"""
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix binding of unix socket to empty address on Linux to use an available
2+
address from the abstract namespace, instead of "\0".

Modules/socketmodule.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1715,8 +1715,10 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
17151715

17161716
struct sockaddr_un* addr = &addrbuf->un;
17171717
#ifdef __linux__
1718-
if (path.len > 0 && *(const char *)path.buf == 0) {
1719-
/* Linux abstract namespace extension */
1718+
if (path.len == 0 || *(const char *)path.buf == 0) {
1719+
/* Linux abstract namespace extension:
1720+
- Empty address auto-binding to an abstract address
1721+
- Address that starts with null byte */
17201722
if ((size_t)path.len > sizeof addr->sun_path) {
17211723
PyErr_SetString(PyExc_OSError,
17221724
"AF_UNIX path too long");

0 commit comments

Comments
 (0)