]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-93251: Decode localized socket error messages from the locale encoding (GH-154683)
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 26 Jul 2026 09:31:26 +0000 (12:31 +0300)
committerGitHub <noreply@github.com>
Sun, 26 Jul 2026 09:31:26 +0000 (12:31 +0300)
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 <noreply@anthropic.com>
Lib/test/test_socket.py
Misc/NEWS.d/next/Library/2026-07-25-13-30-00.gh-issue-93251.gAiErr.rst [new file with mode: 0644]
Modules/socketmodule.c

index a234d6b37ff37976d711c65f55c1891e4353310c..7bb50f7b8aa47e28213f2a2f8b18b3e686c20192 100644 (file)
@@ -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 (file)
index 0000000..3f31303
--- /dev/null
@@ -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.
index 084c2dbcff066e5ee377d1d3edafa5b9f308a933..73ae1c942daba48bcddd0d021b92c6cdff1208fe 100644 (file)
@@ -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