mirror of https://github.com/python/cpython
- __dict__ descriptor abuse for subclasses of built-in types - subclassing from both ModuleType and another built-in types Thanks zseil for the patch.
This commit is contained in:
parent
d83eb316dc
commit
9790a27065
|
@ -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
|
|
|
@ -1,20 +0,0 @@
|
||||||
|
|
||||||
# http://python.org/sf/1303614
|
|
||||||
|
|
||||||
class Y(object):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class type_with_modifiable_dict(type, Y):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class MyClass(object):
|
|
||||||
"""This class has its __dict__ attribute indirectly
|
|
||||||
exposed via the __dict__ getter/setter of Y.
|
|
||||||
"""
|
|
||||||
__metaclass__ = type_with_modifiable_dict
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
dictattr = Y.__dict__['__dict__']
|
|
||||||
dictattr.__delete__(MyClass) # if we set tp_dict to NULL,
|
|
||||||
print MyClass # doing anything with MyClass segfaults
|
|
|
@ -3,6 +3,7 @@
|
||||||
from test.test_support import verify, vereq, verbose, TestFailed, TESTFN, get_original_stdout
|
from test.test_support import verify, vereq, verbose, TestFailed, TESTFN, get_original_stdout
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
import warnings
|
import warnings
|
||||||
|
import types
|
||||||
|
|
||||||
warnings.filterwarnings("ignore",
|
warnings.filterwarnings("ignore",
|
||||||
r'complex divmod\(\), // and % are deprecated$',
|
r'complex divmod\(\), // and % are deprecated$',
|
||||||
|
@ -861,6 +862,16 @@ def pymods():
|
||||||
("getattr", "foo"),
|
("getattr", "foo"),
|
||||||
("delattr", "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():
|
def multi():
|
||||||
if verbose: print "Testing multiple inheritance..."
|
if verbose: print "Testing multiple inheritance..."
|
||||||
class C(object):
|
class C(object):
|
||||||
|
@ -2907,8 +2918,73 @@ def setdict():
|
||||||
cant(a, [])
|
cant(a, [])
|
||||||
cant(a, 1)
|
cant(a, 1)
|
||||||
del a.__dict__ # Deleting __dict__ is allowed
|
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():
|
def pickles():
|
||||||
if verbose:
|
if verbose:
|
||||||
|
|
10
Misc/NEWS
10
Misc/NEWS
|
@ -12,6 +12,9 @@ What's New in Python 2.6 alpha 1?
|
||||||
Core and builtins
|
Core and builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Bug #1303614: don't expose object's __dict__ when the dict is
|
||||||
|
inherited from a builtin base.
|
||||||
|
|
||||||
- When __slots__ are set to a unicode string, make it work the same as
|
- When __slots__ are set to a unicode string, make it work the same as
|
||||||
setting a plain string, ie don't expand to single letter identifiers.
|
setting a plain string, ie don't expand to single letter identifiers.
|
||||||
|
|
||||||
|
@ -199,13 +202,6 @@ Core and builtins
|
||||||
|
|
||||||
- Bug #1664966: Fix crash in exec if Unicode filename can't be decoded.
|
- Bug #1664966: Fix crash in exec if Unicode filename can't be decoded.
|
||||||
|
|
||||||
- Add new requirements for metaclasses. 1) If type or a subclass of type
|
|
||||||
occurs in __bases__, it must occur before any non-type bases, e.g.
|
|
||||||
before regular classes. 2) If you assign to __bases__, you may not
|
|
||||||
change the metaclass. Many more illegal assignments to __bases__
|
|
||||||
are now checked and raise TypeErrors. This changed fixed at least
|
|
||||||
one known crash.
|
|
||||||
|
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
@ -1423,10 +1423,12 @@ extra_ivars(PyTypeObject *type, PyTypeObject *base)
|
||||||
type->tp_itemsize != base->tp_itemsize;
|
type->tp_itemsize != base->tp_itemsize;
|
||||||
}
|
}
|
||||||
if (type->tp_weaklistoffset && base->tp_weaklistoffset == 0 &&
|
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 *);
|
t_size -= sizeof(PyObject *);
|
||||||
if (type->tp_dictoffset && base->tp_dictoffset == 0 &&
|
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 *);
|
t_size -= sizeof(PyObject *);
|
||||||
|
|
||||||
return t_size != b_size;
|
return t_size != b_size;
|
||||||
|
@ -1452,12 +1454,73 @@ static int object_init(PyObject *, PyObject *, PyObject *);
|
||||||
static int update_slot(PyTypeObject *, PyObject *);
|
static int update_slot(PyTypeObject *, PyObject *);
|
||||||
static void fixup_slot_dispatchers(PyTypeObject *);
|
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 *
|
static PyObject *
|
||||||
subtype_dict(PyObject *obj, void *context)
|
subtype_dict(PyObject *obj, void *context)
|
||||||
{
|
{
|
||||||
PyObject **dictptr = _PyObject_GetDictPtr(obj);
|
PyObject **dictptr;
|
||||||
PyObject *dict;
|
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) {
|
if (dictptr == NULL) {
|
||||||
PyErr_SetString(PyExc_AttributeError,
|
PyErr_SetString(PyExc_AttributeError,
|
||||||
"This object has no __dict__");
|
"This object has no __dict__");
|
||||||
|
@ -1473,9 +1536,27 @@ subtype_dict(PyObject *obj, void *context)
|
||||||
static int
|
static int
|
||||||
subtype_setdict(PyObject *obj, PyObject *value, void *context)
|
subtype_setdict(PyObject *obj, PyObject *value, void *context)
|
||||||
{
|
{
|
||||||
PyObject **dictptr = _PyObject_GetDictPtr(obj);
|
PyObject **dictptr;
|
||||||
PyObject *dict;
|
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) {
|
if (dictptr == NULL) {
|
||||||
PyErr_SetString(PyExc_AttributeError,
|
PyErr_SetString(PyExc_AttributeError,
|
||||||
"This object has no __dict__");
|
"This object has no __dict__");
|
||||||
|
|
Loading…
Reference in New Issue