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.
This commit is contained in:
Lennart Poettering
2025-03-19 18:14:24 +01:00
parent 511bf79b4e
commit 179dcf924f
2 changed files with 37 additions and 5 deletions
+34 -4
View File
@@ -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;
return sd_event_add_child(e, s, pid->pid, options, callback, userdata);
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 0;
}
int event_source_get_child_pidref(sd_event_source *s, PidRef *ret) {
+3 -1
View File
@@ -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;