Skip to content

Commit 5c3dd1c

Browse files
committed
gh-130851: Only intern constants of types generated by compiler
The free threading build interns and immortalizes most constants generated by the bytecode compiler. However, users can construct their own code objects with arbitrary constants. Don't intern or immortalize these objects if they are not a type that we know how to handle. This fixes a refleak failure in the recently added `test_code.test_unusual_constants` test. It also fixes a potential crash that could happen when we try to destory a immortalized object on interpreter shutdown.
1 parent 02de9cb commit 5c3dd1c

File tree

1 file changed

+40
-2
lines changed

1 file changed

+40
-2
lines changed

Objects/codeobject.c

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,43 @@ should_intern_string(PyObject *o)
135135

136136
#ifdef Py_GIL_DISABLED
137137
static PyObject *intern_one_constant(PyObject *op);
138+
139+
// gh-130851: In the free threading build, we intern and immortalize most
140+
// constants, except code objects. However, users can generate code objects
141+
// with arbitrary co_consts. We don't want to immortalize or intern unexpected
142+
// constants or tuples/sets containing unexpected constants.
143+
static int
144+
should_immortalize_constant(PyObject *v)
145+
{
146+
if (PyTuple_CheckExact(v)) {
147+
for (Py_ssize_t i = PyTuple_GET_SIZE(v); --i >= 0; ) {
148+
if (!should_immortalize_constant(PyTuple_GET_ITEM(v, i))) {
149+
return 0;
150+
}
151+
}
152+
return 1;
153+
}
154+
else if (PyFrozenSet_CheckExact(v)) {
155+
PyObject *item;
156+
Py_hash_t hash;
157+
Py_ssize_t pos = 0;
158+
while (_PySet_NextEntry(v, &pos, &item, &hash)) {
159+
if (!should_immortalize_constant(item)) {
160+
return 0;
161+
}
162+
}
163+
return 1;
164+
}
165+
else if (PySlice_Check(v)) {
166+
PySliceObject *slice = (PySliceObject *)v;
167+
return (should_immortalize_constant(slice->start) &&
168+
should_immortalize_constant(slice->stop) &&
169+
should_immortalize_constant(slice->step));
170+
}
171+
return (PyUnicode_CheckExact(v) || PyLong_CheckExact(v) ||
172+
PyFloat_CheckExact(v) || PyComplex_Check(v) ||
173+
PyBytes_CheckExact(v) || _Py_IsImmortal(v));
174+
}
138175
#endif
139176

140177
static int
@@ -241,8 +278,9 @@ intern_constants(PyObject *tuple, int *modified)
241278

242279
// Intern non-string constants in the free-threaded build
243280
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
244-
if (!_Py_IsImmortal(v) && !PyCode_Check(v) &&
245-
!PyUnicode_CheckExact(v) && !tstate->suppress_co_const_immortalization)
281+
if (!_Py_IsImmortal(v) && !PyUnicode_CheckExact(v) &&
282+
should_immortalize_constant(v) &&
283+
!tstate->suppress_co_const_immortalization)
246284
{
247285
PyObject *interned = intern_one_constant(v);
248286
if (interned == NULL) {

0 commit comments

Comments
 (0)