mirror of
https://github.com/python/cpython.git
synced 2026-01-03 05:54:03 +00:00
[3.13] gh-143310: fix crash in Tcl object conversion with concurrent mutations (GH-143321) (#143344)
gh-143310: fix crash in Tcl object conversion with concurrent mutations (GH-143321)
(cherry picked from commit 9712dc1d9e)
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
parent
afa24d8324
commit
b40bf5ffc8
3 changed files with 67 additions and 21 deletions
|
|
@ -40,6 +40,9 @@ def setUp(self):
|
|||
self.interp = Tcl()
|
||||
self.wantobjects = self.interp.tk.wantobjects()
|
||||
|
||||
def passValue(self, value):
|
||||
return self.interp.call('set', '_', value)
|
||||
|
||||
def testEval(self):
|
||||
tcl = self.interp
|
||||
tcl.eval('set a 1')
|
||||
|
|
@ -490,8 +493,7 @@ def test_expr_bignum(self):
|
|||
self.assertIsInstance(result, str)
|
||||
|
||||
def test_passing_values(self):
|
||||
def passValue(value):
|
||||
return self.interp.call('set', '_', value)
|
||||
passValue = self.passValue
|
||||
|
||||
self.assertEqual(passValue(True), True if self.wantobjects else '1')
|
||||
self.assertEqual(passValue(False), False if self.wantobjects else '0')
|
||||
|
|
@ -537,6 +539,24 @@ def passValue(value):
|
|||
self.assertEqual(passValue(['a', ['b', 'c']]),
|
||||
('a', ('b', 'c')) if self.wantobjects else 'a {b c}')
|
||||
|
||||
def test_set_object_concurrent_mutation_in_sequence_conversion(self):
|
||||
# Prevent SIGSEV when the object to convert is concurrently mutated.
|
||||
# See: https://github.com/python/cpython/issues/143310.
|
||||
|
||||
string = "value"
|
||||
|
||||
class Value:
|
||||
def __str__(self):
|
||||
values.clear()
|
||||
return string
|
||||
|
||||
class List(list):
|
||||
pass
|
||||
|
||||
expect = (string, "pad") if self.wantobjects else f"{string} pad"
|
||||
self.assertEqual(self.passValue(values := [Value(), "pad"]), expect)
|
||||
self.assertEqual(self.passValue(values := List([Value(), "pad"])), expect)
|
||||
|
||||
def test_user_command(self):
|
||||
result = None
|
||||
def testfunc(arg):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
:mod:`tkinter`: fix a crash when a Python :class:`list` is mutated during
|
||||
the conversion to a Tcl object (e.g., when setting a Tcl variable).
|
||||
Patch by Bénédikt Tran.
|
||||
|
|
@ -946,6 +946,40 @@ asBignumObj(PyObject *value)
|
|||
return result;
|
||||
}
|
||||
|
||||
static Tcl_Obj* AsObj(PyObject *value);
|
||||
|
||||
static Tcl_Obj*
|
||||
TupleAsObj(PyObject *value, int wrapped)
|
||||
{
|
||||
Tcl_Obj *result = NULL;
|
||||
Py_ssize_t size = PyTuple_GET_SIZE(value);
|
||||
if (size == 0) {
|
||||
return Tcl_NewListObj(0, NULL);
|
||||
}
|
||||
if (!CHECK_SIZE(size, sizeof(Tcl_Obj *))) {
|
||||
PyErr_SetString(PyExc_OverflowError,
|
||||
wrapped ? "list is too long" : "tuple is too long");
|
||||
return NULL;
|
||||
}
|
||||
Tcl_Obj **argv = (Tcl_Obj **)PyMem_Malloc(((size_t)size) * sizeof(Tcl_Obj *));
|
||||
if (argv == NULL) {
|
||||
PyErr_NoMemory();
|
||||
return NULL;
|
||||
}
|
||||
for (Py_ssize_t i = 0; i < size; i++) {
|
||||
Tcl_Obj *item = AsObj(PyTuple_GET_ITEM(value, i));
|
||||
if (item == NULL) {
|
||||
goto exit;
|
||||
}
|
||||
argv[i] = item;
|
||||
}
|
||||
result = Tcl_NewListObj((int)size, argv);
|
||||
|
||||
exit:
|
||||
PyMem_Free(argv);
|
||||
return result;
|
||||
}
|
||||
|
||||
static Tcl_Obj*
|
||||
AsObj(PyObject *value)
|
||||
{
|
||||
|
|
@ -992,28 +1026,17 @@ AsObj(PyObject *value)
|
|||
if (PyFloat_Check(value))
|
||||
return Tcl_NewDoubleObj(PyFloat_AS_DOUBLE(value));
|
||||
|
||||
if (PyTuple_Check(value) || PyList_Check(value)) {
|
||||
Tcl_Obj **argv;
|
||||
Py_ssize_t size, i;
|
||||
if (PyTuple_Check(value)) {
|
||||
return TupleAsObj(value, false);
|
||||
}
|
||||
|
||||
size = PySequence_Fast_GET_SIZE(value);
|
||||
if (size == 0)
|
||||
return Tcl_NewListObj(0, NULL);
|
||||
if (!CHECK_SIZE(size, sizeof(Tcl_Obj *))) {
|
||||
PyErr_SetString(PyExc_OverflowError,
|
||||
PyTuple_Check(value) ? "tuple is too long" :
|
||||
"list is too long");
|
||||
if (PyList_Check(value)) {
|
||||
PyObject *value_as_tuple = PyList_AsTuple(value);
|
||||
if (value_as_tuple == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
argv = (Tcl_Obj **) PyMem_Malloc(((size_t)size) * sizeof(Tcl_Obj *));
|
||||
if (!argv) {
|
||||
PyErr_NoMemory();
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < size; i++)
|
||||
argv[i] = AsObj(PySequence_Fast_GET_ITEM(value,i));
|
||||
result = Tcl_NewListObj((int)size, argv);
|
||||
PyMem_Free(argv);
|
||||
result = TupleAsObj(value_as_tuple, true);
|
||||
Py_DECREF(value_as_tuple);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue