Skip to content

Commit 8ff4809

Browse files
committed
py/objtype: Fix 32-bit crash when calling super().__init__() in metaclass.
When super().__init__() was called from within a custom metaclass __init__, the super_attr() function incorrectly mapped __init__ lookups to the make_new slot for all objects. For type objects, this caused type_make_new() to be invoked, which called mp_obj_new_type() again, creating a duplicate type object that overwrote the original's memory. On 32-bit architectures, this memory corruption manifested as corrupted slot_index fields, causing assertion failures when accessing locals_dict. The fix: - Add type.__init__() as a no-op that accepts standard metaclass arguments - In super_attr(), detect when super().__init__() is called on a type object (metaclass context) and return type.__init__() instead of mapping to make_new - Only map __init__ to make_new for regular instance initialization This preserves the existing behavior for regular instances while fixing metaclass super().__init__() calls to not recreate type objects. Fixes: #XXXXX Signed-off-by: Corona <[email protected]> Signed-off-by: Andrew Leech <[email protected]>
1 parent ca58524 commit 8ff4809

File tree

1 file changed

+35
-1
lines changed

1 file changed

+35
-1
lines changed

py/objtype.c

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,18 @@ static mp_obj_t type___new__(size_t n_args, const mp_obj_t *args) {
11521152
}
11531153
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(type___new___obj, 1, 4, type___new__);
11541154

1155+
// type.__init__(cls, name, bases, dict) - does nothing, exists for metaclass super() calls
1156+
static mp_obj_t type___init__(size_t n_args, const mp_obj_t *args) {
1157+
(void)args;
1158+
// Accept 1 or 4 arguments (1 = self, 4 = self, name, bases, dict)
1159+
// Do nothing - type initialization is handled by type.__new__()
1160+
if (n_args != 1 && n_args != 4) {
1161+
mp_raise_TypeError(MP_ERROR_TEXT("type.__init__() takes 1 or 4 arguments"));
1162+
}
1163+
return mp_const_none;
1164+
}
1165+
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(type___init___obj, 1, 4, type___init__);
1166+
11551167
static void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
11561168
mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in);
11571169
// Allow types with custom metaclasses that inherit from type
@@ -1205,6 +1217,13 @@ static void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
12051217
dest[0] = MP_OBJ_FROM_PTR(&type___new___obj);
12061218
return;
12071219
}
1220+
// Only provide type.__init__ when looking up on type itself
1221+
// This allows type.__init__ to be called directly but doesn't interfere with
1222+
// regular classes or metaclasses which should use their own __init__
1223+
if (attr == MP_QSTR___init__ && self == &mp_type_type) {
1224+
dest[0] = MP_OBJ_FROM_PTR(&type___init___obj);
1225+
return;
1226+
}
12081227
struct class_lookup_data lookup = {
12091228
.obj = (mp_obj_instance_t *)self,
12101229
.attr = attr,
@@ -1672,8 +1691,23 @@ static void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
16721691
};
16731692

16741693
// Allow a call super().__init__() to reach any native base classes.
1694+
// But don't do this if self->obj is a type (i.e., calling super() from within a metaclass),
1695+
// as that would incorrectly call type_make_new() which creates a new type.
16751696
if (attr == MP_QSTR___init__) {
1676-
lookup.slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(make_new);
1697+
// Check if self->obj is a type object by checking if its type is a subclass of 'type'
1698+
const mp_obj_type_t *obj_type = mp_obj_get_type(self->obj);
1699+
bool obj_is_type = mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(obj_type), MP_OBJ_FROM_PTR(&mp_type_type));
1700+
1701+
if (obj_is_type) {
1702+
// self->obj is a type, so super().__init__() is being called from a metaclass
1703+
// Return type.__init__ which is a no-op that accepts the right arguments
1704+
dest[0] = MP_OBJ_FROM_PTR(&type___init___obj);
1705+
dest[1] = self->obj; // bind to self->obj
1706+
return;
1707+
} else {
1708+
// Only map __init__ to make_new for regular instances, not for types
1709+
lookup.slot_offset = MP_OBJ_TYPE_OFFSETOF_SLOT(make_new);
1710+
}
16771711
}
16781712

16791713
if (!MP_OBJ_TYPE_HAS_SLOT(type, parent)) {

0 commit comments

Comments
 (0)