Issue #17119: Fixed integer overflows when processing large Unicode strings
and tuples in the tkinter module.
diff --git a/Lib/test/test_tcl.py b/Lib/test/test_tcl.py
index 3da87d9..3460262 100644
--- a/Lib/test/test_tcl.py
+++ b/Lib/test/test_tcl.py
@@ -3,6 +3,7 @@
import unittest
import sys
import os
+import _testcapi
from test import test_support
from subprocess import Popen, PIPE
@@ -245,8 +246,22 @@
self.assertEqual(split(arg), res)
+class BigmemTclTest(unittest.TestCase):
+
+ def setUp(self):
+ self.interp = Tcl()
+
+ @unittest.skipUnless(_testcapi.INT_MAX < _testcapi.PY_SSIZE_T_MAX,
+ "needs UINT_MAX < SIZE_MAX")
+ @test_support.precisionbigmemtest(size=_testcapi.INT_MAX + 1, memuse=5,
+ dry_run=False)
+ def test_huge_string(self, size):
+ value = ' ' * size
+ self.assertRaises(OverflowError, self.interp.call, 'set', '_', value)
+
+
def test_main():
- test_support.run_unittest(TclTest, TkinterTest)
+ test_support.run_unittest(TclTest, TkinterTest, BigmemTclTest)
if __name__ == "__main__":
test_main()
diff --git a/Misc/NEWS b/Misc/NEWS
index 83b5116..67853e4 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -32,6 +32,9 @@
Library
-------
+- Issue #17119: Fixed integer overflows when processing large Unicode strings
+ and tuples in the tkinter module.
+
- Issue #15233: Python now guarantees that callables registered with the atexit
module will be called in a deterministic order.
diff --git a/Modules/_tkinter.c b/Modules/_tkinter.c
index aed42ac..a04d580 100644
--- a/Modules/_tkinter.c
+++ b/Modules/_tkinter.c
@@ -47,6 +47,10 @@
#define PyBool_FromLong PyInt_FromLong
#endif
+#define CHECK_SIZE(size, elemsize) \
+ ((size_t)(size) <= (size_t)INT_MAX && \
+ (size_t)(size) <= UINT_MAX / (size_t)(elemsize))
+
/* Starting with Tcl 8.4, many APIs offer const-correctness. Unfortunately,
making _tkinter correct for this API means to break earlier
versions. USE_COMPAT_CONST allows to make _tkinter work with both 8.4 and
@@ -378,7 +382,7 @@
char **argv = NULL;
int fvStore[ARGSZ];
int *fv = NULL;
- int argc = 0, fvc = 0, i;
+ Py_ssize_t argc = 0, fvc = 0, i;
char *res = NULL;
if (!(tmp = PyList_New(0)))
@@ -400,8 +404,12 @@
argc = PyTuple_Size(args);
if (argc > ARGSZ) {
- argv = (char **)ckalloc(argc * sizeof(char *));
- fv = (int *)ckalloc(argc * sizeof(int));
+ if (!CHECK_SIZE(argc, sizeof(char *))) {
+ PyErr_SetString(PyExc_OverflowError, "tuple is too long");
+ goto finally;
+ }
+ argv = (char **)ckalloc((size_t)argc * sizeof(char *));
+ fv = (int *)ckalloc((size_t)argc * sizeof(int));
if (argv == NULL || fv == NULL) {
PyErr_NoMemory();
goto finally;
@@ -983,12 +991,18 @@
else if (PyFloat_Check(value))
return Tcl_NewDoubleObj(PyFloat_AS_DOUBLE(value));
else if (PyTuple_Check(value)) {
- Tcl_Obj **argv = (Tcl_Obj**)
- ckalloc(PyTuple_Size(value)*sizeof(Tcl_Obj*));
- int i;
+ Tcl_Obj **argv;
+ Py_ssize_t size, i;
+
+ size = PyTuple_Size(value);
+ if (!CHECK_SIZE(size, sizeof(Tcl_Obj *))) {
+ PyErr_SetString(PyExc_OverflowError, "tuple is too long");
+ return NULL;
+ }
+ argv = (Tcl_Obj **) ckalloc(((size_t)size) * sizeof(Tcl_Obj *));
if(!argv)
return 0;
- for(i=0;i<PyTuple_Size(value);i++)
+ for (i = 0; i < size; i++)
argv[i] = AsObj(PyTuple_GetItem(value,i));
result = Tcl_NewListObj(PyTuple_Size(value), argv);
ckfree(FREECAST argv);
@@ -1003,7 +1017,12 @@
#if defined(Py_UNICODE_WIDE) && TCL_UTF_MAX == 3
Tcl_UniChar *outbuf = NULL;
Py_ssize_t i;
- size_t allocsize = ((size_t)size) * sizeof(Tcl_UniChar);
+ size_t allocsize;
+ if (!CHECK_SIZE(size, sizeof(Tcl_UniChar))) {
+ PyErr_SetString(PyExc_OverflowError, "string is too long");
+ return NULL;
+ }
+ allocsize = ((size_t)size) * sizeof(Tcl_UniChar);
if (allocsize >= size)
outbuf = (Tcl_UniChar*)ckalloc(allocsize);
/* Else overflow occurred, and we take the next exit */
@@ -1198,7 +1217,7 @@
Tkapp_CallArgs(PyObject *args, Tcl_Obj** objStore, int *pobjc)
{
Tcl_Obj **objv = objStore;
- int objc = 0, i;
+ Py_ssize_t objc = 0, i;
if (args == NULL)
/* do nothing */;
@@ -1213,7 +1232,11 @@
objc = PyTuple_Size(args);
if (objc > ARGSZ) {
- objv = (Tcl_Obj **)ckalloc(objc * sizeof(char *));
+ if (!CHECK_SIZE(objc, sizeof(Tcl_Obj *))) {
+ PyErr_SetString(PyExc_OverflowError, "tuple is too long");
+ return NULL;
+ }
+ objv = (Tcl_Obj **)ckalloc(((size_t)objc) * sizeof(Tcl_Obj *));
if (objv == NULL) {
PyErr_NoMemory();
objc = 0;