-
Notifications
You must be signed in to change notification settings - Fork 891
Description
In analogy to
we can smuggle Ungil data across Python::allow_threads using scoped-tls, too.
use pyo3::prelude::*;
use pyo3::types::PyString;
use scoped_tls::scoped_thread_local;
fn main() {
Python::with_gil(|py| {
let string = PyString::new(py, "foo");
scoped_thread_local!(static WRAPPED: PyString);
WRAPPED.set(string, || {
py.allow_threads(|| {
WRAPPED.with(|smuggled: &PyString| {
println!("{:?}", smuggled);
});
});
});
});
}(results in segfault in my test)
Unlike #2141, this issue is virtually unsolvable, i.e. even the auto trait approach with the feature="nightly" enabled cannot catch this at all. There’s no property in the callback to allow_threads that can catch this. Really, the callback doesn’t even capture anything at all:
use pyo3::prelude::*;
use pyo3::types::PyString;
use scoped_tls::scoped_thread_local;
scoped_thread_local!(static WRAPPED: PyString);
fn callback() {
WRAPPED.with(|smuggled: &PyString| {
println!("{:?}", smuggled);
});
}
fn main() {
Python::with_gil(|py| {
let string = PyString::new(py, "foo");
WRAPPED.set(string, || {
py.allow_threads(callback); // callback is an ordinary `fn() -> ()` item.
});
});
}Again, there’s a “whose fault” question to be asked, whether the fact that the standard library’s thread-locals require T: 'static means there’s any guarantees that non-'static data isn’t allowed to be “smuggled” through thread-local storage. In my view, if you look at what kind of things it offers, scoped-tls seems even less unreasonable to be called “sound”, compared to sync_wrapper. Yet the consequences for Ungil are more detrimental.