From: Serhiy Storchaka Date: Sun, 26 Jul 2026 09:31:26 +0000 (+0300) Subject: gh-93251: Decode localized socket error messages from the locale encoding (GH-154683) X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=5afbb60e0283caaf34990bbe8437111ebaae04a4;p=thirdparty%2FPython%2Fcpython.git gh-93251: Decode localized socket error messages from the locale encoding (GH-154683) The gai_strerror() and hstrerror() messages were decoded as UTF-8, so gaierror and herror could be replaced with UnicodeDecodeError if the message is localized and the locale encoding is not UTF-8. Co-authored-by: Claude Fable 5 --- diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index a234d6b37ff3..7bb50f7b8aa4 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -1787,6 +1787,23 @@ class GeneralModuleTests(unittest.TestCase): except socket.gaierror: pass + @unittest.skipUnless(hasattr(socket, 'AI_NUMERICSERV'), + 'needs socket.AI_NUMERICSERV') + @support.thread_unsafe('setlocale is not thread-safe') + @support.run_with_locales('LC_ALL', + 'uk_UA.KOI8-U', 'uk_UA', 'ja_JP.eucJP', 'ja_JP.SJIS', 'ja_JP', + 'ko_KR.eucKR', 'zh_CN.GB18030', 'el_GR.ISO8859-7', + 'de_DE.ISO8859-1', 'ja_JP.UTF-8', + '') + def test_getaddrinfo_localized_error(self): + # gh-93251: the localized gai_strerror() message could fail to be + # decoded as UTF-8, so UnicodeDecodeError was raised + # instead of gaierror. + with self.assertRaises(socket.gaierror) as cm: + socket.getaddrinfo("localhost", "http", + flags=socket.AI_NUMERICSERV) + str(cm.exception) + @unittest.skipIf(_testcapi is None, "requires _testcapi") def test_getaddrinfo_int_port_overflow(self): # gh-74895: Test that getaddrinfo does not raise OverflowError on port. diff --git a/Misc/NEWS.d/next/Library/2026-07-25-13-30-00.gh-issue-93251.gAiErr.rst b/Misc/NEWS.d/next/Library/2026-07-25-13-30-00.gh-issue-93251.gAiErr.rst new file mode 100644 index 000000000000..3f313030802f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-25-13-30-00.gh-issue-93251.gAiErr.rst @@ -0,0 +1,4 @@ +Fix :exc:`UnicodeDecodeError` in :mod:`socket` functions +(such as :func:`~socket.getaddrinfo` and :func:`~socket.gethostbyaddr`) +when the localized error message of the C library is not UTF-8: +decode it from the locale encoding. diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index 084c2dbcff06..73ae1c942dab 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -745,6 +745,16 @@ set_error(void) } +#if defined(HAVE_HSTRERROR) || defined(HAVE_GAI_STRERROR) +/* Decode a locale-encoded error message from the C library. + It can be localized and use a non-UTF-8 encoding. */ +static PyObject * +decode_error_message(const char *str) +{ + return PyUnicode_DecodeLocale(str, "surrogateescape"); +} +#endif + #if defined(HAVE_GETHOSTBYNAME_R) || defined (HAVE_GETHOSTBYNAME) || defined (HAVE_GETHOSTBYADDR) static PyObject * set_herror(socket_state *state, int h_error) @@ -752,7 +762,7 @@ set_herror(socket_state *state, int h_error) PyObject *v; #ifdef HAVE_HSTRERROR - v = Py_BuildValue("(is)", h_error, hstrerror(h_error)); + v = Py_BuildValue("(iN)", h_error, decode_error_message(hstrerror(h_error))); #else v = Py_BuildValue("(is)", h_error, "host not found"); #endif @@ -779,7 +789,7 @@ set_gaierror(socket_state *state, int error) #endif #ifdef HAVE_GAI_STRERROR - v = Py_BuildValue("(is)", error, gai_strerror(error)); + v = Py_BuildValue("(iN)", error, decode_error_message(gai_strerror(error))); #else v = Py_BuildValue("(is)", error, "getaddrinfo failed"); #endif