Skip to content

Commit bf0f06a

Browse files
gh-126219: Fix crash in tkinter.Tk with non-BMP className on Tcl/Tk 8.x
Tcl 8.x crashes when title-casing a non-BMP character during Tk initialization, so such a className is now rejected with a ValueError. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent da99711 commit bf0f06a

3 files changed

Lines changed: 40 additions & 1 deletion

File tree

Lib/test/test_tkinter/test_misc.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
from test.support import os_helper
1111
from test.test_tkinter.support import setUpModule # noqa: F401
1212
from test.test_tkinter.support import (AbstractTkTest, AbstractDefaultRootTest,
13-
requires_tk, get_tk_patchlevel)
13+
requires_tk, get_tk_patchlevel,
14+
tcl_version)
1415

1516
support.requires('gui')
1617

@@ -463,6 +464,27 @@ def test_tk_bisque(self):
463464
self.assertEqual(root['background'], '#ffe4c4')
464465
self.assertRaises(TypeError, root.tk_bisque, 'x')
465466

467+
def test_className(self):
468+
# Tk title-cases the class name: the first letter is upper-cased and
469+
# the rest lower-cased.
470+
cases = [
471+
('fooBAR', 'Foobar'),
472+
('\u00e9\u00c9', '\u00c9\u00e9'), # small and capital E WITH ACUTE
473+
]
474+
if tcl_version >= (9, 0):
475+
# small and capital DESERET LETTER LONG I (a non-BMP script)
476+
cases.append(('\U00010428\U00010400', '\U00010400\U00010428'))
477+
for className, klass in cases:
478+
root = tkinter.Tk(className=className)
479+
try:
480+
self.assertEqual(root.winfo_class(), klass)
481+
finally:
482+
root.destroy()
483+
if tcl_version < (9, 0):
484+
# gh-126219: title-casing a non-BMP first letter crashed Tcl 8.x;
485+
# such a class name is now rejected.
486+
self.assertRaises(ValueError, tkinter.Tk, className='\U00010428')
487+
466488
def test_tk_appname(self):
467489
old = self.root.tk_appname()
468490
self.assertIsInstance(old, str)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed a crash in :class:`tkinter.Tk` when *className* contains a non-BMP
2+
character and tkinter is built against Tcl/Tk 8.x. Such a name is now
3+
rejected with a :exc:`ValueError`.

Modules/_tkinter.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3272,6 +3272,20 @@ _tkinter_create_impl(PyObject *module, const char *screenName,
32723272
CHECK_STRING_LENGTH(className);
32733273
CHECK_STRING_LENGTH(use);
32743274

3275+
#if TCL_MAJOR_VERSION < 9
3276+
/* className is title-cased during Tk initialization. Tcl 8.x does not
3277+
* support non-BMP characters (encoded as 4-byte UTF-8 sequences) there
3278+
* and crashes in Tcl_UtfToTitle (see gh-126219). Reject them up front. */
3279+
for (const unsigned char *p = (const unsigned char *)className; *p; p++) {
3280+
if (*p >= 0xF0) {
3281+
PyErr_SetString(PyExc_ValueError,
3282+
"className must not contain non-BMP characters "
3283+
"with this version of Tcl/Tk");
3284+
return NULL;
3285+
}
3286+
}
3287+
#endif
3288+
32753289
return (PyObject *) Tkapp_New(screenName, className,
32763290
interactive, wantobjects, wantTk,
32773291
sync, use);

0 commit comments

Comments
 (0)