summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLaurens Valk <laurens@pybricks.com>2022-11-21 21:35:26 +0100
committerDamien George <damien@micropython.org>2024-07-25 12:01:43 +1000
commit9ca668f881865958cbcc0e6341849a6133c4c7b4 (patch)
tree5bc69513c0fa68868aa7f6dedb69a542363eb8f1
parent19b1333cb1376ef60376a07e8e76a41854014840 (diff)
downloadmicropython-9ca668f881865958cbcc0e6341849a6133c4c7b4.tar.gz
micropython-9ca668f881865958cbcc0e6341849a6133c4c7b4.zip
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 <laurens@pybricks.com>
-rw-r--r--py/objtype.c8
-rw-r--r--tests/misc/cexample_subclass.py37
-rw-r--r--tests/misc/cexample_subclass.py.exp5
3 files changed, 49 insertions, 1 deletions
diff --git a/py/objtype.c b/py/objtype.c
index b6d600e943..f7a65a6ca5 100644
--- a/py/objtype.c
+++ b/py/objtype.c
@@ -82,7 +82,7 @@ static int instance_count_native_bases(const mp_obj_type_t *type, const mp_obj_t
}
}
-// This wrapper function is allows a subclass of a native type to call the
+// This wrapper function allows a subclass of a native type to call the
// __init__() method (corresponding to type->make_new) of the native type.
static mp_obj_t native_base_init_wrapper(size_t n_args, const mp_obj_t *args) {
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
if (obj != NULL && mp_obj_is_native_type(type) && type != &mp_type_object /* object is not a real type */) {
// If we're dealing with native base class, then it applies to native sub-object
obj_obj = obj->subobj[0];
+ #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG
+ if (obj_obj == MP_OBJ_FROM_PTR(&native_base_init_wrapper_obj)) {
+ // But we shouldn't attempt lookups on object that is not yet instantiated.
+ mp_raise_msg(&mp_type_AttributeError, MP_ERROR_TEXT("call super().__init__() first"));
+ }
+ #endif // MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG
} else {
obj_obj = MP_OBJ_FROM_PTR(obj);
}
diff --git a/tests/misc/cexample_subclass.py b/tests/misc/cexample_subclass.py
new file mode 100644
index 0000000000..9f52a2c737
--- /dev/null
+++ b/tests/misc/cexample_subclass.py
@@ -0,0 +1,37 @@
+# test subclassing custom native class
+
+try:
+ from cexample import AdvancedTimer
+except ImportError:
+ print("SKIP")
+ raise SystemExit
+
+
+class SubTimer(AdvancedTimer):
+ def __init__(self):
+ # At this point, self does not yet represent a AdvancedTimer instance.
+ print(self)
+
+ # So lookups via type.attr handler will fail.
+ try:
+ self.seconds
+ except AttributeError:
+ print("AttributeError")
+
+ # Also applies to builtin methods.
+ try:
+ self.time()
+ except AttributeError:
+ print("AttributeError")
+
+ # Initialize base class.
+ super().__init__(self)
+
+ # Now you can access methods and attributes normally.
+ self.time()
+ print(self.seconds)
+ self.seconds = 123
+ print(self.seconds)
+
+
+watch = SubTimer()
diff --git a/tests/misc/cexample_subclass.py.exp b/tests/misc/cexample_subclass.py.exp
new file mode 100644
index 0000000000..a035649e47
--- /dev/null
+++ b/tests/misc/cexample_subclass.py.exp
@@ -0,0 +1,5 @@
+<function>
+AttributeError
+AttributeError
+0
+123