Skip to content

Commit

Permalink
gh-94821: Fix autobind of empty unix domain address (GH-94826) (GH-94875
Browse files Browse the repository at this point in the history
)

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 <nsoffer@redhat.com>
  • Loading branch information
miss-islington and nirs committed Jul 26, 2022
1 parent eff4aa5 commit cd0a59f
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 2 deletions.
19 changes: 19 additions & 0 deletions Lib/test/test_socket.py
Expand Up @@ -5417,6 +5417,20 @@ def testBytearrayName(self):
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):

Expand Down Expand Up @@ -5486,6 +5500,11 @@ def testUnencodableAddr(self):
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):
"""
Expand Down
@@ -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".
6 changes: 4 additions & 2 deletions Modules/socketmodule.c
Expand Up @@ -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");
Expand Down

0 comments on commit cd0a59f

Please sign in to comment.