Skip to content

Commit a32bd11

Browse files
[3.13] gh-137273: Fix debug assertion failure in locale.setlocale() on Windows (GH-137300) (GH-137306)
It happened when there were at least 16 characters after dot in the locale name. (cherry picked from commit 718e0c8) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 6a217ce commit a32bd11

File tree

3 files changed

+97
-21
lines changed

3 files changed

+97
-21
lines changed

Lib/test/test_locale.py

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from unittest import mock
66
import unittest
77
import locale
8+
import os
89
import sys
910
import codecs
1011

@@ -487,6 +488,54 @@ def test_japanese(self):
487488
self.check('jp_jp', 'ja_JP.eucJP')
488489

489490

491+
class TestRealLocales(unittest.TestCase):
492+
def setUp(self):
493+
oldlocale = locale.setlocale(locale.LC_CTYPE)
494+
self.addCleanup(locale.setlocale, locale.LC_CTYPE, oldlocale)
495+
496+
def test_getsetlocale_issue1813(self):
497+
# Issue #1813: setting and getting the locale under a Turkish locale
498+
try:
499+
locale.setlocale(locale.LC_CTYPE, 'tr_TR')
500+
except locale.Error:
501+
# Unsupported locale on this system
502+
self.skipTest('test needs Turkish locale')
503+
loc = locale.getlocale(locale.LC_CTYPE)
504+
if verbose:
505+
print('testing with %a' % (loc,), end=' ', flush=True)
506+
try:
507+
locale.setlocale(locale.LC_CTYPE, loc)
508+
except locale.Error as exc:
509+
# bpo-37945: setlocale(LC_CTYPE) fails with getlocale(LC_CTYPE)
510+
# and the tr_TR locale on Windows. getlocale() builds a locale
511+
# which is not recognize by setlocale().
512+
self.skipTest(f"setlocale(LC_CTYPE, {loc!r}) failed: {exc!r}")
513+
self.assertEqual(loc, locale.getlocale(locale.LC_CTYPE))
514+
515+
@unittest.skipUnless(os.name == 'nt', 'requires Windows')
516+
def test_setlocale_long_encoding(self):
517+
with self.assertRaises(locale.Error):
518+
locale.setlocale(locale.LC_CTYPE, 'English.%016d' % 1252)
519+
locale.setlocale(locale.LC_CTYPE, 'English.%015d' % 1252)
520+
loc = locale.setlocale(locale.LC_ALL)
521+
self.assertIn('.1252', loc)
522+
loc2 = loc.replace('.1252', '.%016d' % 1252, 1)
523+
with self.assertRaises(locale.Error):
524+
locale.setlocale(locale.LC_ALL, loc2)
525+
loc2 = loc.replace('.1252', '.%015d' % 1252, 1)
526+
locale.setlocale(locale.LC_ALL, loc2)
527+
528+
# gh-137273: Debug assertion failure on Windows for long encoding.
529+
with self.assertRaises(locale.Error):
530+
locale.setlocale(locale.LC_CTYPE, 'en_US.' + 'x'*16)
531+
locale.setlocale(locale.LC_CTYPE, 'en_US.UTF-8')
532+
loc = locale.setlocale(locale.LC_ALL)
533+
self.assertIn('.UTF-8', loc)
534+
loc2 = loc.replace('.UTF-8', '.' + 'x'*16, 1)
535+
with self.assertRaises(locale.Error):
536+
locale.setlocale(locale.LC_ALL, loc2)
537+
538+
490539
class TestMiscellaneous(unittest.TestCase):
491540
def test_defaults_UTF8(self):
492541
# Issue #18378: on (at least) macOS setting LC_CTYPE to "UTF-8" is
@@ -565,27 +614,6 @@ def test_setlocale_category(self):
565614
# crasher from bug #7419
566615
self.assertRaises(locale.Error, locale.setlocale, 12345)
567616

568-
def test_getsetlocale_issue1813(self):
569-
# Issue #1813: setting and getting the locale under a Turkish locale
570-
oldlocale = locale.setlocale(locale.LC_CTYPE)
571-
self.addCleanup(locale.setlocale, locale.LC_CTYPE, oldlocale)
572-
try:
573-
locale.setlocale(locale.LC_CTYPE, 'tr_TR')
574-
except locale.Error:
575-
# Unsupported locale on this system
576-
self.skipTest('test needs Turkish locale')
577-
loc = locale.getlocale(locale.LC_CTYPE)
578-
if verbose:
579-
print('testing with %a' % (loc,), end=' ', flush=True)
580-
try:
581-
locale.setlocale(locale.LC_CTYPE, loc)
582-
except locale.Error as exc:
583-
# bpo-37945: setlocale(LC_CTYPE) fails with getlocale(LC_CTYPE)
584-
# and the tr_TR locale on Windows. getlocale() builds a locale
585-
# which is not recognize by setlocale().
586-
self.skipTest(f"setlocale(LC_CTYPE, {loc!r}) failed: {exc!r}")
587-
self.assertEqual(loc, locale.getlocale(locale.LC_CTYPE))
588-
589617
def test_invalid_locale_format_in_localetuple(self):
590618
with self.assertRaises(TypeError):
591619
locale.setlocale(locale.LC_ALL, b'fi_FI')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix debug assertion failure in :func:`locale.setlocale` on Windows.

Modules/_localemodule.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,41 @@ copy_grouping(const char* s)
8787
return result;
8888
}
8989

90+
#if defined(MS_WINDOWS)
91+
92+
// 16 is the number of elements in the szCodePage field
93+
// of the __crt_locale_strings structure.
94+
#define MAX_CP_LEN 15
95+
96+
static int
97+
check_locale_name(const char *locale, const char *end)
98+
{
99+
size_t len = end ? (size_t)(end - locale) : strlen(locale);
100+
const char *dot = memchr(locale, '.', len);
101+
if (dot && locale + len - dot - 1 > MAX_CP_LEN) {
102+
return -1;
103+
}
104+
return 0;
105+
}
106+
107+
static int
108+
check_locale_name_all(const char *locale)
109+
{
110+
const char *start = locale;
111+
while (1) {
112+
const char *end = strchr(start, ';');
113+
if (check_locale_name(start, end) < 0) {
114+
return -1;
115+
}
116+
if (end == NULL) {
117+
break;
118+
}
119+
start = end + 1;
120+
}
121+
return 0;
122+
}
123+
#endif
124+
90125
/*[clinic input]
91126
_locale.setlocale
92127
@@ -111,6 +146,18 @@ _locale_setlocale_impl(PyObject *module, int category, const char *locale)
111146
"invalid locale category");
112147
return NULL;
113148
}
149+
if (locale) {
150+
if ((category == LC_ALL
151+
? check_locale_name_all(locale)
152+
: check_locale_name(locale, NULL)) < 0)
153+
{
154+
/* Debug assertion failure on Windows.
155+
* _Py_BEGIN_SUPPRESS_IPH/_Py_END_SUPPRESS_IPH do not help. */
156+
PyErr_SetString(get_locale_state(module)->Error,
157+
"unsupported locale setting");
158+
return NULL;
159+
}
160+
}
114161
#endif
115162

116163
if (locale) {

0 commit comments

Comments
 (0)