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

Commitae2a25b

Browse files
authored
[3.12] Check for valid tp_version_tag in specializer (gh-89811) (gh-114216)
1 parentffac6ac commitae2a25b

File tree

4 files changed

+197
-2
lines changed

4 files changed

+197
-2
lines changed

‎Lib/test/test_type_cache.py‎

Lines changed: 144 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
""" Tests for the internal type cache in CPython. """
22
importunittest
3+
importdis
34
fromtestimportsupport
45
fromtest.supportimportimport_helper
56
try:
@@ -8,8 +9,11 @@
89
_clear_type_cache=None
910

1011
# Skip this test if the _testcapi module isn't available.
11-
type_get_version=import_helper.import_module('_testcapi').type_get_version
12-
type_assign_version=import_helper.import_module('_testcapi').type_assign_version
12+
_testcapi=import_helper.import_module("_testcapi")
13+
type_get_version=_testcapi.type_get_version
14+
type_assign_specific_version_unsafe=_testcapi.type_assign_specific_version_unsafe
15+
type_assign_version=_testcapi.type_assign_version
16+
type_modified=_testcapi.type_modified
1317

1418

1519
@support.cpython_only
@@ -56,6 +60,144 @@ class C:
5660
self.assertNotEqual(type_get_version(C),0)
5761
self.assertNotEqual(type_get_version(C),c_ver)
5862

63+
deftest_type_assign_specific_version(self):
64+
"""meta-test for type_assign_specific_version_unsafe"""
65+
classC:
66+
pass
67+
68+
type_assign_version(C)
69+
orig_version=type_get_version(C)
70+
iforig_version==0:
71+
self.skipTest("Could not assign a valid type version")
72+
73+
type_modified(C)
74+
type_assign_specific_version_unsafe(C,orig_version+5)
75+
type_assign_version(C)# this should do nothing
76+
77+
new_version=type_get_version(C)
78+
self.assertEqual(new_version,orig_version+5)
79+
80+
_clear_type_cache()
81+
82+
83+
@support.cpython_only
84+
classTypeCacheWithSpecializationTests(unittest.TestCase):
85+
deftearDown(self):
86+
_clear_type_cache()
87+
88+
def_assign_valid_version_or_skip(self,type_):
89+
type_modified(type_)
90+
type_assign_version(type_)
91+
iftype_get_version(type_)==0:
92+
self.skipTest("Could not assign valid type version")
93+
94+
def_assign_and_check_version_0(self,user_type):
95+
type_modified(user_type)
96+
type_assign_specific_version_unsafe(user_type,0)
97+
self.assertEqual(type_get_version(user_type),0)
98+
99+
def_all_opnames(self,func):
100+
returnset(instr.opnameforinstrindis.Bytecode(func,adaptive=True))
101+
102+
def_check_specialization(self,func,arg,opname,*,should_specialize):
103+
for_inrange(100):
104+
func(arg)
105+
106+
ifshould_specialize:
107+
self.assertNotIn(opname,self._all_opnames(func))
108+
else:
109+
self.assertIn(opname,self._all_opnames(func))
110+
111+
deftest_class_load_attr_specialization_user_type(self):
112+
classA:
113+
deffoo(self):
114+
pass
115+
116+
self._assign_valid_version_or_skip(A)
117+
118+
defload_foo_1(type_):
119+
type_.foo
120+
121+
self._check_specialization(load_foo_1,A,"LOAD_ATTR",should_specialize=True)
122+
delload_foo_1
123+
124+
self._assign_and_check_version_0(A)
125+
126+
defload_foo_2(type_):
127+
returntype_.foo
128+
129+
self._check_specialization(load_foo_2,A,"LOAD_ATTR",should_specialize=False)
130+
131+
deftest_class_load_attr_specialization_static_type(self):
132+
self._assign_valid_version_or_skip(str)
133+
self._assign_valid_version_or_skip(bytes)
134+
135+
defget_capitalize_1(type_):
136+
returntype_.capitalize
137+
138+
self._check_specialization(get_capitalize_1,str,"LOAD_ATTR",should_specialize=True)
139+
self.assertEqual(get_capitalize_1(str)('hello'),'Hello')
140+
self.assertEqual(get_capitalize_1(bytes)(b'hello'),b'Hello')
141+
delget_capitalize_1
142+
143+
# Permanently overflow the static type version counter, and force str and bytes
144+
# to have tp_version_tag == 0
145+
for_inrange(2**16):
146+
type_modified(str)
147+
type_assign_version(str)
148+
type_modified(bytes)
149+
type_assign_version(bytes)
150+
151+
self.assertEqual(type_get_version(str),0)
152+
self.assertEqual(type_get_version(bytes),0)
153+
154+
defget_capitalize_2(type_):
155+
returntype_.capitalize
156+
157+
self._check_specialization(get_capitalize_2,str,"LOAD_ATTR",should_specialize=False)
158+
self.assertEqual(get_capitalize_2(str)('hello'),'Hello')
159+
self.assertEqual(get_capitalize_2(bytes)(b'hello'),b'Hello')
160+
161+
deftest_property_load_attr_specialization_user_type(self):
162+
classG:
163+
@property
164+
defx(self):
165+
return9
166+
167+
self._assign_valid_version_or_skip(G)
168+
169+
defload_x_1(instance):
170+
instance.x
171+
172+
self._check_specialization(load_x_1,G(),"LOAD_ATTR",should_specialize=True)
173+
delload_x_1
174+
175+
self._assign_and_check_version_0(G)
176+
177+
defload_x_2(instance):
178+
instance.x
179+
180+
self._check_specialization(load_x_2,G(),"LOAD_ATTR",should_specialize=False)
181+
182+
deftest_store_attr_specialization_user_type(self):
183+
classB:
184+
__slots__= ("bar",)
185+
186+
self._assign_valid_version_or_skip(B)
187+
188+
defstore_bar_1(type_):
189+
type_.bar=10
190+
191+
self._check_specialization(store_bar_1,B(),"STORE_ATTR",should_specialize=True)
192+
delstore_bar_1
193+
194+
self._assign_and_check_version_0(B)
195+
196+
defstore_bar_2(type_):
197+
type_.bar=10
198+
199+
self._check_specialization(store_bar_2,B(),"STORE_ATTR",should_specialize=False)
200+
59201

60202
if__name__=="__main__":
61203
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Check for a valid ``tp_version_tag`` before performing bytecode specializations that
2+
rely on this value being usable.

‎Modules/_testcapimodule.c‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2530,6 +2530,32 @@ type_get_version(PyObject *self, PyObject *type)
25302530
returnres;
25312531
}
25322532

2533+
staticPyObject*
2534+
type_modified(PyObject*self,PyObject*type)
2535+
{
2536+
if (!PyType_Check(type)) {
2537+
PyErr_SetString(PyExc_TypeError,"argument must be a type");
2538+
returnNULL;
2539+
}
2540+
PyType_Modified((PyTypeObject*)type);
2541+
Py_RETURN_NONE;
2542+
}
2543+
2544+
// Circumvents standard version assignment machinery - use with caution and only on
2545+
// short-lived heap types
2546+
staticPyObject*
2547+
type_assign_specific_version_unsafe(PyObject*self,PyObject*args)
2548+
{
2549+
PyTypeObject*type;
2550+
unsignedintversion;
2551+
if (!PyArg_ParseTuple(args,"Oi:type_assign_specific_version_unsafe",&type,&version)) {
2552+
returnNULL;
2553+
}
2554+
assert(!PyType_HasFeature(type,Py_TPFLAGS_IMMUTABLETYPE));
2555+
type->tp_version_tag=version;
2556+
type->tp_flags |=Py_TPFLAGS_VALID_VERSION_TAG;
2557+
Py_RETURN_NONE;
2558+
}
25332559

25342560
staticPyObject*
25352561
type_assign_version(PyObject*self,PyObject*type)
@@ -3357,6 +3383,9 @@ static PyMethodDef TestMethods[] = {
33573383
{"test_py_is_macros",test_py_is_macros,METH_NOARGS},
33583384
{"test_py_is_funcs",test_py_is_funcs,METH_NOARGS},
33593385
{"type_get_version",type_get_version,METH_O,PyDoc_STR("type->tp_version_tag")},
3386+
{"type_modified",type_modified,METH_O,PyDoc_STR("PyType_Modified")},
3387+
{"type_assign_specific_version_unsafe",type_assign_specific_version_unsafe,METH_VARARGS,
3388+
PyDoc_STR("forcefully assign type->tp_version_tag")},
33603389
{"type_assign_version",type_assign_version,METH_O,PyDoc_STR("PyUnstable_Type_AssignVersionTag")},
33613390
{"type_get_tp_bases",type_get_tp_bases,METH_O},
33623391
{"type_get_tp_mro",type_get_tp_mro,METH_O},

‎Python/specialize.c‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ _PyCode_Quicken(PyCodeObject *code)
464464
staticintfunction_kind(PyCodeObject*code);
465465
staticboolfunction_check_args(PyObject*o,intexpected_argcount,intopcode);
466466
staticuint32_tfunction_get_version(PyObject*o,intopcode);
467+
staticuint32_ttype_get_version(PyTypeObject*t,intopcode);
467468

468469
staticint
469470
specialize_module_load_attr(
@@ -746,6 +747,9 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
746747
PyObject*descr=NULL;
747748
DescriptorClassificationkind=analyze_descriptor(type,name,&descr,0);
748749
assert(descr!=NULL||kind==ABSENT||kind==GETSET_OVERRIDDEN);
750+
if (type_get_version(type,LOAD_ATTR)==0) {
751+
gotofail;
752+
}
749753
switch(kind) {
750754
caseOVERRIDING:
751755
SPECIALIZATION_FAIL(LOAD_ATTR,SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR);
@@ -917,6 +921,9 @@ _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
917921
}
918922
PyObject*descr;
919923
DescriptorClassificationkind=analyze_descriptor(type,name,&descr,1);
924+
if (type_get_version(type,STORE_ATTR)==0) {
925+
gotofail;
926+
}
920927
switch(kind) {
921928
caseOVERRIDING:
922929
SPECIALIZATION_FAIL(STORE_ATTR,SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR);
@@ -1043,6 +1050,9 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr,
10431050
PyObject*descr=NULL;
10441051
DescriptorClassificationkind=0;
10451052
kind=analyze_descriptor((PyTypeObject*)owner,name,&descr,0);
1053+
if (type_get_version((PyTypeObject*)owner,LOAD_ATTR)==0) {
1054+
return-1;
1055+
}
10461056
switch (kind) {
10471057
caseMETHOD:
10481058
caseNON_DESCRIPTOR:
@@ -1317,6 +1327,18 @@ function_get_version(PyObject *o, int opcode)
13171327
returnversion;
13181328
}
13191329

1330+
/* Returning 0 indicates a failure. */
1331+
staticuint32_t
1332+
type_get_version(PyTypeObject*t,intopcode)
1333+
{
1334+
uint32_tversion=t->tp_version_tag;
1335+
if (version==0) {
1336+
SPECIALIZATION_FAIL(opcode,SPEC_FAIL_OUT_OF_VERSIONS);
1337+
return0;
1338+
}
1339+
returnversion;
1340+
}
1341+
13201342
void
13211343
_Py_Specialize_BinarySubscr(
13221344
PyObject*container,PyObject*sub,_Py_CODEUNIT*instr)

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp