Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autobind of empty unix socket on Linux broken since python 3.9 #94821

Closed
nirs opened this issue Jul 13, 2022 · 1 comment
Closed

Autobind of empty unix socket on Linux broken since python 3.9 #94821

nirs opened this issue Jul 13, 2022 · 1 comment
Labels
3.9 only security fixes 3.10 only security fixes 3.11 only security fixes 3.12 bugs and security fixes type-bug An unexpected behavior, bug, or error

Comments

@nirs
Copy link
Contributor

nirs commented Jul 13, 2022

Bug report

When using empty address for unix socket on Linux, the socket is bound to a random
address in the abstract namespace.

from unix(7):

   Autobind feature
       If  a  bind(2)  call  specifies  addrlen as sizeof(sa_family_t), or the
       SO_PASSCRED socket option was specified for a socket that was  not  ex‐
       plicitly  bound  to  an address, then the socket is autobound to an ab‐
       stract address.  The address consists of a  null  byte  followed  by  5
       bytes  in  the  character set [0-9a-f].  Thus, there is a limit of 2^20
       autobind addresses.  (From Linux 2.1.15, when the autobind feature  was
       added,  8  bytes  were  used,  and the limit was thus 2^32 autobind ad‐
       dresses.  The change to 5 bytes came in Linux 2.3.15.)

Since python 3.9, when using empty socket address (""), bind() is called with
with path.len + offsetof(struct sockaddr_un, sun_path) + 1, so the socket is
bound to the specific address "\0" instead of a random address (e.g. "\075499").
This breaks code assuming that multiple sockets can be bound to a random address
at the same time.

In python 3.8 (not including this change):

$ python3.8
Python 3.8.13 (default, Jun 10 2022, 00:00:00) 
[GCC 12.1.1 20220507 (Red Hat 12.1.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
>>> s.bind("")
>>> s.getsockname()
b'\x0075499'

With python 3.9 (including this change):

$ python3.9
Python 3.9.13 (main, Jun  9 2022, 00:00:00) 
[GCC 12.1.1 20220507 (Red Hat 12.1.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
>>> s.bind("")
>>> s.getsockname()
b'\x00'

Your environment

  • CPython versions tested on: python 3.9, python 3.10
  • Operating system and architecture: Fedora 36, Ubuntu latest (GitHub actions)

The breaking change is f6b3a07, backported
to python 3.9. I suspect that change was not needed since it did not include
any test showing a real issue, but it is possible to fix handling of empty
unix socket address without reverting this change.

@nirs nirs added the type-bug An unexpected behavior, bug, or error label Jul 13, 2022
@tiran tiran added 3.11 only security fixes 3.10 only security fixes 3.12 bugs and security fixes 3.9 only security fixes labels Jul 13, 2022
aesteve-rh added a commit to aesteve-rh/ovirt-imageio that referenced this issue Jul 14, 2022
A recent change in cpython broke autobind of abstract
unix socket in linux, causing bind calls with an
empty string to be bound to '\0' instead of a
random address (expected). This results in an
'address already in use' error when consecutive empty
string binding is used in the tests.

While this issue may get solved eventually, we
can change the test strategy and use the random
unique addresses in the abstract space, to create
unique sockets per test so that they do not collide,
and do not depend on the empty socket feature.

Related: python/cpython#94821
Signed-off-by: Albert Esteve <aesteve@redhat.com>
aesteve-rh added a commit to aesteve-rh/ovirt-imageio that referenced this issue Jul 14, 2022
A recent change in cpython broke autobind of abstract
unix socket in linux, causing bind calls with an
empty string to be bound to '\0' instead of a
random address (expected). This results in an
'address already in use' error when consecutive empty
string binding is used in the tests.

While this issue may get solved eventually, we
can change the test strategy and use the random
unique addresses in the abstract space, to create
unique sockets per test so that they do not collide,
and do not depend on the empty socket feature.

Related: python/cpython#94821
Signed-off-by: Albert Esteve <aesteve@redhat.com>
aesteve-rh added a commit to aesteve-rh/ovirt-imageio that referenced this issue Jul 14, 2022
A recent change in cpython broke autobind of abstract
unix socket in linux, causing bind calls with an
empty string to be bound to '\0' instead of a
random address (expected). This results in an
'address already in use' error when consecutive empty
string binding is used in the tests.

While this issue may get solved eventually, we
can change the test strategy and use the random
unique addresses in the abstract space, to create
unique sockets per test so that they do not collide,
and do not depend on the empty socket feature.

Related: python/cpython#94821
Signed-off-by: Albert Esteve <aesteve@redhat.com>
aesteve-rh added a commit to aesteve-rh/ovirt-imageio that referenced this issue Jul 14, 2022
A recent change in cpython broke autobind of abstract
unix socket in linux, causing bind calls with an
empty string to be bound to '\0' instead of a
random address (expected). This results in an
'address already in use' error when consecutive empty
string binding is used.

While we wait for this issue to be solved, we need
to enforce a random address to be used for empty
strings in the abstract space.

Related: python/cpython#94821
Signed-off-by: Albert Esteve <aesteve@redhat.com>
nirs pushed a commit to oVirt/ovirt-imageio that referenced this issue Jul 14, 2022
A recent change in cpython broke autobind of abstract
unix socket in linux, causing bind calls with an
empty string to be bound to '\0' instead of a
random address (expected). This results in an
'address already in use' error when consecutive empty
string binding is used.

While we wait for this issue to be solved, we need
to enforce a random address to be used for empty
strings in the abstract space.

Related: python/cpython#94821
Signed-off-by: Albert Esteve <aesteve@redhat.com>
serhiy-storchaka pushed a commit that referenced this issue Jul 15, 2022
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)
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Jul 15, 2022
…4826)

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 (pythonGH-26866)
(cherry picked from commit c22f134)

Co-authored-by: Nir Soffer <nsoffer@redhat.com>
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Jul 15, 2022
…4826)

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 (pythonGH-26866)
(cherry picked from commit c22f134)

Co-authored-by: Nir Soffer <nsoffer@redhat.com>
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Jul 15, 2022
…4826)

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 (pythonGH-26866)
(cherry picked from commit c22f134)

Co-authored-by: Nir Soffer <nsoffer@redhat.com>
miss-islington added a commit that referenced this issue Jul 17, 2022
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>
miss-islington added a commit that referenced this issue Jul 17, 2022
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>
ambv pushed a commit that referenced this issue Jul 26, 2022
)

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>
@zhuofeng6
Copy link

any question about this issue? if not, i think it is better to close it

@gpshead gpshead closed this as completed Sep 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.9 only security fixes 3.10 only security fixes 3.11 only security fixes 3.12 bugs and security fixes type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

4 participants