diff --git a/crates/vm/src/builtins/traceback.rs b/crates/vm/src/builtins/traceback.rs index a91ec13c86f..2c3ab56229a 100644 --- a/crates/vm/src/builtins/traceback.rs +++ b/crates/vm/src/builtins/traceback.rs @@ -92,7 +92,10 @@ impl Py { } } } - *zelf.next.lock() = value; + // Dropping the old chain can finalize a frame whose locals reenter + // this traceback. Publish the replacement before releasing the lock. + let old = core::mem::replace(&mut *zelf.next.lock(), value); + drop(old); Ok(()) } } diff --git a/crates/vm/src/exceptions.rs b/crates/vm/src/exceptions.rs index 774e6e55745..178f88dbc82 100644 --- a/crates/vm/src/exceptions.rs +++ b/crates/vm/src/exceptions.rs @@ -743,18 +743,30 @@ impl PyBaseException { } impl PyBaseException { + // Replaced references may run Python finalizers. Release their locks + // before dropping the old values so those finalizers can reenter safely. pub fn set_traceback(&self, traceback: Option) { - *self.traceback.write() = traceback; + let old = core::mem::replace(&mut *self.traceback.write(), traceback); + drop(old); } pub fn set_cause(&self, cause: Option>) { - let mut c = self.cause.write(); - self.set_suppress_context(true); - *c = cause; + let old = { + let mut current = self.cause.write(); + self.set_suppress_context(true); + core::mem::replace(&mut *current, cause) + }; + drop(old); } pub fn set_context(&self, context: Option>) { - *self.context.write() = context; + let old = core::mem::replace(&mut *self.context.write(), context); + drop(old); + } + + fn set_args_tuple(&self, args: PyTupleRef) { + let old = core::mem::replace(&mut *self.args.write(), args); + drop(old); } pub(super) fn __suppress_context__(&self) -> bool { @@ -873,7 +885,7 @@ impl Py { }; let args: ArgIterable = value.try_into_value(vm)?; let args = args.iter(vm)?.collect::>>()?; - *self.args.write() = PyTuple::new_ref(args, &vm.ctx); + self.set_args_tuple(PyTuple::new_ref(args, &vm.ctx)); Ok(()) } @@ -958,7 +970,7 @@ impl Py { impl PyRef { #[pymethod] fn with_traceback(self, tb: Option) -> Self { - *self.traceback.write() = tb; + self.set_traceback(tb); self } } @@ -984,7 +996,7 @@ impl Initializer for PyBaseException { type Args = FuncArgs; fn init(zelf: &Py, args: Self::Args, vm: &VirtualMachine) -> PyResult<()> { - *zelf.args.write() = PyTuple::new_ref(args.args, &vm.ctx); + zelf.set_args_tuple(PyTuple::new_ref(args.args, &vm.ctx)); Ok(()) } }