]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-94821: Fix autobind of empty unix domain address (GH-94826) (GH-94875)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 26 Jul 2022 10:07:41 +0000 (03:07 -0700)
committerGitHub <noreply@github.com>
Tue, 26 Jul 2022 10:07:41 +0000 (12:07 +0200)
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 f6b3a07b7df6 (bpo-44493: Add missing terminated NUL in sockaddr_un's length (GH-26866)
(cherry picked from commit c22f134211743cd5ad14cec1dd4f527bee542b4c)

Co-authored-by: Nir Soffer <nsoffer@redhat.com>
Lib/test/test_socket.py
Misc/NEWS.d/next/Library/2022-07-14-00-43-52.gh-issue-94821.e17ghU.rst [new file with mode: 0644]
Modules/socketmodule.c

index 5712b46f7f111a3512eb048dc7f4a2cc00fbc221..127d61cb6a89e6b8ae430f154f70109e1727080d 100755 (executable)
@@ -5417,6 +5417,20 @@ class TestLinuxAbstractNamespace(unittest.TestCase):
             s.bind(bytearray(b"\x00python\x00test\x00"))
             self.assertEqual(s.getsockname(), b"\x00python\x00test\x00")
 
+    def testAutobind(self):
+        # Check that binding to an empty string binds to an available address
+        # in the abstract namespace as specified in unix(7) "Autobind feature".
+        abstract_address = b"^\0[0-9a-f]{5}"
+        with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s1:
+            s1.bind("")
+            self.assertRegex(s1.getsockname(), abstract_address)
+            # Each socket is bound to a different abstract address.
+            with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s2:
+                s2.bind("")
+                self.assertRegex(s2.getsockname(), abstract_address)
+                self.assertNotEqual(s1.getsockname(), s2.getsockname())
+
+
 @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'test needs socket.AF_UNIX')
 class TestUnixDomain(unittest.TestCase):
 
@@ -5486,6 +5500,11 @@ class TestUnixDomain(unittest.TestCase):
         self.addCleanup(support.unlink, path)
         self.assertEqual(self.sock.getsockname(), path)
 
+    @unittest.skipIf(sys.platform == 'linux', 'Linux specific test')
+    def testEmptyAddress(self):
+        # Test that binding empty address fails.
+        self.assertRaises(OSError, self.sock.bind, "")
+
 
 class BufferIOTest(SocketConnectedTest):
     """
diff --git a/Misc/NEWS.d/next/Library/2022-07-14-00-43-52.gh-issue-94821.e17ghU.rst b/Misc/NEWS.d/next/Library/2022-07-14-00-43-52.gh-issue-94821.e17ghU.rst
new file mode 100644 (file)
index 0000000..bf7885a
--- /dev/null
@@ -0,0 +1,2 @@
+Fix binding of unix socket to empty address on Linux to use an available
+address from the abstract namespace, instead of "\0".
index 6d6c92e4c95d199586b46eb595e42725c5cd5f0a..133470f9b8193ca6ec195804ad031a0772ee66ce 100644 (file)
@@ -1715,8 +1715,10 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
 
         struct sockaddr_un* addr = &addrbuf->un;
 #ifdef __linux__
-        if (path.len > 0 && *(const char *)path.buf == 0) {
-            /* Linux abstract namespace extension */
+        if (path.len == 0 || *(const char *)path.buf == 0) {
+            /* Linux abstract namespace extension:
+               - Empty address auto-binding to an abstract address
+               - Address that starts with null byte */
             if ((size_t)path.len > sizeof addr->sun_path) {
                 PyErr_SetString(PyExc_OSError,
                                 "AF_UNIX path too long");