Backport r55080:

Fix for #1303614 and #1174712:
- __dict__ descriptor abuse for subclasses of built-in types
- subclassing from both ModuleType and another built-in types
This commit is contained in:
Guido van Rossum 2008-01-18 21:31:32 +00:00
parent d187381b81
commit 03706d2db0
4 changed files with 163 additions and 37 deletions

View File

@ -1,12 +0,0 @@
# http://python.org/sf/1174712
import types
class X(types.ModuleType, str):
"""Such a subclassing is incorrectly allowed --
see the SF bug report for explanations"""
if __name__ == '__main__':
X('name') # segfault: ModuleType.__init__() reads
# the dict at the wrong offset

View File

@ -1,19 +0,0 @@
# http://python.org/sf/1303614
class Y(object):
pass
class type_with_modifiable_dict(Y, type):
pass
class MyClass(object):
"""This class has its __dict__ attribute completely exposed:
user code can read, reassign and even delete it.
"""
__metaclass__ = type_with_modifiable_dict
if __name__ == '__main__':
del MyClass.__dict__ # if we set tp_dict to NULL,
print MyClass # doing anything with MyClass segfaults

View File

@ -3,6 +3,7 @@
from test.test_support import verify, vereq, verbose, TestFailed, TESTFN, get_original_stdout
from copy import deepcopy
import warnings
import types
warnings.filterwarnings("ignore",
r'complex divmod\(\), // and % are deprecated$',
@ -845,6 +846,16 @@ def pymods():
("getattr", "foo"),
("delattr", "foo")])
# http://python.org/sf/1174712
try:
class Module(types.ModuleType, str):
pass
except TypeError:
pass
else:
raise TestFailed("inheriting from ModuleType and str at the "
"same time should fail")
def multi():
if verbose: print "Testing multiple inheritance..."
class C(object):
@ -2815,8 +2826,73 @@ def setdict():
cant(a, [])
cant(a, 1)
del a.__dict__ # Deleting __dict__ is allowed
# Classes don't allow __dict__ assignment
cant(C, {})
class Base(object):
pass
def verify_dict_readonly(x):
"""
x has to be an instance of a class inheriting from Base.
"""
cant(x, {})
try:
del x.__dict__
except (AttributeError, TypeError):
pass
else:
raise TestFailed, "shouldn't allow del %r.__dict__" % x
dict_descr = Base.__dict__["__dict__"]
try:
dict_descr.__set__(x, {})
except (AttributeError, TypeError):
pass
else:
raise TestFailed, "dict_descr allowed access to %r's dict" % x
# Classes don't allow __dict__ assignment and have readonly dicts
class Meta1(type, Base):
pass
class Meta2(Base, type):
pass
class D(object):
__metaclass__ = Meta1
class E(object):
__metaclass__ = Meta2
for cls in C, D, E:
verify_dict_readonly(cls)
class_dict = cls.__dict__
try:
class_dict["spam"] = "eggs"
except TypeError:
pass
else:
raise TestFailed, "%r's __dict__ can be modified" % cls
# Modules also disallow __dict__ assignment
class Module1(types.ModuleType, Base):
pass
class Module2(Base, types.ModuleType):
pass
for ModuleType in Module1, Module2:
mod = ModuleType("spam")
verify_dict_readonly(mod)
mod.__dict__["spam"] = "eggs"
# Exception's __dict__ can be replaced, but not deleted
class Exception1(Exception, Base):
pass
class Exception2(Base, Exception):
pass
for ExceptionType in Exception, Exception1, Exception2:
e = ExceptionType()
e.__dict__ = {"a": 1}
vereq(e.a, 1)
try:
del e.__dict__
except (TypeError, AttributeError):
pass
else:
raise TestFaied, "%r's __dict__ can be deleted" % e
def pickles():
if verbose:

View File

@ -1422,10 +1422,12 @@ extra_ivars(PyTypeObject *type, PyTypeObject *base)
type->tp_itemsize != base->tp_itemsize;
}
if (type->tp_weaklistoffset && base->tp_weaklistoffset == 0 &&
type->tp_weaklistoffset + sizeof(PyObject *) == t_size)
type->tp_weaklistoffset + sizeof(PyObject *) == t_size &&
type->tp_flags & Py_TPFLAGS_HEAPTYPE)
t_size -= sizeof(PyObject *);
if (type->tp_dictoffset && base->tp_dictoffset == 0 &&
type->tp_dictoffset + sizeof(PyObject *) == t_size)
type->tp_dictoffset + sizeof(PyObject *) == t_size &&
type->tp_flags & Py_TPFLAGS_HEAPTYPE)
t_size -= sizeof(PyObject *);
return t_size != b_size;
@ -1451,12 +1453,73 @@ static int object_init(PyObject *, PyObject *, PyObject *);
static int update_slot(PyTypeObject *, PyObject *);
static void fixup_slot_dispatchers(PyTypeObject *);
/*
* Helpers for __dict__ descriptor. We don't want to expose the dicts
* inherited from various builtin types. The builtin base usually provides
* its own __dict__ descriptor, so we use that when we can.
*/
static PyTypeObject *
get_builtin_base_with_dict(PyTypeObject *type)
{
while (type->tp_base != NULL) {
if (type->tp_dictoffset != 0 &&
!(type->tp_flags & Py_TPFLAGS_HEAPTYPE))
return type;
type = type->tp_base;
}
return NULL;
}
static PyObject *
get_dict_descriptor(PyTypeObject *type)
{
static PyObject *dict_str;
PyObject *descr;
if (dict_str == NULL) {
dict_str = PyString_InternFromString("__dict__");
if (dict_str == NULL)
return NULL;
}
descr = _PyType_Lookup(type, dict_str);
if (descr == NULL || !PyDescr_IsData(descr))
return NULL;
return descr;
}
static void
raise_dict_descr_error(PyObject *obj)
{
PyErr_Format(PyExc_TypeError,
"this __dict__ descriptor does not support "
"'%.200s' objects", obj->ob_type->tp_name);
}
static PyObject *
subtype_dict(PyObject *obj, void *context)
{
PyObject **dictptr = _PyObject_GetDictPtr(obj);
PyObject **dictptr;
PyObject *dict;
PyTypeObject *base;
base = get_builtin_base_with_dict(obj->ob_type);
if (base != NULL) {
descrgetfunc func;
PyObject *descr = get_dict_descriptor(base);
if (descr == NULL) {
raise_dict_descr_error(obj);
return NULL;
}
func = descr->ob_type->tp_descr_get;
if (func == NULL) {
raise_dict_descr_error(obj);
return NULL;
}
return func(descr, obj, (PyObject *)(obj->ob_type));
}
dictptr = _PyObject_GetDictPtr(obj);
if (dictptr == NULL) {
PyErr_SetString(PyExc_AttributeError,
"This object has no __dict__");
@ -1472,9 +1535,27 @@ subtype_dict(PyObject *obj, void *context)
static int
subtype_setdict(PyObject *obj, PyObject *value, void *context)
{
PyObject **dictptr = _PyObject_GetDictPtr(obj);
PyObject **dictptr;
PyObject *dict;
PyTypeObject *base;
base = get_builtin_base_with_dict(obj->ob_type);
if (base != NULL) {
descrsetfunc func;
PyObject *descr = get_dict_descriptor(base);
if (descr == NULL) {
raise_dict_descr_error(obj);
return -1;
}
func = descr->ob_type->tp_descr_set;
if (func == NULL) {
raise_dict_descr_error(obj);
return -1;
}
return func(descr, obj, value);
}
dictptr = _PyObject_GetDictPtr(obj);
if (dictptr == NULL) {
PyErr_SetString(PyExc_AttributeError,
"This object has no __dict__");