From 8c9a5a26906c84a2e77b7bd48c5b30974ee3c75a Mon Sep 17 00:00:00 2001 From: Pavel Tikhomirov Date: Wed, 14 Dec 2022 17:28:47 +0300 Subject: [PATCH] sk-unix: resolve fake queuers vs scm fds in unix sockets fallacy Long story short: we face standard "which was first chicken or egg?" problem here. 1) In add_fake_unix_queuers() for each unpared established unix socket we want to add a "fake" queuer. And we want to decide at this point which task should create this queuer and we just try to copy the task from original socket. But original socket can have no task yet too, because it can have no open fds, instead it is residing inside other unix socket. 2) In prepare_scms we look through the packets from all unix sockets and detect file descriptors in them. 3) We need to add those packets in special queuer's list so that queuer can understand when all files we need to put in it are already created. So it means that we need to have "fake" queuers present at this point. 4) At the same time it is also a point to understand which task should create those descriptors from unix sockets and we just try to copy the task from the queuer who would put them to unix socket. You can imagine that queuers should be created (1) before we try to copy task from them (3) and their peer socket may only give the task to queuers (1) after it copyes the task from it's ancestor unix socket (4). So let's fix it with: - Split "fake" queuer owner task detection into separate step after handling scm - When handling scm, if queuer has no owner task, take it from original socket Signed-off-by: Pavel Tikhomirov (cherry picked from commit 82641caf2580bd941cd78df32193556ed1ae31c2) Signed-off-by: Ahmed Elaidy Reviewed-by: Alexander Mikhalitsyn --- criu/cr-restore.c | 4 ++++ criu/include/sockets.h | 1 + criu/sk-unix.c | 52 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/criu/cr-restore.c b/criu/cr-restore.c index 3abdbbbf8..7579639a2 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -360,6 +360,10 @@ static int root_prepare_shared(void) if (ret) goto err; + ret = add_fake_unix_queuers_finish(); + if (ret) + goto err; + ret = run_post_prepare(); if (ret) goto err; diff --git a/criu/include/sockets.h b/criu/include/sockets.h index 6c81d3edd..764cb93b2 100644 --- a/criu/include/sockets.h +++ b/criu/include/sockets.h @@ -40,6 +40,7 @@ extern int collect_sockets(struct ns_id *); extern struct collect_image_info inet_sk_cinfo; extern struct collect_image_info unix_sk_cinfo; extern int add_fake_unix_queuers(void); +extern int add_fake_unix_queuers_finish(void); extern int fix_external_unix_sockets(void); extern int prepare_scms(void); extern int unix_note_scm_rights(int id_for, uint32_t *file_ids, int *fds, int n_ids); diff --git a/criu/sk-unix.c b/criu/sk-unix.c index f41f6c1ef..5f41a1e44 100644 --- a/criu/sk-unix.c +++ b/criu/sk-unix.c @@ -1058,6 +1058,18 @@ static struct unix_sk_info *find_queuer_for(int id) return NULL; } +static struct unix_sk_info *find_unix_socket(int id) +{ + struct unix_sk_info *ui; + + list_for_each_entry(ui, &unix_sockets, list) { + if (ui->ue->id == id) + return ui; + } + + return NULL; +} + static struct fdinfo_list_entry *get_fle_for_task(struct file_desc *tgt, struct pstree_item *owner, bool force_master) { struct fdinfo_list_entry *fle; @@ -1115,13 +1127,14 @@ static struct fdinfo_list_entry *get_fle_for_task(struct file_desc *tgt, struct int unix_note_scm_rights(int id_for, uint32_t *file_ids, int *fds, int n_ids) { + struct fdinfo_list_entry *fle; struct unix_sk_info *ui; struct pstree_item *owner; int i; ui = find_queuer_for(id_for); if (!ui) { - pr_err("Can't find sender for %#x\n", id_for); + pr_err("Can't find %#x\n", id_for); return -1; } @@ -1129,7 +1142,14 @@ int unix_note_scm_rights(int id_for, uint32_t *file_ids, int *fds, int n_ids) /* * This is the task that will restore this socket */ - owner = file_master(&ui->d)->task; + fle = try_file_master(&ui->d); + if (!fle) { + struct unix_sk_info *peer; + + peer = find_unix_socket(id_for); + fle = file_master(&peer->d); + } + owner = fle->task; pr_info("-> will set up deps\n"); /* @@ -2215,7 +2235,6 @@ static void set_peer(struct unix_sk_info *ui, struct unix_sk_info *peer) static int add_fake_queuer(struct unix_sk_info *ui) { struct unix_sk_info *peer; - struct pstree_item *task; UnixSkEntry *peer_ue; SkOptsEntry *skopts; FownEntry *fown; @@ -2250,9 +2269,7 @@ static int add_fake_queuer(struct unix_sk_info *ui) file_desc_add(&peer->d, peer_ue->id, &unix_desc_ops); list_del_init(&peer->d.fake_master_list); list_add(&peer->list, &unix_sockets); - task = file_master(&ui->d)->task; - - return (get_fle_for_task(&peer->d, task, true) == NULL); + return 0; } int add_fake_unix_queuers(void) @@ -2272,6 +2289,29 @@ int add_fake_unix_queuers(void) return 0; } +static int add_fake_queuer_finish(struct unix_sk_info *queuer) +{ + struct pstree_item *task = file_master(&queuer->peer->d)->task; + + return get_fle_for_task(&queuer->d, task, true) == NULL; +} + +int add_fake_unix_queuers_finish(void) +{ + struct unix_sk_info *ui; + + pr_info("Adding fake unix queuers (finish)\n"); + + list_for_each_entry(ui, &unix_sockets, list) { + if (ui->ue->ino != FAKE_INO) + continue; + + if (add_fake_queuer_finish(ui)) + return -1; + } + return 0; +} + /* This function is called from post prepare only */ static int interconnected_pair(struct unix_sk_info *ui, struct unix_sk_info *peer) {