-
Notifications
You must be signed in to change notification settings - Fork 665
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sched::clone
doesn't pass ownership of closure to new thread.
#919
Comments
I'll have a patch in a few minutes. |
Without rust-lang/rust#28796 being stabilised, In light of this, I think I'll make a patch to change clone to accept something more like what the syscall does now (i.e. |
std::sched::clone
doesn't pass ownership of closure to new thread.sched::clone
doesn't pass ownership of closure to new thread.
Fixes nix-rust#919 Annotate the correct lifetime (`'static`) for the closure transferred to the child process of the clone. Properly box the closure in the parent then rebox it in the child to ensure that the closure is dropped at the end of the invocation in the child rather than at the end of the call to `clone` in the parent. For some reason, unwrapping the boxed function itself caused all kinds of mayhem, so it instead ends up wrapped in an additional box from which it is then unwrapped.
Fixes nix-rust#919 Annotate the correct lifetime (`'static`) for the closure transferred to the child process of the clone. Properly box the closure in the parent then rebox it in the child to ensure that the closure is dropped at the end of the invocation in the child rather than at the end of the call to `clone` in the parent. For some reason, unwrapping the boxed function itself caused all kinds of mayhem, so it instead ends up wrapped in an additional box from which it is then unwrapped.
This is still a problem, and makes clone completely broken (rather than the expected horribly unsafe because it's clone). FnBox has been fixed for a while, though it would be a breaking change to improve On top of that |
@talchas would you like to try rewriting clone? I wouldn't worry about preserving backwards-compatibility; it probably can't be done in a way that also fixes clone's problems. |
I'd go with pub fn clone<F: FnOnce() -> c_int + Send + 'static>(
cb: F,
stack: *mut [u8],
flags: CloneFlags,
signal: Option<c_int>,
) -> Result<Pid> {
extern "C" fn callback<F: FnOnce() -> c_int + Send + 'static>(data: *mut c_void) -> c_int {
let cb: Box<F> = unsafe { Box::from_raw(data as *mut F) };
(*cb)()
}
let cb = Box::into_raw(Box::new(cb));
let res = unsafe {
let combined = flags.bits() | signal.unwrap_or(0);
let ptr = (stack as *mut u8).add((*stack).len());
let ptr_aligned = ptr.sub(ptr as usize % 16);
libc::clone(
callback::<F>,
ptr_aligned as *mut c_void,
combined,
cb as *mut c_void,
)
};
let res = Errno::result(res).map(Pid::from_raw);
// In CLONE_VM we need to free the callback on both sides
if flags.contains(CloneFlags::CLONE_VM) || res.is_err() {
unsafe { Box::from_raw(cb); }
}
res
} but there's a lot of fairly reasonable bikeshed options: taking |
|
Yeah, I didn't do &'static because it would need unsafe anyways, and didn't do Box<[]> because of the freeing issues, and statically allocating a buffer is maybe useful sometimes. You still want a closure, even if it's 'static; you can move in a String/ints/anything else owned still. Yes, I forgot which way around CLONE_VM worked, and making it unsafe is probably good too. |
Hm. The child process/thread thing will never free stack, and unless |
Having a separate CLONE_VM certainly could be reasonable, as the mechanics around memory safety and lifetimes are completely different. For non-VM ones you can drop the 'static requirement, use &mut [u8], and use &mut cb rather than boxing it (but that one definitely should be Looking more at clone(2), I guess there's also the issue that all the SETTID/CLEARTID/PIDFD stuff causes it to write to the varargs slots which there is no way to fill in, so setting those flags has no use other than being UB. Similarly, SETTLS can't be specified, and I guess would be reason enough to make clone_vm() be |
Yeah, good catch. You should remove those flags too, or create separate functions to enable their use. |
The current implementation of of
sched::clone
doesn't pass ownership of the the closure called bycallback
into the newly created thread, only a reference. This means that when the call toclone
ends, the callback is actually dropped, releasing all associated memory. This most likely means that the closure that actually gets run in the child thread is a reference to invalid memory when it is run.Recommended fix is to unbox the
CloneCb
into a raw pointer and box it back up again incallback
. This would mean that the closure would be dropped at the end of the callback in the child thread, rather than at the end of the call toclone
.The text was updated successfully, but these errors were encountered: