public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/criu] f43: Route veth restore through usernsd for userns mode (upstream PR#3006)
@ 2026-07-06 6:07 Adrian Reber
0 siblings, 0 replies; only message in thread
From: Adrian Reber @ 2026-07-06 6:07 UTC (permalink / raw)
To: git-commits
A new commit has been pushed.
Repo : rpms/criu
Branch : f43
Commit : 7ca58ef6a75fdd015dccfcdda9c4e29d22f3c485
Author : Adrian Reber <adrian@lisas.de>
Date : 2026-04-22T07:20:37+00:00
Stats : +444/-1 in 5 file(s)
URL : https://src.fedoraproject.org/rpms/criu/c/7ca58ef6a75fdd015dccfcdda9c4e29d22f3c485?branch=f43
Log:
Route veth restore through usernsd for userns mode (upstream PR#3006)
Fix rseq01 test for kernel 7.0 rseq changes (upstream PR#3007)
Handle UDPLITE removal in kernel 7.1 (upstream PR#3003)
Signed-off-by: Adrian Reber <adrian@lisas.de>
---
diff --git a/0002-net-Route-veth-restore-through-usernsd-for-userns-mode.patch b/0002-net-Route-veth-restore-through-usernsd-for-userns-mode.patch
new file mode 100644
index 0000000..cbedd55
--- /dev/null
+++ b/0002-net-Route-veth-restore-through-usernsd-for-userns-mode.patch
@@ -0,0 +1,178 @@
+From 7ad6bb97b4e3951fc79cbda51b05e649aea2854a Mon Sep 17 00:00:00 2001
+From: Adrian Reber <areber@redhat.com>
+Date: Tue, 21 Apr 2026 09:04:51 +0000
+Subject: [PATCH] net: Route veth restore through usernsd for userns mode
+
+Starting with Linux kernel commit 7b735ef81286 ("rtnetlink: add
+missing netlink_ns_capable() check for peer netns"), creating a
+veth pair with a peer in a different network namespace requires
+CAP_NET_ADMIN in the peer namespace as well:
+
+ rtnetlink: add missing netlink_ns_capable() check for peer netns
+
+ rtnl_newlink() lacks a CAP_NET_ADMIN capability check on the peer
+ network namespace when creating paired devices (veth, vxcan,
+ netkit). This allows an unprivileged user with a user namespace
+ to create interfaces in arbitrary network namespaces, including
+ init_net.
+
+ Add a netlink_ns_capable() check for CAP_NET_ADMIN in the peer
+ namespace before allowing device creation to proceed.
+
+Link: https://github.com/torvalds/linux/commit/7b735ef81286007794a227ce2539419479c02a5f
+
+When CRIU restores a veth in user namespace mode, it sends the
+RTM_NEWLINK request from a netlink socket inside the child user
+namespace. The veth peer is placed into the root network namespace
+via IFLA_NET_NS_FD, but the child user namespace does not have
+CAP_NET_ADMIN in the root namespace, so the new kernel check
+rejects the request with EPERM.
+
+Fix this by routing the veth creation through usernsd when the
+peer lives in an external (host) namespace specified via the
+--external veth[name] restore option. usernsd runs with real
+root privileges in the init user namespace, so it passes the
+capability check in both namespaces.
+
+The usernsd path is only used for external veth mappings, not for
+child-to-child namespace veths (has_peer_nsid), because in that
+case both namespaces share the same user namespace and already
+have CAP_NET_ADMIN in each other.
+
+The approach mirrors restore_one_macvlan(), which already solves
+the same CAP_NET_ADMIN-in-both-namespaces problem for macvlan
+devices. A new veth_link_info_userns() builds the RTM_NEWLINK
+request without IFLA_NET_NS_FD for the peer -- since usernsd
+sends the request from the root network namespace, the peer
+naturally stays there. userns_restore_one_link() then adds a
+top-level IFLA_NET_NS_FD to move the main device into the child
+namespace.
+
+Assisted-by: Claude Code (claude-opus-4-6):claude-opus-4-6@default
+Signed-off-by: Adrian Reber <areber@redhat.com>
+---
+ criu/net.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 104 insertions(+)
+
+diff --git a/criu/net.c b/criu/net.c
+index e5775a3287..e1dc5973fd 100644
+--- a/criu/net.c
++++ b/criu/net.c
+@@ -1653,6 +1653,94 @@ static int restore_one_macvlan(struct ns_id *ns, struct net_link *link, int nlsk
+ return ret;
+ }
+
++/*
++ * Simplified veth_link_info for the usernsd path: builds the veth peer
++ * info without adding IFLA_NET_NS_FD for the peer, since the netlink
++ * request will be sent from usernsd's namespace (the root/peer namespace)
++ * and the peer should remain there.
++ */
++static int veth_link_info_userns(struct ns_id *ns, struct net_link *link, struct newlink_req *req)
++{
++ NetDeviceEntry *nde = link->nde;
++ struct rtattr *veth_data, *peer_data;
++ struct ifinfomsg ifm;
++ char key[100], *val;
++
++ addattr_l(&req->h, sizeof(*req), IFLA_INFO_KIND, "veth", 4);
++
++ veth_data = NLMSG_TAIL(&req->h);
++ addattr_l(&req->h, sizeof(*req), IFLA_INFO_DATA, NULL, 0);
++ peer_data = NLMSG_TAIL(&req->h);
++ memset(&ifm, 0, sizeof(ifm));
++
++ if (nde->has_peer_nsid)
++ ifm.ifi_index = nde->peer_ifindex;
++ addattr_l(&req->h, sizeof(*req), VETH_INFO_PEER, &ifm, sizeof(ifm));
++
++ /* Set peer name from external mapping if available */
++ snprintf(key, sizeof(key), "veth[%s]", nde->name);
++ val = external_lookup_by_key(key);
++ if (!IS_ERR_OR_NULL(val)) {
++ char *aux = strchrnul(val, '@');
++
++ addattr_l(&req->h, sizeof(*req), IFLA_IFNAME, val, aux - val);
++ }
++
++ /*
++ * No IFLA_NET_NS_FD for the peer -- the request is sent from
++ * usernsd's namespace (the root network namespace) and the
++ * peer stays there. userns_restore_one_link() adds a top-level
++ * IFLA_NET_NS_FD to move the main device into the child netns.
++ */
++
++ peer_data->rta_len = (char *)NLMSG_TAIL(&req->h) - (char *)peer_data;
++ veth_data->rta_len = (char *)NLMSG_TAIL(&req->h) - (char *)veth_data;
++
++ link->created = true;
++
++ return 0;
++}
++
++/*
++ * When running in user namespaces with a cross-namespace veth peer,
++ * CAP_NET_ADMIN is required in both namespaces for veth creation
++ * (kernel >= 7.0, commit 7b735ef81286). Route the request through
++ * usernsd which runs with real root privileges, similar to
++ * restore_one_macvlan().
++ *
++ * The netlink request is sent from usernsd's namespace (the root
++ * network namespace), so the veth peer stays there. The main device
++ * is moved to the child namespace via a top-level IFLA_NET_NS_FD
++ * added by userns_restore_one_link().
++ */
++static int restore_one_veth_userns(struct ns_id *ns, struct net_link *link)
++{
++ struct newlink_req req;
++ int my_netns, ret;
++
++ my_netns = open_proc(PROC_SELF, "ns/net");
++ if (my_netns < 0)
++ return -1;
++
++ if (populate_newlink_req(ns, &req, RTM_NEWLINK, link,
++ veth_link_info_userns, NULL) < 0) {
++ close(my_netns);
++ return -1;
++ }
++
++ pr_info("Restoring netdev %s idx %d via usernsd\n",
++ link->nde->name, link->nde->ifindex);
++
++ ret = userns_call(userns_restore_one_link, 0,
++ &req, sizeof(req), my_netns);
++ if (ret < 0)
++ pr_err("couldn't restore veth interface %s via usernsd\n",
++ link->nde->name);
++
++ close(my_netns);
++ return ret;
++}
++
+ static int sit_link_info(struct ns_id *ns, struct net_link *link, struct newlink_req *req)
+ {
+ NetDeviceEntry *nde = link->nde;
+@@ -1762,6 +1850,22 @@ static int __restore_link(struct ns_id *ns, struct net_link *link, int nlsk)
+ if (!IS_ERR_OR_NULL(val))
+ return move_veth(val, ns, link, nlsk);
+
++ /*
++ * Creating a veth with a cross-namespace peer requires
++ * CAP_NET_ADMIN in both namespaces (kernel >= 7.0).
++ * In user namespace mode, route through usernsd which
++ * has real root when the peer lives in an external
++ * (host) namespace specified via --external veth[name].
++ */
++ if (root_ns_mask & CLONE_NEWUSER) {
++ char vkey[100];
++
++ snprintf(vkey, sizeof(vkey), "veth[%s]", nde->name);
++ val = external_lookup_by_key(vkey);
++ if (!IS_ERR_OR_NULL(val))
++ return restore_one_veth_userns(ns, link);
++ }
++
+ return restore_one_link(ns, link, nlsk, veth_link_info, NULL);
+ case ND_TYPE__TUN:
+ return restore_one_tun(ns, link, nlsk);
diff --git a/0003-zdtm-Fix-rseq01-test-for-kernel-7.0-rseq-changes.patch b/0003-zdtm-Fix-rseq01-test-for-kernel-7.0-rseq-changes.patch
new file mode 100644
index 0000000..e0ead93
--- /dev/null
+++ b/0003-zdtm-Fix-rseq01-test-for-kernel-7.0-rseq-changes.patch
@@ -0,0 +1,163 @@
+From 14574afd322113630b897f5f7d08a292fb06ce5a Mon Sep 17 00:00:00 2001
+From: Adrian Reber <areber@redhat.com>
+Date: Tue, 21 Apr 2026 09:30:17 +0000
+Subject: [PATCH] zdtm: Fix rseq01 test for kernel 7.0 rseq changes
+
+Starting with Linux 7.0, the rseq feature size has grown to 33 bytes
+and AT_RSEQ_ALIGN has increased to 64. This causes sizeof(struct rseq)
+from the uapi header to be 64 (33 bytes padded to aligned(32)), while
+the actual registration size used by glibc (__rseq_size) is 33.
+
+The rseq01 test was using sizeof(struct rseq) as the registration
+size and a test-local __rseq_abi variable (with only 32-byte
+alignment from the uapi header) as the rseq area. Both are
+wrong on kernel 7.0:
+
+ - The kernel now checks alignment against __alignof__(struct rseq)
+ which is 64 internally; the 32-byte-aligned test variable may
+ not satisfy this.
+
+ - sizeof(struct rseq) = 64 does not match the feature size of 33
+ that glibc registered with, so re-registration with a different
+ size fails.
+
+Fix by:
+
+ - Using __rseq_size (the feature size reported by glibc) as the
+ registration size instead of sizeof(struct rseq).
+
+ - Using the glibc-provided rseq area (at thread_pointer() +
+ __rseq_offset) which is allocated with proper AT_RSEQ_ALIGN
+ alignment, instead of a test-local variable.
+
+Both fall back to the previous behavior when glibc rseq support
+is not available.
+
+This mirrors the glibc fix:
+
+ https://sourceware.org/cgit/glibc/commit/?id=67f303b47dc584f204e3f2441b9832082415eebc
+
+Assisted-by: Claude Code (claude-opus-4-6):claude-opus-4-6@default
+Signed-off-by: Adrian Reber <areber@redhat.com>
+---
+ test/zdtm/transition/rseq01.c | 59 +++++++++++++++++++++++++++++------
+ 1 file changed, 50 insertions(+), 9 deletions(-)
+
+diff --git a/test/zdtm/transition/rseq01.c b/test/zdtm/transition/rseq01.c
+index 08a7a8e1a6..9c5925bc45 100644
+--- a/test/zdtm/transition/rseq01.c
++++ b/test/zdtm/transition/rseq01.c
+@@ -20,9 +20,19 @@
+ #endif
+ #endif
+
++/*
++ * HAVE_GLIBC_RSEQ is set when the C library provides rseq support
++ * (__rseq_size, __rseq_offset). glibc >= 2.35 defines RSEQ_SIG in
++ * <sys/rseq.h>; musl does not, so the test falls back to its own
++ * definitions below.
++ */
++#if defined(RSEQ_SIG)
++#define HAVE_GLIBC_RSEQ 1
++#endif
++
+ #if defined(__x86_64__)
+
+-#if defined(__x86_64__) && defined(RSEQ_SIG)
++#ifdef HAVE_GLIBC_RSEQ
+ static inline void *thread_pointer(void)
+ {
+ void *result;
+@@ -38,11 +48,11 @@ static inline void unregister_old_rseq(void)
+ size = 32;
+ syscall(__NR_rseq, (void *)((char *)thread_pointer() + __rseq_offset), size, 1, RSEQ_SIG);
+ }
+-#else
++#else /* !HAVE_GLIBC_RSEQ */
+ static inline void unregister_old_rseq(void)
+ {
+ }
+-#endif
++#endif /* HAVE_GLIBC_RSEQ */
+
+ const char *test_doc = "rseq() transition test";
+ const char *test_author = "Alexander Mikhalitsyn <alexander.mikhalitsyn@virtuozzo.com>";
+@@ -97,11 +107,27 @@ static int sys_rseq(volatile struct rseq *rseq_abi, uint32_t rseq_len, int flags
+ return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
+ }
+
++/*
++ * Return the rseq registration size. Starting with Linux 7.0,
++ * AT_RSEQ_ALIGN is 64 but the feature size is 33, so sizeof(struct rseq)
++ * (padded to alignment) no longer matches the registration size the kernel
++ * expects. Use __rseq_size when available, clamped to a minimum of 32
++ * for older kernels (mirroring glibc's rseq-internal.h).
++ */
++static uint32_t rseq_reg_size(void)
++{
++#ifdef HAVE_GLIBC_RSEQ
++ if (__rseq_size)
++ return (__rseq_size < 32) ? 32 : __rseq_size;
++#endif
++ return sizeof(struct rseq);
++}
++
+ static void register_thread(void)
+ {
+ int rc;
+ unregister_old_rseq();
+- rc = sys_rseq(rseq_ptr, sizeof(struct rseq), 0, RSEQ_SIG);
++ rc = sys_rseq(rseq_ptr, rseq_reg_size(), 0, RSEQ_SIG);
+ if (rc) {
+ fail("Failed to register rseq");
+ exit(1);
+@@ -111,7 +137,7 @@ static void register_thread(void)
+ static void check_thread(void)
+ {
+ int rc;
+- rc = sys_rseq(rseq_ptr, sizeof(struct rseq), 0, RSEQ_SIG);
++ rc = sys_rseq(rseq_ptr, rseq_reg_size(), 0, RSEQ_SIG);
+ if (!(rc && errno == EBUSY)) {
+ fail("Failed to check rseq %d", rc);
+ exit(1);
+@@ -206,12 +232,27 @@ static intptr_t *cpu_data;
+ bool ignore_abort = true;
+ int thread_ret;
+
++static volatile struct rseq *rseq_area(void)
++{
++#ifdef HAVE_GLIBC_RSEQ
++ /*
++ * Use the glibc-provided rseq area which is allocated with the
++ * correct alignment (AT_RSEQ_ALIGN, 64 on kernel >= 7.0).
++ * The test-local __rseq_abi only has the uapi header alignment
++ * (32 bytes) which may not satisfy the kernel's requirement.
++ */
++ if (__rseq_size)
++ return (volatile struct rseq *)((char *)thread_pointer() + __rseq_offset);
++#endif
++ return &__rseq_abi;
++}
++
+ void *thread_routine(void *args)
+ {
+ int cpu;
+
+- rseq_ptr = &__rseq_abi;
+- memset((void *)rseq_ptr, 0, sizeof(struct rseq));
++ rseq_ptr = rseq_area();
++ memset((void *)rseq_ptr, 0, rseq_reg_size());
+ register_thread();
+ task_waiter_complete(&waiter, 1);
+ task_waiter_wait4(&waiter, 2);
+@@ -235,8 +276,8 @@ int main(int argc, char *argv[])
+ long nr_cpus;
+ pthread_t thread;
+
+- rseq_ptr = &__rseq_abi;
+- memset((void *)rseq_ptr, 0, sizeof(struct rseq));
++ rseq_ptr = rseq_area();
++ memset((void *)rseq_ptr, 0, rseq_reg_size());
+
+ test_init(argc, argv);
+ nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
diff --git a/0004-sockets-Treat-UDPLITE-as-optional-in-collect_err.patch b/0004-sockets-Treat-UDPLITE-as-optional-in-collect_err.patch
new file mode 100644
index 0000000..1cd8f12
--- /dev/null
+++ b/0004-sockets-Treat-UDPLITE-as-optional-in-collect_err.patch
@@ -0,0 +1,52 @@
+From 1cf8d3b21c64f0ef3310301019136433fc9f6392 Mon Sep 17 00:00:00 2001
+From: Adrian Reber <areber@redhat.com>
+Date: Mon, 20 Apr 2026 19:19:42 +0000
+Subject: [PATCH] sockets: Treat UDPLITE as optional in collect_err()
+
+Kernel 7.1 removed IPPROTO_UDPLITE support. When the UDPLITE diag
+module is absent, the netlink request returns -ENOENT, which CRIU
+was treating as a fatal error causing the entire dump to fail.
+
+Apply the same treatment as IPPROTO_RAW: return success from the
+error callback so the dump can proceed. If a UDPLITE socket is
+actually encountered, it will fail at lookup time with a clear
+error rather than failing the entire dump preemptively.
+
+Fixes: https://github.com/checkpoint-restore/criu/issues/3002
+
+Assisted-by: Claude Code (claude-opus-4-6):claude-opus-4-6@default
+Signed-off-by: Adrian Reber <areber@redhat.com>
+---
+ criu/sockets.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/criu/sockets.c b/criu/sockets.c
+index e4adae03cd..7778b46881 100644
+--- a/criu/sockets.c
++++ b/criu/sockets.c
+@@ -827,16 +827,20 @@ static int collect_err(int err, struct ns_id *ns, void *arg)
+ if (err == -ENOENT) {
+ pr_debug("%s: %d\n", msg, err);
+ /*
+- * Unlike other modules RAW sockets are
+- * always optional and not commonly used.
++ * Unlike other modules RAW and UDPLITE sockets
++ * are always optional and not commonly used.
+ * Currently we warn user about lack of
+ * a particular module support in "check"
+ * procedure. Thus don't fail on lack of
+- * RAW diags in a regular dump. If we meet
+- * a raw socket we will simply fail on dump
++ * these diags in a regular dump. If we meet
++ * such a socket we will simply fail on dump
+ * procedure because it won't be resolved.
++ *
++ * Note: IPPROTO_UDPLITE support was removed
++ * from the kernel starting with v7.1.
+ */
+- if (gr->protocol == IPPROTO_RAW)
++ if (gr->protocol == IPPROTO_RAW ||
++ gr->protocol == IPPROTO_UDPLITE)
+ return 0;
+ return -ENOENT;
+ }
diff --git a/0005-zdtm-Skip-socket_udplite-test-when-kernel-lacks-UDPLITE.patch b/0005-zdtm-Skip-socket_udplite-test-when-kernel-lacks-UDPLITE.patch
new file mode 100644
index 0000000..5d9d5d5
--- /dev/null
+++ b/0005-zdtm-Skip-socket_udplite-test-when-kernel-lacks-UDPLITE.patch
@@ -0,0 +1,37 @@
+From 23905c26b86afdc01bcfa85ae0a3aab015fc81f1 Mon Sep 17 00:00:00 2001
+From: Adrian Reber <areber@redhat.com>
+Date: Tue, 21 Apr 2026 07:58:00 +0000
+Subject: [PATCH] zdtm: Skip socket_udplite test when kernel lacks UDPLITE
+
+Kernel 7.1 removed IPPROTO_UDPLITE support. Add a checkskip
+script that probes for a UDPLITE socket and skips the test
+with EPROTONOSUPPORT instead of failing.
+
+Assisted-by: Claude Code (claude-opus-4-6):claude-opus-4-6@default
+Signed-off-by: Adrian Reber <areber@redhat.com>
+---
+ test/zdtm/static/socket_udplite.checkskip | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+ create mode 100755 test/zdtm/static/socket_udplite.checkskip
+
+diff --git a/test/zdtm/static/socket_udplite.checkskip b/test/zdtm/static/socket_udplite.checkskip
+new file mode 100755
+index 0000000000..a562550843
+--- /dev/null
++++ b/test/zdtm/static/socket_udplite.checkskip
+@@ -0,0 +1,15 @@
++#!/usr/bin/env python3
++# IPPROTO_UDPLITE was removed in kernel 7.1, skip the test if the
++# protocol is not available.
++import socket
++import errno
++
++try:
++ socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDPLITE).close()
++except OSError as e:
++ if e.errno == errno.EPROTONOSUPPORT:
++ print("UDPLITE is not supported by this kernel.")
++ exit(1)
++ raise
++
++exit(0)
diff --git a/criu.spec b/criu.spec
index af083a2..383213a 100644
--- a/criu.spec
+++ b/criu.spec
@@ -13,13 +13,17 @@
Name: criu
Version: 4.2
-Release: 17%{?dist}
+Release: 18%{?dist}
Summary: Tool for Checkpoint/Restore in User-space
License: GPL-2.0-only AND LGPL-2.1-only AND MIT
URL: http://criu.org/
Source0: https://github.com/checkpoint-restore/criu/archive/v%{version}/criu-%{version}.tar.gz
Patch0: 0001-rseq-use-kernel-rseq.h-when-glibc-detects-it.patch
Patch1: 0001-tty-fix-compiler-error.patch
+Patch2: 0002-net-Route-veth-restore-through-usernsd-for-userns-mode.patch
+Patch3: 0003-zdtm-Fix-rseq01-test-for-kernel-7.0-rseq-changes.patch
+Patch4: 0004-sockets-Treat-UDPLITE-as-optional-in-collect_err.patch
+Patch5: 0005-zdtm-Skip-socket_udplite-test-when-kernel-lacks-UDPLITE.patch
# Add protobuf-c as a dependency.
# We use this patch because the protobuf-c package name
@@ -118,6 +122,10 @@ This script can help to workaround the so called "PID mismatch" problem.
%setup -q
%patch -P 0 -p1
%patch -P 1 -p1
+%patch -P 2 -p1
+%patch -P 3 -p1
+%patch -P 4 -p1
+%patch -P 5 -p1
%patch -P 99 -p1
%build
@@ -206,6 +214,11 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/libcriu.a
%tmpfiles_create %{name}.conf
%changelog
+* Wed Apr 22 2026 Adrian Reber <adrian@lisas.de> - 4.2-18
+- Route veth restore through usernsd for userns mode (upstream PR#3006)
+- Fix rseq01 test for kernel 7.0 rseq changes (upstream PR#3007)
+- Handle UDPLITE removal in kernel 7.1 (upstream PR#3003)
+
* Thu Mar 26 2026 Adrian Reber <adrian@lisas.de> - 4.2-17
- Always use nftables network locking backend
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-07-06 6:07 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-06 6:07 [rpms/criu] f43: Route veth restore through usernsd for userns mode (upstream PR#3006) Adrian Reber
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox