Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions Doc/library/socket.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1738,16 +1738,23 @@ Socket Objects

.. method:: listen([backlog])

Enable a server to accept connections. If *backlog* is specified, it must
be at least 0 (if it is lower, it is set to 0); it specifies the number of
unaccepted connections that the system will allow before refusing new
connections. If not specified, a default reasonable value is chosen.
Enable a server to accept connections. If *backlog* is specified, it
specifies the number of unaccepted connections that the system will allow
before refusing new connections. If not specified, a default reasonable
value is chosen. The value is passed on to the system, which may adjust
it; the meaning of a value less than or equal to 0 is system dependent.
POSIX specifies that a negative value is treated as 0, but on Linux,
macOS and FreeBSD it selects the system maximum instead.

.. availability:: not WASI.

.. versionchanged:: 3.5
The *backlog* parameter is now optional.

.. versionchanged:: next
A negative *backlog* is now passed on to the system instead of being
clamped to 0.


.. method:: makefile(mode='r', buffering=None, *, encoding=None, \
errors=None, newline=None)
Expand Down
15 changes: 15 additions & 0 deletions Lib/test/test_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -1997,6 +1997,21 @@ def test_listen_backlog(self):
srv.bind((HOST, 0))
srv.listen()

def test_listen_backlog_connect(self):
# gh-93316: a negative backlog is passed on to listen(2) instead of
# being clamped to 0, and the socket can still accept a connection.
# backlog=0 is deliberately not tested here: a system is then free
# to not queue any incoming connection at all.
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as srv:
srv.bind((HOST, 0))
srv.listen(-1)
srv.settimeout(support.LOOPBACK_TIMEOUT)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as cli:
cli.settimeout(support.LOOPBACK_TIMEOUT)
cli.connect(srv.getsockname())
conn, _ = srv.accept()
conn.close()

@support.cpython_only
@unittest.skipIf(_testcapi is None, "requires _testcapi")
def test_listen_backlog_overflow(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
:meth:`socket.socket.listen` no longer clamps a negative *backlog* to 0, but
passes it on to the system, whose interpretation of it is system dependent.
4 changes: 0 additions & 4 deletions Modules/socketmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3917,10 +3917,6 @@ sock_listen(PyObject *self, PyObject *args)
return NULL;

Py_BEGIN_ALLOW_THREADS
/* To avoid problems on systems that don't allow a negative backlog
* (which doesn't make sense anyway) we force a minimum value of 0. */
if (backlog < 0)
backlog = 0;
res = listen(get_sock_fd(s), backlog);
Py_END_ALLOW_THREADS
if (res < 0)
Expand Down
Loading