Skip to content

Commit 0ba13d3

Browse files
miss-islingtonserhiy-storchakaclaude
authored
[3.14] gh-126219: Fix crash in tkinter.Tk with non-BMP className on Tcl/Tk 8.x (GH-151980) (GH-152046)
Tcl 8.x crashes when title-casing a non-BMP character during Tk initialization, so such a className is now rejected with a ValueError. (cherry picked from commit 124c7cd) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 33b5aeb commit 0ba13d3

3 files changed

Lines changed: 43 additions & 1 deletion

File tree

Lib/test/test_tkinter/test_misc.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
from test.support import os_helper
88
from test.test_tkinter.support import setUpModule # noqa: F401
99
from test.test_tkinter.support import (AbstractTkTest, AbstractDefaultRootTest,
10-
requires_tk, get_tk_patchlevel)
10+
requires_tk, get_tk_patchlevel,
11+
tcl_version)
1112

1213
support.requires('gui')
1314

@@ -655,6 +656,30 @@ def test_embedded_null(self):
655656
self.assertEqual(widget.selection_get(), '\u20ac\0abc\x00def')
656657

657658

659+
class TkTest(AbstractTkTest, unittest.TestCase):
660+
661+
def test_className(self):
662+
# The className argument sets the class of the root window. Tk
663+
# title-cases it: the first letter is upper-cased, the rest lower-cased.
664+
cases = [
665+
('fooBAR', 'Foobar'),
666+
('éÉ', 'Éé'), # small and capital E WITH ACUTE
667+
]
668+
if tcl_version >= (9, 0):
669+
# small and capital DESERET LETTER LONG I (a non-BMP script)
670+
cases.append(('\U00010428\U00010400', '\U00010400\U00010428'))
671+
for className, klass in cases:
672+
root = tkinter.Tk(className=className)
673+
try:
674+
self.assertEqual(root.winfo_class(), klass)
675+
finally:
676+
root.destroy()
677+
if tcl_version < (9, 0):
678+
# gh-126219: title-casing a non-BMP first letter crashed Tcl 8.x;
679+
# such a class name is now rejected.
680+
self.assertRaises(ValueError, tkinter.Tk, className='\U00010428')
681+
682+
658683
class WinfoTest(AbstractTkTest, unittest.TestCase):
659684

660685
def test_winfo_rgb(self):
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
@@ -3239,6 +3239,20 @@ _tkinter_create_impl(PyObject *module, const char *screenName,
32393239
CHECK_STRING_LENGTH(className);
32403240
CHECK_STRING_LENGTH(use);
32413241

3242+
#if TCL_MAJOR_VERSION < 9
3243+
/* className is title-cased during Tk initialization. Tcl 8.x does not
3244+
* support non-BMP characters (encoded as 4-byte UTF-8 sequences) there
3245+
* and crashes in Tcl_UtfToTitle (see gh-126219). Reject them up front. */
3246+
for (const unsigned char *p = (const unsigned char *)className; *p; p++) {
3247+
if (*p >= 0xF0) {
3248+
PyErr_SetString(PyExc_ValueError,
3249+
"className must not contain non-BMP characters "
3250+
"with this version of Tcl/Tk");
3251+
return NULL;
3252+
}
3253+
}
3254+
#endif
3255+
32423256
return (PyObject *) Tkapp_New(screenName, className,
32433257
interactive, wantobjects, wantTk,
32443258
sync, use);

0 commit comments

Comments
 (0)