bpo-41295: Reimplement the Carlo Verre "hackcheck" (GH-21528)
Walk down the MRO backwards to find the type that originally defined the final `tp_setattro`, then make sure we are not jumping over intermediate C-level bases with the Python-level call.
Automerge-Triggered-By: @gvanrossum
(cherry picked from commit c53b310e59
)
Co-authored-by: scoder <stefan_ml@behnel.de>
This commit is contained in:
parent
668d321476
commit
38d930f2cc
|
@ -4315,6 +4315,42 @@ order (MRO) for bases """
|
||||||
else:
|
else:
|
||||||
self.fail("Carlo Verre __delattr__ succeeded!")
|
self.fail("Carlo Verre __delattr__ succeeded!")
|
||||||
|
|
||||||
|
def test_carloverre_multi_inherit_valid(self):
|
||||||
|
class A(type):
|
||||||
|
def __setattr__(cls, key, value):
|
||||||
|
type.__setattr__(cls, key, value)
|
||||||
|
|
||||||
|
class B:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class C(B, A):
|
||||||
|
pass
|
||||||
|
|
||||||
|
obj = C('D', (object,), {})
|
||||||
|
try:
|
||||||
|
obj.test = True
|
||||||
|
except TypeError:
|
||||||
|
self.fail("setattr through direct base types should be legal")
|
||||||
|
|
||||||
|
def test_carloverre_multi_inherit_invalid(self):
|
||||||
|
class A(type):
|
||||||
|
def __setattr__(cls, key, value):
|
||||||
|
object.__setattr__(cls, key, value) # this should fail!
|
||||||
|
|
||||||
|
class B:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class C(B, A):
|
||||||
|
pass
|
||||||
|
|
||||||
|
obj = C('D', (object,), {})
|
||||||
|
try:
|
||||||
|
obj.test = True
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.fail("setattr through indirect base types should be rejected")
|
||||||
|
|
||||||
def test_weakref_segfault(self):
|
def test_weakref_segfault(self):
|
||||||
# Testing weakref segfault...
|
# Testing weakref segfault...
|
||||||
# SF 742911
|
# SF 742911
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Resolve a regression in CPython 3.8.4 where defining "__setattr__" in a
|
||||||
|
multi-inheritance setup and calling up the hierarchy chain could fail
|
||||||
|
if builtins/extension types were involved in the base types.
|
|
@ -5822,14 +5822,29 @@ hackcheck(PyObject *self, setattrofunc func, const char *what)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
assert(PyTuple_Check(mro));
|
assert(PyTuple_Check(mro));
|
||||||
Py_ssize_t i, n;
|
|
||||||
n = PyTuple_GET_SIZE(mro);
|
/* Find the (base) type that defined the type's slot function. */
|
||||||
for (i = 0; i < n; i++) {
|
PyTypeObject *defining_type = type;
|
||||||
|
Py_ssize_t i;
|
||||||
|
for (i = PyTuple_GET_SIZE(mro) - 1; i >= 0; i--) {
|
||||||
PyTypeObject *base = (PyTypeObject*) PyTuple_GET_ITEM(mro, i);
|
PyTypeObject *base = (PyTypeObject*) PyTuple_GET_ITEM(mro, i);
|
||||||
if (base->tp_setattro == func) {
|
if (base->tp_setattro == slot_tp_setattro) {
|
||||||
/* 'func' is the earliest non-Python implementation in the MRO. */
|
/* Ignore Python classes:
|
||||||
|
they never define their own C-level setattro. */
|
||||||
|
}
|
||||||
|
else if (base->tp_setattro == type->tp_setattro) {
|
||||||
|
defining_type = base;
|
||||||
break;
|
break;
|
||||||
} else if (base->tp_setattro != slot_tp_setattro) {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reject calls that jump over intermediate C-level overrides. */
|
||||||
|
for (PyTypeObject *base = defining_type; base; base = base->tp_base) {
|
||||||
|
if (base->tp_setattro == func) {
|
||||||
|
/* 'func' is the right slot function to call. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (base->tp_setattro != slot_tp_setattro) {
|
||||||
/* 'base' is not a Python class and overrides 'func'.
|
/* 'base' is not a Python class and overrides 'func'.
|
||||||
Its tp_setattro should be called instead. */
|
Its tp_setattro should be called instead. */
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
@ -5839,8 +5854,6 @@ hackcheck(PyObject *self, setattrofunc func, const char *what)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Either 'func' is not in the mro (which should fail when checking 'self'),
|
|
||||||
or it's the right slot function to call. */
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue