[3.13] gh-137273: Fix debug assertion failure in locale.setlocale() on Windows (GH-137300) (GH-137306)
Some checks are pending
Tests / Check if generated files are up to date (push) Blocked by required conditions
Tests / Change detection (push) Waiting to run
Tests / Docs (push) Blocked by required conditions
Tests / (push) Blocked by required conditions
Tests / Windows MSI (push) Blocked by required conditions
Tests / Check if the ABI has changed (push) Blocked by required conditions
Tests / Check if Autoconf files are up to date (push) Blocked by required conditions
Tests / Sanitizers (push) Blocked by required conditions
Tests / Ubuntu SSL tests with OpenSSL (push) Blocked by required conditions
Tests / WASI (push) Blocked by required conditions
Tests / Hypothesis tests on Ubuntu (push) Blocked by required conditions
Tests / Address sanitizer (push) Blocked by required conditions
Tests / CIFuzz (push) Blocked by required conditions
Tests / All required checks pass (push) Blocked by required conditions
Lint / lint (push) Waiting to run

It happened when there were at least 16 characters after dot in the
locale name.
(cherry picked from commit 718e0c89ba)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
Miss Islington (bot) 2025-08-01 17:06:16 +02:00 committed by GitHub
parent 6a217cef27
commit a32bd11cb0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 97 additions and 21 deletions

View file

@ -5,6 +5,7 @@ from test.support.import_helper import import_fresh_module
from unittest import mock
import unittest
import locale
import os
import sys
import codecs
@ -487,6 +488,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
@ -565,27 +614,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')

View file

@ -0,0 +1 @@
Fix debug assertion failure in :func:`locale.setlocale` on Windows.

View 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) {