Skip to content

Commit 9ca668f

Browse files
laurensvalkdpgeorge
authored andcommitted
py/objtype: Avoid crash on calling members of uninitialized native type.
When subclassing a native type, calling native members in `__init__` before `super().__init__()` has been called could cause a crash. In this situation, `self` in `mp_convert_member_lookup` is the `native_base_init_wrapper_obj`. The check added in this commit ensures that an `AttributeError` is raised before this happens, which is consistent with other failed lookups. Also fix a typo in a related comment. Signed-off-by: Laurens Valk <[email protected]>
1 parent 19b1333 commit 9ca668f

File tree

3 files changed

+49
-1
lines changed

3 files changed

+49
-1
lines changed

py/objtype.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ static int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t
8282
}
8383
}
8484

85-
// This wrapper function is allows a subclass of a native type to call the
85+
// This wrapper function allows a subclass of a native type to call the
8686
// __init__() method (corresponding to type->make_new) of the native type.
8787
static mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *args) {
8888
mp_obj_instance_t *self = MP_OBJ_TO_PTR(args[0]);
@@ -170,6 +170,12 @@ static void mp_obj_class_lookup(struct class_lookup_data *lookup, const mp_obj_t
170170
if (obj != NULL && mp_obj_is_native_type(type) && type != &mp_type_object /* object is not a real type */) {
171171
// If we're dealing with native base class, then it applies to native sub-object
172172
obj_obj = obj->subobj[0];
173+
#if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG
174+
if (obj_obj == MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj)) {
175+
// But we shouldn't attempt lookups on object that is not yet instantiated.
176+
mp_raise_msg(&mp_type_AttributeError, MP_ERROR_TEXT("call super().__init__() first"));
177+
}
178+
#endif // MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG
173179
} else {
174180
obj_obj = MP_OBJ_FROM_PTR(obj);
175181
}

tests/misc/cexample_subclass.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# test subclassing custom native class
2+
3+
try:
4+
from cexample import AdvancedTimer
5+
except ImportError:
6+
print("SKIP")
7+
raise SystemExit
8+
9+
10+
class SubTimer(AdvancedTimer):
11+
def __init__(self):
12+
# At this point, self does not yet represent a AdvancedTimer instance.
13+
print(self)
14+
15+
# So lookups via type.attr handler will fail.
16+
try:
17+
self.seconds
18+
except AttributeError:
19+
print("AttributeError")
20+
21+
# Also applies to builtin methods.
22+
try:
23+
self.time()
24+
except AttributeError:
25+
print("AttributeError")
26+
27+
# Initialize base class.
28+
super().__init__(self)
29+
30+
# Now you can access methods and attributes normally.
31+
self.time()
32+
print(self.seconds)
33+
self.seconds = 123
34+
print(self.seconds)
35+
36+
37+
watch = SubTimer()

tests/misc/cexample_subclass.py.exp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<function>
2+
AttributeError
3+
AttributeError
4+
0
5+
123

0 commit comments

Comments
 (0)