From 179dcf924f7d0ac9398f54baeb39b47abd23aeaf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Mar 2025 15:11:58 +0100 Subject: [PATCH] sd-event: make pidfd copy in event_add_child_pidref() So far we'd directly use the pidfd passed into event_add_child_pidref(), hoping it would not be closed by the caller before we are done. This was violated by vmspawn however. Let's make this safe, and simply duplicate the fd, and make us independent of the caller. --- src/libsystemd/sd-event/event-util.c | 38 +++++++++++++++++++++++++--- src/vmspawn/vmspawn.c | 4 ++- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/libsystemd/sd-event/event-util.c b/src/libsystemd/sd-event/event-util.c index 47cf76f9c2c..d4e986fc491 100644 --- a/src/libsystemd/sd-event/event-util.c +++ b/src/libsystemd/sd-event/event-util.c @@ -165,19 +165,49 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle int event_add_child_pidref( sd_event *e, - sd_event_source **s, + sd_event_source **ret, const PidRef *pid, int options, sd_event_child_handler_t callback, void *userdata) { + int r; + + assert(e); + if (!pidref_is_set(pid)) return -ESRCH; - if (pid->fd >= 0) - return sd_event_add_child_pidfd(e, s, pid->fd, options, callback, userdata); + if (pidref_is_remote(pid)) + return -EREMOTE; + + if (pid->fd < 0) + return sd_event_add_child(e, ret, pid->pid, options, callback, userdata); + + _cleanup_close_ int copy_fd = fcntl(pid->fd, F_DUPFD_CLOEXEC, 3); + if (copy_fd < 0) + return -errno; + + _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; + r = sd_event_add_child_pidfd(e, &s, copy_fd, options, callback, userdata); + if (r < 0) + return r; + + r = sd_event_source_set_child_pidfd_own(s, true); + if (r < 0) + return r; + + TAKE_FD(copy_fd); + + if (ret) + *ret = TAKE_PTR(s); + else { + r = sd_event_source_set_floating(s, true); + if (r < 0) + return r; + } - return sd_event_add_child(e, s, pid->pid, options, callback, userdata); + return 0; } int event_source_get_child_pidref(sd_event_source *s, PidRef *ret) { diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index d616f2c7ab8..294775d03e8 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -2350,7 +2350,9 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { log_debug_errno(r, "Failed allocate memory pressure event source, ignoring: %m"); /* Exit when the child exits */ - (void) event_add_child_pidref(event, NULL, &child_pidref, WEXITED, on_child_exit, NULL); + r = event_add_child_pidref(event, /* ret_event_source= */ NULL, &child_pidref, WEXITED, on_child_exit, /* userdata= */ NULL); + if (r < 0) + return log_error_errno(r, "Failed to watch qemu process: &m"); _cleanup_(osc_context_closep) sd_id128_t osc_context_id = SD_ID128_NULL; _cleanup_(pty_forward_freep) PTYForward *forward = NULL; -- 2.47.3