]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-137273: Fix debug assertion failure in locale.setlocale() on Windows (GH-137300)
authorSerhiy Storchaka <storchaka@gmail.com>
Fri, 1 Aug 2025 14:43:53 +0000 (17:43 +0300)
committerGitHub <noreply@github.com>
Fri, 1 Aug 2025 14:43:53 +0000 (17:43 +0300)
It happened when there were at least 16 characters after dot in the
locale name.

Lib/test/test_locale.py
Misc/NEWS.d/next/Library/2025-08-01-15-07-59.gh-issue-137273.4V8Xmv.rst [new file with mode: 0644]
Modules/_localemodule.c

index 55b502e52ca45450b4942127aabafa06734dc773..698e137e3e8abd6234f3ae048091f50e083e3455 100644 (file)
@@ -5,6 +5,7 @@ from test.support.import_helper import ensure_lazy_imports, import_fresh_module
 from unittest import mock
 import unittest
 import locale
+import os
 import sys
 import codecs
 
@@ -486,6 +487,54 @@ class NormalizeTest(unittest.TestCase):
         self.check('jp_jp', 'ja_JP.eucJP')
 
 
+class TestRealLocales(unittest.TestCase):
+    def setUp(self):
+        oldlocale = locale.setlocale(locale.LC_CTYPE)
+        self.addCleanup(locale.setlocale, locale.LC_CTYPE, oldlocale)
+
+    def test_getsetlocale_issue1813(self):
+        # Issue #1813: setting and getting the locale under a Turkish locale
+        try:
+            locale.setlocale(locale.LC_CTYPE, 'tr_TR')
+        except locale.Error:
+            # Unsupported locale on this system
+            self.skipTest('test needs Turkish locale')
+        loc = locale.getlocale(locale.LC_CTYPE)
+        if verbose:
+            print('testing with %a' % (loc,), end=' ', flush=True)
+        try:
+            locale.setlocale(locale.LC_CTYPE, loc)
+        except locale.Error as exc:
+            # bpo-37945: setlocale(LC_CTYPE) fails with getlocale(LC_CTYPE)
+            # and the tr_TR locale on Windows. getlocale() builds a locale
+            # which is not recognize by setlocale().
+            self.skipTest(f"setlocale(LC_CTYPE, {loc!r}) failed: {exc!r}")
+        self.assertEqual(loc, locale.getlocale(locale.LC_CTYPE))
+
+    @unittest.skipUnless(os.name == 'nt', 'requires Windows')
+    def test_setlocale_long_encoding(self):
+        with self.assertRaises(locale.Error):
+            locale.setlocale(locale.LC_CTYPE, 'English.%016d' % 1252)
+        locale.setlocale(locale.LC_CTYPE, 'English.%015d' % 1252)
+        loc = locale.setlocale(locale.LC_ALL)
+        self.assertIn('.1252', loc)
+        loc2 = loc.replace('.1252', '.%016d' % 1252, 1)
+        with self.assertRaises(locale.Error):
+            locale.setlocale(locale.LC_ALL, loc2)
+        loc2 = loc.replace('.1252', '.%015d' % 1252, 1)
+        locale.setlocale(locale.LC_ALL, loc2)
+
+        # gh-137273: Debug assertion failure on Windows for long encoding.
+        with self.assertRaises(locale.Error):
+            locale.setlocale(locale.LC_CTYPE, 'en_US.' + 'x'*16)
+        locale.setlocale(locale.LC_CTYPE, 'en_US.UTF-8')
+        loc = locale.setlocale(locale.LC_ALL)
+        self.assertIn('.UTF-8', loc)
+        loc2 = loc.replace('.UTF-8', '.' + 'x'*16, 1)
+        with self.assertRaises(locale.Error):
+            locale.setlocale(locale.LC_ALL, loc2)
+
+
 class TestMiscellaneous(unittest.TestCase):
     def test_defaults_UTF8(self):
         # Issue #18378: on (at least) macOS setting LC_CTYPE to "UTF-8" is
@@ -552,27 +601,6 @@ class TestMiscellaneous(unittest.TestCase):
         # crasher from bug #7419
         self.assertRaises(locale.Error, locale.setlocale, 12345)
 
-    def test_getsetlocale_issue1813(self):
-        # Issue #1813: setting and getting the locale under a Turkish locale
-        oldlocale = locale.setlocale(locale.LC_CTYPE)
-        self.addCleanup(locale.setlocale, locale.LC_CTYPE, oldlocale)
-        try:
-            locale.setlocale(locale.LC_CTYPE, 'tr_TR')
-        except locale.Error:
-            # Unsupported locale on this system
-            self.skipTest('test needs Turkish locale')
-        loc = locale.getlocale(locale.LC_CTYPE)
-        if verbose:
-            print('testing with %a' % (loc,), end=' ', flush=True)
-        try:
-            locale.setlocale(locale.LC_CTYPE, loc)
-        except locale.Error as exc:
-            # bpo-37945: setlocale(LC_CTYPE) fails with getlocale(LC_CTYPE)
-            # and the tr_TR locale on Windows. getlocale() builds a locale
-            # which is not recognize by setlocale().
-            self.skipTest(f"setlocale(LC_CTYPE, {loc!r}) failed: {exc!r}")
-        self.assertEqual(loc, locale.getlocale(locale.LC_CTYPE))
-
     def test_invalid_locale_format_in_localetuple(self):
         with self.assertRaises(TypeError):
             locale.setlocale(locale.LC_ALL, b'fi_FI')
diff --git a/Misc/NEWS.d/next/Library/2025-08-01-15-07-59.gh-issue-137273.4V8Xmv.rst b/Misc/NEWS.d/next/Library/2025-08-01-15-07-59.gh-issue-137273.4V8Xmv.rst
new file mode 100644 (file)
index 0000000..f344877
--- /dev/null
@@ -0,0 +1 @@
+Fix debug assertion failure in :func:`locale.setlocale` on Windows.
index 41e6d48b1dbd9bb96acced3f9cb19923c8be4c35..17b5220fd6f9e15095d272dc9d589b7706cf16d2 100644 (file)
@@ -87,6 +87,41 @@ copy_grouping(const char* s)
     return result;
 }
 
+#if defined(MS_WINDOWS)
+
+// 16 is the number of elements in the szCodePage field
+// of the __crt_locale_strings structure.
+#define MAX_CP_LEN 15
+
+static int
+check_locale_name(const char *locale, const char *end)
+{
+    size_t len = end ? (size_t)(end - locale) : strlen(locale);
+    const char *dot = memchr(locale, '.', len);
+    if (dot && locale + len - dot - 1 > MAX_CP_LEN) {
+        return -1;
+    }
+    return 0;
+}
+
+static int
+check_locale_name_all(const char *locale)
+{
+    const char *start = locale;
+    while (1) {
+        const char *end = strchr(start, ';');
+        if (check_locale_name(start, end) < 0) {
+            return -1;
+        }
+        if (end == NULL) {
+            break;
+        }
+        start = end + 1;
+    }
+    return 0;
+}
+#endif
+
 /*[clinic input]
 _locale.setlocale
 
@@ -111,6 +146,18 @@ _locale_setlocale_impl(PyObject *module, int category, const char *locale)
                         "invalid locale category");
         return NULL;
     }
+    if (locale) {
+        if ((category == LC_ALL
+             ? check_locale_name_all(locale)
+             : check_locale_name(locale, NULL)) < 0)
+        {
+            /* Debug assertion failure on Windows.
+             * _Py_BEGIN_SUPPRESS_IPH/_Py_END_SUPPRESS_IPH do not help. */
+            PyErr_SetString(get_locale_state(module)->Error,
+                "unsupported locale setting");
+            return NULL;
+        }
+    }
 #endif
 
     if (locale) {