From 832801cd81251c9b826a29296129bbd4268440d2 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Wed, 8 Jul 2026 22:28:19 +0300 Subject: [PATCH] netlink: dump and restore ucred-s for netlink messages ucred in a netlink message contains the same pid and it doesn't metter from which pidns it is read. Cc: Kirill Tkhai Signed-off-by: Andrei Vagin Signed-off-by: Cyrill Gorcunov (cherry picked from commit 66f8f7b2ae51a2cef4044df478389137735e1d6e) Ahmed Elaidy: - dropped the ENOBUFS bits (SK_QUEUE_TRACK_ENOBUFS and the int *val out-parameter): they belong to "netlink: save and generate ENOBUFS at restore", which needs a non-upstream kernel extension and is intentionally not ported. - SK_QUEUE_DUMP_ADDR / the int flags signature come from the preceding "sk-queue: use flags in dump_sk_queue" cherry-pick rather than being glued in here. Signed-off-by: Ahmed Elaidy Reviewed-by: Alexander Mikhalitsyn --- criu/include/sk-queue.h | 1 + criu/sk-queue.c | 78 +++++++++++++++++++++++++++++++++++++++-- criu/sk-unix.c | 2 +- images/sk-packet.proto | 9 +++-- 4 files changed, 85 insertions(+), 5 deletions(-) diff --git a/criu/include/sk-queue.h b/criu/include/sk-queue.h index ffcd6b21e..1040ca071 100644 --- a/criu/include/sk-queue.h +++ b/criu/include/sk-queue.h @@ -3,6 +3,7 @@ extern struct collect_image_info sk_queues_cinfo; +#define SK_QUEUE_REAL_PID 0x1 /* scm creds contains a real pid */ extern int dump_sk_queue(int sock_fd, int sock_id, int flags); extern int restore_sk_queue(int fd, unsigned int peer_id); diff --git a/criu/sk-queue.c b/criu/sk-queue.c index 6158f5da0..f61780e6e 100644 --- a/criu/sk-queue.c +++ b/criu/sk-queue.c @@ -18,6 +18,10 @@ #include "util-pie.h" #include "sockets.h" #include "xmalloc.h" +#include "namespaces.h" +#include "pstree.h" +#include "util.h" + #include "sk-queue.h" #include "files.h" #include "protobuf.h" @@ -132,7 +136,49 @@ static int dump_scm_rights(struct cmsghdr *ch, SkPacketEntry *pe) */ #define CMSG_MAX_SIZE 2048 -static int dump_packet_cmsg(struct msghdr *mh, SkPacketEntry *pe) +static int dump_sk_creds(struct ucred *ucred, SkPacketEntry *pe, int flags) +{ + SkUcredEntry *ent; + + ent = xmalloc(sizeof(*ent)); + if (!ent) + return -1; + + sk_ucred_entry__init(ent); + ent->uid = userns_uid(ucred->uid); + ent->gid = userns_gid(ucred->gid); + if (flags & SK_QUEUE_REAL_PID) { + /* + * It is impossible to convert pid from real to virt, + * because virt pid-s are known for dumped task only + */ + pr_err("ucred-s for unix sockets aren't supported yet\n"); + return -1; + } else { + int pidns = root_ns_mask & CLONE_NEWPID; + char path[64]; + int ret; + + /* Does a process exist? */ + if (pidns) { + snprintf(path, sizeof(path), "%d", ucred->pid); + ret = faccessat(get_service_fd(CR_PROC_FD_OFF), path, R_OK, 0); + } else { + snprintf(path, sizeof(path), "/proc/%d", ucred->pid); + ret = access(path, R_OK); + } + if (ret) { + pr_err("Unable to dump ucred for a dead process %d\n", ucred->pid); + return -1; + } + ent->pid = ucred->pid; + } + pe->ucred = ent; + + return 0; +} + +static int dump_packet_cmsg(struct msghdr *mh, SkPacketEntry *pe, int flags) { struct cmsghdr *ch; int n_rights = 0; @@ -155,6 +201,16 @@ static int dump_packet_cmsg(struct msghdr *mh, SkPacketEntry *pe) continue; } + if (ch->cmsg_len == CMSG_LEN(sizeof(struct ucred)) && + ch->cmsg_type == SCM_CREDENTIALS && + ch->cmsg_level == SOL_SOCKET) { + struct ucred *ucred = (struct ucred *)CMSG_DATA(ch); + + if (dump_sk_creds(ucred, pe, flags)) + return -1; + continue; + } + pr_err("Control messages in queue, not supported\n"); return -1; } @@ -270,7 +326,7 @@ int dump_sk_queue(int sock_fd, int sock_id, int flags) goto err_set_sock; } - if (dump_packet_cmsg(&msg, &pe)) + if (dump_packet_cmsg(&msg, &pe, flags)) goto err_set_sock; ret = pb_write_one(img_from_set(glob_imgset, CR_FD_SK_QUEUES), &pe, PB_SK_QUEUES); @@ -311,6 +367,7 @@ static int send_one_pkt(int fd, struct sk_packet *pkt) SkPacketEntry *entry = pkt->entry; struct msghdr mh = {}; struct iovec iov; + char cmsg[CMSG_MAX_SIZE]; mh.msg_iov = &iov; mh.msg_iovlen = 1; @@ -330,6 +387,23 @@ static int send_one_pkt(int fd, struct sk_packet *pkt) * boundaries messages should be saved. */ + if (entry->ucred) { + struct ucred *ucred; + struct cmsghdr *ch; + + mh.msg_control = cmsg; + mh.msg_controllen = sizeof(cmsg); + + ch = CMSG_FIRSTHDR(&mh); + ch->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + ch->cmsg_level = SOL_SOCKET; + ch->cmsg_type = SCM_CREDENTIALS; + ucred = (struct ucred *)CMSG_DATA(ch); + ucred->pid = entry->ucred->pid; + ucred->uid = entry->ucred->uid; + ucred->gid = entry->ucred->gid; + mh.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); + } ret = sendmsg(fd, &mh, 0); xfree(pkt->data); xfree(pkt->scm); diff --git a/criu/sk-unix.c b/criu/sk-unix.c index 3eaabb074..3eab56038 100644 --- a/criu/sk-unix.c +++ b/criu/sk-unix.c @@ -408,7 +408,7 @@ static int dump_one_unix_fd(int lfd, uint32_t id, const struct fd_parms *p) * (i stands for in-flight, cons -- for connections) things. */ if (sk->rqlen != 0 && sk->state != TCP_LISTEN) { - if (dump_sk_queue(lfd, id, 0)) + if (dump_sk_queue(lfd, id, SK_QUEUE_REAL_PID)) goto err; } diff --git a/images/sk-packet.proto b/images/sk-packet.proto index b60a8870a..341892607 100644 --- a/images/sk-packet.proto +++ b/images/sk-packet.proto @@ -7,12 +7,17 @@ message scm_entry { repeated uint32 rights = 2; } +message sk_ucred_entry { + required uint32 uid = 1; + required uint32 gid = 2; + required uint32 pid = 3; +} + message sk_packet_entry { required uint32 id_for = 1; required uint32 length = 2; // Reserved for message address // optional bytes addr = 3; repeated scm_entry scm = 4; - // Reserved for ucred restore - // optional sk_ucred_entry ucred = 128; + optional sk_ucred_entry ucred = 128; }