From e4739c47bf35750f4fc29ef359a2f11f1948ce7d Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Tue, 27 Oct 2026 16:26:49 -0500 Subject: [PATCH 1/4] gh-160898: Reject null bytes in getaddrinfo() host and port --- Doc/library/socket.rst | 3 ++ Lib/test/test_socket.py | 33 +++++++++++++++++++ ...-10-27-16-25-40.gh-issue-160898.epi53X.rst | 4 +++ Modules/socketmodule.c | 20 +++++++++-- 4 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 054ccc556740c20..34fa56385a50644 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -1065,6 +1065,9 @@ The :mod:`!socket` module also offers various network-related services: for IPv6 multicast addresses, string representing an address will not contain ``%scope_id`` part. + .. versionchanged:: next + null characters are now rejected in ``host`` and ``port`` parameters. + .. _Happy Eyeballs: https://en.wikipedia.org/wiki/Happy_Eyeballs .. function:: getfqdn([name]) diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index 51b666bbaba8271..b37925af1e2d0f7 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -2104,6 +2104,39 @@ def test_getnameinfo_ipv6_scopeid_numeric(self): nameinfo = socket.getnameinfo(sockaddr, socket.NI_NUMERICHOST | socket.NI_NUMERICSERV) self.assertEqual(nameinfo, ('ff02::1de:c0:face:8d%' + str(ifindex), '1234')) + def test_getaddrinfo_null_bytes(self): + # getaddrinfo() must reject embedded null bytes instead of silently + # truncating the host or port at the C boundary. The checks run + # before any name resolution, so no network access is needed. + hosts = [ + # embedded + 'local\0host', b'local\0host', + # trailing + 'localhost\0', b'localhost\0', + # leading (truncates to '') + '\0localhost', b'\0localhost', + '\0', b'\0', + # non-ASCII, goes through IDNA + 'b\xfccher\0.example', + ] + for host in hosts: + with self.subTest(host=host): + with self.assertRaisesRegex(ValueError, 'host'): + socket.getaddrinfo(host, 80) + + ports = [ + '8\x000', + b'8\x000', + 'http\0', + b'http\0', + '\0', + b'\0' + ] + for port in ports: + with self.subTest(port=port): + with self.assertRaisesRegex(ValueError, 'port'): + socket.getaddrinfo("localhost", port) + def test_str_for_enums(self): # Make sure that the AF_* and SOCK_* constants have enum-like string # reprs. diff --git a/Misc/NEWS.d/next/Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst b/Misc/NEWS.d/next/Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst new file mode 100644 index 000000000000000..e84c70bbdb4d3b8 --- /dev/null +++ b/Misc/NEWS.d/next/Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst @@ -0,0 +1,4 @@ +.. cve: CVE-2026-116566 + +``socket.getaddrinfo()`` now rejects null bytes within ``host`` and ``port`` +parameters to avoid being truncated during resolution. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 97146b1d752c3d8..4df03284cbdd24f 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -7018,6 +7018,7 @@ _socket_getaddrinfo_impl(PyObject *module, PyObject *hobj, PyObject *pobj, struct addrinfo *res0 = NULL; PyObject *pstr = NULL; const char *hptr, *pptr; + Py_ssize_t hlen = 0, plen = 0; int error; PyObject *all = (PyObject *)NULL; PyObject *idna = NULL; @@ -7030,13 +7031,20 @@ _socket_getaddrinfo_impl(PyObject *module, PyObject *hobj, PyObject *pobj, return NULL; assert(PyBytes_Check(idna)); hptr = PyBytes_AS_STRING(idna); + hlen = PyBytes_GET_SIZE(idna); } else if (PyBytes_Check(hobj)) { - hptr = PyBytes_AsString(hobj); + hptr = PyBytes_AS_STRING(hobj); + hlen = PyBytes_GET_SIZE(hobj); } else { PyErr_SetString(PyExc_TypeError, "getaddrinfo() argument 1 must be string or None"); return NULL; } + if (hptr != NULL && strlen(hptr) != (size_t)hlen) { + PyErr_SetString(PyExc_ValueError, + "getaddrinfo() host must not contain null characters"); + goto err; + } if (PyIndex_Check(pobj)) { pstr = PyNumber_Index(pobj); if (pstr == NULL) @@ -7046,21 +7054,27 @@ _socket_getaddrinfo_impl(PyObject *module, PyObject *hobj, PyObject *pobj, if (pstr == NULL) goto err; assert(PyUnicode_Check(pstr)); - pptr = PyUnicode_AsUTF8(pstr); + pptr = PyUnicode_AsUTF8AndSize(pstr, &plen); if (pptr == NULL) goto err; } else if (PyUnicode_Check(pobj)) { - pptr = PyUnicode_AsUTF8(pobj); + pptr = PyUnicode_AsUTF8AndSize(pobj, &plen); if (pptr == NULL) goto err; } else if (PyBytes_Check(pobj)) { pptr = PyBytes_AS_STRING(pobj); + plen = PyBytes_GET_SIZE(pobj); } else if (pobj == Py_None) { pptr = (char *)NULL; } else { PyErr_SetString(PyExc_OSError, "Int or String expected"); goto err; } + if (pptr != NULL && strlen(pptr) != (size_t)plen) { + PyErr_SetString(PyExc_ValueError, + "getaddrinfo() port must not contain null characters"); + goto err; + } #if defined(__APPLE__) && defined(AI_NUMERICSERV) if ((flags & AI_NUMERICSERV) && (pptr == NULL || (pptr[0] == '0' && pptr[1] == 0))) { /* On OSX up to at least OSX 10.8 getaddrinfo crashes From 63d843a5c33d1a3ff3fe02c7f9e244c76bc93473 Mon Sep 17 00:00:00 2001 From: Seth Larson Date: Thu, 29 Oct 2026 14:54:52 +0000 Subject: [PATCH 2/4] Apply changes suggested in code review Co-authored-by: Stan Ulbrych --- .../Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst b/Misc/NEWS.d/next/Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst index e84c70bbdb4d3b8..48733495c601bbe 100644 --- a/Misc/NEWS.d/next/Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst +++ b/Misc/NEWS.d/next/Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst @@ -1,4 +1,4 @@ .. cve: CVE-2026-116566 -``socket.getaddrinfo()`` now rejects null bytes within ``host`` and ``port`` -parameters to avoid being truncated during resolution. +:func:`socket.getaddrinfo()` now rejects null bytes within *host* and *port* +parameters as this previously caused the values to be silently truncated. From f3afc0af1696c1afa4b706038c4660505fb00462 Mon Sep 17 00:00:00 2001 From: Seth Larson Date: Thu, 29 Oct 2026 14:55:06 +0000 Subject: [PATCH 3/4] Apply changes suggested in code review Co-authored-by: Stan Ulbrych --- Doc/library/socket.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst index 34fa56385a50644..5a1cd95da527ea8 100644 --- a/Doc/library/socket.rst +++ b/Doc/library/socket.rst @@ -1066,7 +1066,7 @@ The :mod:`!socket` module also offers various network-related services: contain ``%scope_id`` part. .. versionchanged:: next - null characters are now rejected in ``host`` and ``port`` parameters. + Null characters are now rejected in the *host* and *port* parameters. .. _Happy Eyeballs: https://en.wikipedia.org/wiki/Happy_Eyeballs From 92ff489802a5acc36812db0bd60bfb2b9d10684d Mon Sep 17 00:00:00 2001 From: Seth Larson Date: Thu, 29 Oct 2026 15:01:53 +0000 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: Stan Ulbrych --- .../Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst b/Misc/NEWS.d/next/Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst index 48733495c601bbe..2eb04454cbd9a7f 100644 --- a/Misc/NEWS.d/next/Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst +++ b/Misc/NEWS.d/next/Security/2026-10-27-16-25-40.gh-issue-160898.epi53X.rst @@ -1,4 +1,4 @@ .. cve: CVE-2026-116566 -:func:`socket.getaddrinfo()` now rejects null bytes within *host* and *port* +:func:`socket.getaddrinfo` now rejects null bytes within *host* and *port* parameters as this previously caused the values to be silently truncated.