Skip to content

Commit 718e0c8

Browse files
gh-137273: Fix debug assertion failure in locale.setlocale() on Windows (GH-137300)
It happened when there were at least 16 characters after dot in the locale name.
1 parent e99bc7f commit 718e0c8

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

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

488489

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

555-
def test_getsetlocale_issue1813(self):
556-
# Issue #1813: setting and getting the locale under a Turkish locale
557-
oldlocale = locale.setlocale(locale.LC_CTYPE)
558-
self.addCleanup(locale.setlocale, locale.LC_CTYPE, oldlocale)
559-
try:
560-
locale.setlocale(locale.LC_CTYPE, 'tr_TR')
561-
except locale.Error:
562-
# Unsupported locale on this system
563-
self.skipTest('test needs Turkish locale')
564-
loc = locale.getlocale(locale.LC_CTYPE)
565-
if verbose:
566-
print('testing with %a' % (loc,), end=' ', flush=True)
567-
try:
568-
locale.setlocale(locale.LC_CTYPE, loc)
569-
except locale.Error as exc:
570-
# bpo-37945: setlocale(LC_CTYPE) fails with getlocale(LC_CTYPE)
571-
# and the tr_TR locale on Windows. getlocale() builds a locale
572-
# which is not recognize by setlocale().
573-
self.skipTest(f"setlocale(LC_CTYPE, {loc!r}) failed: {exc!r}")
574-
self.assertEqual(loc, locale.getlocale(locale.LC_CTYPE))
575-
576604
def test_invalid_locale_format_in_localetuple(self):
577605
with self.assertRaises(TypeError):
578606
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)