Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1469393

Browse files
gh-104614: Make Sure ob_type is Always Set Correctly by PyType_Ready() (gh-105122)
When I added the relevant condition to type_ready_set_bases() ingh-103912, I had missed that the function also sets tp_base and ob_type (if necessary). That led to problems for third-party static types.We fix that here, by making those extra operations distinct and by adjusting the condition to be more specific.
1 parent3698fda commit1469393

File tree

4 files changed

+128
-17
lines changed

4 files changed

+128
-17
lines changed

‎Lib/test/test_capi/test_misc.py‎

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Run the _testcapi module tests (tests for the Python/C API): by defn,
22
# these are all functions _testcapi exports whose name begins with 'test_'.
33

4-
fromcollectionsimportOrderedDict
54
import_thread
5+
fromcollectionsimportOrderedDict
6+
importcontextlib
67
importimportlib.machinery
78
importimportlib.util
89
importos
@@ -1626,6 +1627,41 @@ def test_tp_mro_is_set(self):
16261627
self.assertIsNot(mro,None)
16271628

16281629

1630+
classTestStaticTypes(unittest.TestCase):
1631+
1632+
_has_run=False
1633+
1634+
@classmethod
1635+
defsetUpClass(cls):
1636+
# The tests here don't play nice with our approach to refleak
1637+
# detection, so we bail out in that case.
1638+
ifcls._has_run:
1639+
raiseunittest.SkipTest('these tests do not support re-running')
1640+
cls._has_run=True
1641+
1642+
@contextlib.contextmanager
1643+
defbasic_static_type(self,*args):
1644+
cls=_testcapi.get_basic_static_type(*args)
1645+
yieldcls
1646+
1647+
deftest_pytype_ready_always_sets_tp_type(self):
1648+
# The point of this test is to prevent something like
1649+
# https://github.com/python/cpython/issues/104614
1650+
# from happening again.
1651+
1652+
# First check when tp_base/tp_bases is *not* set before PyType_Ready().
1653+
withself.basic_static_type()ascls:
1654+
self.assertIs(cls.__base__,object);
1655+
self.assertEqual(cls.__bases__, (object,));
1656+
self.assertIs(type(cls),type(object));
1657+
1658+
# Then check when we *do* set tp_base/tp_bases first.
1659+
withself.basic_static_type(object)ascls:
1660+
self.assertIs(cls.__base__,object);
1661+
self.assertEqual(cls.__bases__, (object,));
1662+
self.assertIs(type(cls),type(object));
1663+
1664+
16291665
classTestThreadState(unittest.TestCase):
16301666

16311667
@threading_helper.reap_threads

‎Modules/_testcapimodule.c‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2640,6 +2640,50 @@ type_get_tp_mro(PyObject *self, PyObject *type)
26402640
}
26412641

26422642

2643+
/* We only use 2 in test_capi/test_misc.py. */
2644+
#defineNUM_BASIC_STATIC_TYPES 2
2645+
staticPyTypeObjectBasicStaticTypes[NUM_BASIC_STATIC_TYPES]= {
2646+
#defineINIT_BASIC_STATIC_TYPE \
2647+
{ \
2648+
PyVarObject_HEAD_INIT(NULL,0) \
2649+
.tp_name="BasicStaticType", \
2650+
.tp_basicsize=sizeof(PyObject), \
2651+
}
2652+
INIT_BASIC_STATIC_TYPE,
2653+
INIT_BASIC_STATIC_TYPE,
2654+
#undefINIT_BASIC_STATIC_TYPE
2655+
};
2656+
staticintnum_basic_static_types_used=0;
2657+
2658+
staticPyObject*
2659+
get_basic_static_type(PyObject*self,PyObject*args)
2660+
{
2661+
PyObject*base=NULL;
2662+
if (!PyArg_ParseTuple(args,"|O",&base)) {
2663+
returnNULL;
2664+
}
2665+
assert(base==NULL||PyType_Check(base));
2666+
2667+
if(num_basic_static_types_used >=NUM_BASIC_STATIC_TYPES) {
2668+
PyErr_SetString(PyExc_RuntimeError,"no more available basic static types");
2669+
returnNULL;
2670+
}
2671+
PyTypeObject*cls=&BasicStaticTypes[num_basic_static_types_used++];
2672+
2673+
if (base!=NULL) {
2674+
cls->tp_base= (PyTypeObject*)Py_NewRef(base);
2675+
cls->tp_bases=Py_BuildValue("(O)",base);
2676+
if (cls->tp_bases==NULL) {
2677+
returnNULL;
2678+
}
2679+
}
2680+
if (PyType_Ready(cls)<0) {
2681+
returnNULL;
2682+
}
2683+
return (PyObject*)cls;
2684+
}
2685+
2686+
26432687
// Test PyThreadState C API
26442688
staticPyObject*
26452689
test_tstate_capi(PyObject*self,PyObject*Py_UNUSED(args))
@@ -3395,6 +3439,7 @@ static PyMethodDef TestMethods[] = {
33953439
{"type_assign_version",type_assign_version,METH_O,PyDoc_STR("PyUnstable_Type_AssignVersionTag")},
33963440
{"type_get_tp_bases",type_get_tp_bases,METH_O},
33973441
{"type_get_tp_mro",type_get_tp_mro,METH_O},
3442+
{"get_basic_static_type",get_basic_static_type,METH_VARARGS,NULL},
33983443
{"test_tstate_capi",test_tstate_capi,METH_NOARGS,NULL},
33993444
{"frame_getlocals",frame_getlocals,METH_O,NULL},
34003445
{"frame_getglobals",frame_getglobals,METH_O,NULL},

‎Objects/typeobject.c‎

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -306,11 +306,14 @@ clear_tp_bases(PyTypeObject *self)
306306
{
307307
if (self->tp_flags&_Py_TPFLAGS_STATIC_BUILTIN) {
308308
if (_Py_IsMainInterpreter(_PyInterpreterState_GET())) {
309-
if (self->tp_bases!=NULL
310-
&&PyTuple_GET_SIZE(self->tp_bases)>0)
311-
{
312-
assert(_Py_IsImmortal(self->tp_bases));
313-
_Py_ClearImmortal(self->tp_bases);
309+
if (self->tp_bases!=NULL) {
310+
if (PyTuple_GET_SIZE(self->tp_bases)==0) {
311+
Py_CLEAR(self->tp_bases);
312+
}
313+
else {
314+
assert(_Py_IsImmortal(self->tp_bases));
315+
_Py_ClearImmortal(self->tp_bases);
316+
}
314317
}
315318
}
316319
return;
@@ -352,11 +355,14 @@ clear_tp_mro(PyTypeObject *self)
352355
{
353356
if (self->tp_flags&_Py_TPFLAGS_STATIC_BUILTIN) {
354357
if (_Py_IsMainInterpreter(_PyInterpreterState_GET())) {
355-
if (self->tp_mro!=NULL
356-
&&PyTuple_GET_SIZE(self->tp_mro)>0)
357-
{
358-
assert(_Py_IsImmortal(self->tp_mro));
359-
_Py_ClearImmortal(self->tp_mro);
358+
if (self->tp_mro!=NULL) {
359+
if (PyTuple_GET_SIZE(self->tp_mro)==0) {
360+
Py_CLEAR(self->tp_mro);
361+
}
362+
else {
363+
assert(_Py_IsImmortal(self->tp_mro));
364+
_Py_ClearImmortal(self->tp_mro);
365+
}
360366
}
361367
}
362368
return;
@@ -6996,12 +7002,8 @@ type_ready_pre_checks(PyTypeObject *type)
69967002

69977003

69987004
staticint
6999-
type_ready_set_bases(PyTypeObject*type)
7005+
type_ready_set_base(PyTypeObject*type)
70007006
{
7001-
if (lookup_tp_bases(type)!=NULL) {
7002-
return0;
7003-
}
7004-
70057007
/* Initialize tp_base (defaults to BaseObject unless that's us) */
70067008
PyTypeObject*base=type->tp_base;
70077009
if (base==NULL&&type!=&PyBaseObject_Type) {
@@ -7025,18 +7027,38 @@ type_ready_set_bases(PyTypeObject *type)
70257027
}
70267028
}
70277029

7030+
return0;
7031+
}
7032+
7033+
staticint
7034+
type_ready_set_type(PyTypeObject*type)
7035+
{
70287036
/* Initialize ob_type if NULL. This means extensions that want to be
70297037
compilable separately on Windows can call PyType_Ready() instead of
70307038
initializing the ob_type field of their type objects. */
70317039
/* The test for base != NULL is really unnecessary, since base is only
70327040
NULL when type is &PyBaseObject_Type, and we know its ob_type is
70337041
not NULL (it's initialized to &PyType_Type). But coverity doesn't
70347042
know that. */
7043+
PyTypeObject*base=type->tp_base;
70357044
if (Py_IS_TYPE(type,NULL)&&base!=NULL) {
70367045
Py_SET_TYPE(type,Py_TYPE(base));
70377046
}
70387047

7039-
/* Initialize tp_bases */
7048+
return0;
7049+
}
7050+
7051+
staticint
7052+
type_ready_set_bases(PyTypeObject*type)
7053+
{
7054+
if (type->tp_flags&_Py_TPFLAGS_STATIC_BUILTIN) {
7055+
if (!_Py_IsMainInterpreter(_PyInterpreterState_GET())) {
7056+
assert(lookup_tp_bases(type)!=NULL);
7057+
return0;
7058+
}
7059+
assert(lookup_tp_bases(type)==NULL);
7060+
}
7061+
70407062
PyObject*bases=lookup_tp_bases(type);
70417063
if (bases==NULL) {
70427064
PyTypeObject*base=type->tp_base;
@@ -7446,6 +7468,12 @@ type_ready(PyTypeObject *type, int rerunbuiltin)
74467468
if (type_ready_set_dict(type)<0) {
74477469
gotoerror;
74487470
}
7471+
if (type_ready_set_base(type)<0) {
7472+
gotoerror;
7473+
}
7474+
if (type_ready_set_type(type)<0) {
7475+
gotoerror;
7476+
}
74497477
if (type_ready_set_bases(type)<0) {
74507478
gotoerror;
74517479
}

‎Tools/c-analyzer/cpython/ignored.tsv‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,8 @@ Modules/_testcapi/watchers.c-num_code_object_destroyed_events-
420420
Modules/_testcapi/watchers.c-pyfunc_watchers-
421421
Modules/_testcapi/watchers.c-func_watcher_ids-
422422
Modules/_testcapi/watchers.c-func_watcher_callbacks-
423+
Modules/_testcapimodule.c-BasicStaticTypes-
424+
Modules/_testcapimodule.c-num_basic_static_types_used-
423425
Modules/_testcapimodule.c-ContainerNoGC_members-
424426
Modules/_testcapimodule.c-ContainerNoGC_type-
425427
Modules/_testcapimodule.c-FmData-

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp