From fc7f531da7b12a4f17e33b18f520931855a6a68d Mon Sep 17 00:00:00 2001 From: eabdullin Date: Mon, 22 Sep 2025 09:10:03 +0000 Subject: [PATCH] import UBI crun-1.23.1-2.el9_6 --- .crun.metadata | 2 +- .gitignore | 2 +- SOURCES/1859.patch | 233 +++++++++++++++++ ...8c0620882fbc989f29ba83d1c46fab3bca09.patch | 237 ++++++++++++++++++ SPECS/crun.spec | 15 +- 5 files changed, 485 insertions(+), 4 deletions(-) create mode 100644 SOURCES/1859.patch create mode 100644 SOURCES/d8a88c0620882fbc989f29ba83d1c46fab3bca09.patch diff --git a/.crun.metadata b/.crun.metadata index 1d60182..8d2848d 100644 --- a/.crun.metadata +++ b/.crun.metadata @@ -1 +1 @@ -001defaef47960d938ebe6d07b4333a3b330b4b3 SOURCES/crun-1.22.tar.zst +d2f75ee20a04208db2bbe4351b6a955425623a5e SOURCES/crun-1.23.1.tar.zst diff --git a/.gitignore b/.gitignore index 0b7315c..a6a4dfe 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/crun-1.22.tar.zst +SOURCES/crun-1.23.1.tar.zst diff --git a/SOURCES/1859.patch b/SOURCES/1859.patch new file mode 100644 index 0000000..c77cd08 --- /dev/null +++ b/SOURCES/1859.patch @@ -0,0 +1,233 @@ +From 1556c13f89f5db22911a4e771af9253a9b79e02c Mon Sep 17 00:00:00 2001 +From: Sohan Kunkerkar +Date: Thu, 28 Aug 2025 08:53:20 -0400 +Subject: [PATCH 1/2] src/libcrun: limit tmpfs memory usage for masked paths + +Replace "size=0k" (unlimited growth) with explicit block and inode limits +for tmpfs mounts used in masked directory paths. This prevents excessive +kernel memory consumption under high container density. + +Signed-off-by: Sohan Kunkerkar +--- + src/libcrun/linux.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/libcrun/linux.c b/src/libcrun/linux.c +index 75120cea37..36ed40bb5b 100644 +--- a/src/libcrun/linux.c ++++ b/src/libcrun/linux.c +@@ -1114,7 +1114,7 @@ do_masked_or_readonly_path (libcrun_container_t *container, const char *rel_path + return crun_make_error (err, errno, "cannot stat `%s`", rel_path); + + if ((mode & S_IFMT) == S_IFDIR) +- ret = do_mount (container, "tmpfs", pathfd, rel_path, "tmpfs", MS_RDONLY, "size=0k", LABEL_MOUNT, err); ++ ret = do_mount (container, "tmpfs", pathfd, rel_path, "tmpfs", MS_RDONLY, "nr_blocks=1,nr_inodes=1", LABEL_MOUNT, err); + else + ret = do_mount (container, "/dev/null", pathfd, rel_path, NULL, MS_BIND | MS_RDONLY, NULL, LABEL_MOUNT, err); + if (UNLIKELY (ret < 0)) + +From 4004e5bed9ff52029a829131fbc16f9a877154b9 Mon Sep 17 00:00:00 2001 +From: Sohan Kunkerkar +Date: Tue, 26 Aug 2025 23:22:56 -0400 +Subject: [PATCH 2/2] linux: optimize masked paths with shared empty directory + +Optimize masked path handling by bind-mounting a shared empty directory +(via cached /proc/self/fd) instead of creating per-path tmpfs mounts. +This reduces kernel memory and mount syscall overhead under high container +density. + +Signed-off-by: Sohan Kunkerkar +--- + src/libcrun/linux.c | 110 ++++++++++++++++++++++++++++++++++++++++++- + src/libcrun/status.c | 2 +- + src/libcrun/status.h | 1 + + 3 files changed, 111 insertions(+), 2 deletions(-) + +diff --git a/src/libcrun/linux.c b/src/libcrun/linux.c +index 36ed40bb5b..552715f729 100644 +--- a/src/libcrun/linux.c ++++ b/src/libcrun/linux.c +@@ -21,6 +21,7 @@ + #include + #include "linux.h" + #include "utils.h" ++#include "status.h" + #include + #include + #include +@@ -148,6 +149,12 @@ struct private_data_s + /* Used to save stdin, stdout, stderr during checkpointing to descriptors.json + * and needed during restore. */ + char *external_descriptors; ++ ++ /* Cached shared empty directory for masked paths optimization */ ++ int maskdir_fd; ++ char *maskdir_proc_path; ++ bool maskdir_bind_failed; ++ bool maskdir_warned; + }; + + struct linux_namespace_s +@@ -164,6 +171,8 @@ cleanup_private_data (void *private_data) + + if (p->rootfsfd >= 0) + TEMP_FAILURE_RETRY (close (p->rootfsfd)); ++ if (p->maskdir_fd >= 0) ++ TEMP_FAILURE_RETRY (close (p->maskdir_fd)); + if (p->mount_fds) + cleanup_close_mapp (&(p->mount_fds)); + if (p->dev_fds) +@@ -173,6 +182,7 @@ cleanup_private_data (void *private_data) + free (p->host_notify_socket_path); + free (p->container_notify_socket_path); + free (p->external_descriptors); ++ free (p->maskdir_proc_path); + free (p); + } + +@@ -185,6 +195,7 @@ get_private_data (struct libcrun_container_s *container) + container->private_data = p; + p->rootfsfd = -1; + p->notify_socket_tree_fd = -1; ++ p->maskdir_fd = -1; + container->cleanup_private_data = cleanup_private_data; + } + return container->private_data; +@@ -1058,6 +1069,103 @@ has_mount_for (libcrun_container_t *container, const char *destination) + return false; + } + ++static void ++warn_tmpfs_fallback_once (struct private_data_s *private_data, const char *reason) ++{ ++ if (! private_data->maskdir_warned) ++ { ++ libcrun_warning ("Falling back to tmpfs for masked dirs (reason: %s)", reason); ++ private_data->maskdir_warned = true; ++ } ++} ++ ++/* Get or create the cached shared empty directory for masked paths optimization. ++ * Creates directory and FD once per container, caches /proc/self/fd path for fast mounting. ++ */ ++static int ++get_shared_empty_dir_cached (libcrun_container_t *container, char **proc_fd_path, libcrun_error_t *err) ++{ ++ struct private_data_s *private_data = get_private_data (container); ++ cleanup_close int fd = -1; ++ cleanup_free char *run_dir = NULL; ++ cleanup_free char *empty_dir_path = NULL; ++ int ret; ++ ++ /* Fast path: return cached proc fd path if already set up */ ++ if (private_data->maskdir_proc_path != NULL) ++ { ++ *proc_fd_path = private_data->maskdir_proc_path; ++ return 0; ++ } ++ ++ /* Slow path: create directory and cache everything once */ ++ ret = get_run_directory (&run_dir, container->context->state_root, err); ++ if (UNLIKELY (ret < 0)) ++ return ret; ++ ++ ret = append_paths (&empty_dir_path, err, run_dir, ".empty-directory", NULL); ++ if (UNLIKELY (ret < 0)) ++ return ret; ++ ++ /* Ensure the empty directory exists (once per container) */ ++ ret = crun_ensure_directory (empty_dir_path, 0555, false, err); ++ if (UNLIKELY (ret < 0)) ++ return ret; ++ ++ /* Open directory and cache FD (once per container) */ ++ fd = open (empty_dir_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC); ++ if (fd < 0) ++ return crun_make_error (err, errno, "open directory `%s`", empty_dir_path); ++ ++ /* Cache the /proc/self/fd path for fast mounting */ ++ ret = xasprintf (&private_data->maskdir_proc_path, "/proc/self/fd/%d", fd); ++ if (UNLIKELY (ret < 0)) ++ return crun_make_error (err, errno, "xasprintf failed"); ++ ++ private_data->maskdir_fd = fd; ++ fd = -1; /* Don't auto-close */ ++ ++ *proc_fd_path = private_data->maskdir_proc_path; ++ return 0; ++} ++ ++static int ++mount_masked_dir (libcrun_container_t *container, int pathfd, const char *rel_path, libcrun_error_t *err) ++{ ++ struct private_data_s *private_data = get_private_data (container); ++ char *proc_fd_path = NULL; ++ libcrun_error_t tmp_err = NULL; ++ int ret; ++ ++ if (private_data->maskdir_bind_failed) ++ goto fallback_to_tmpfs; ++ ++ /* Get cached /proc/self/fd path (fast after first call) */ ++ ret = get_shared_empty_dir_cached (container, &proc_fd_path, &tmp_err); ++ if (ret < 0) ++ { ++ private_data->maskdir_bind_failed = true; ++ warn_tmpfs_fallback_once (private_data, tmp_err->msg); ++ crun_error_release (&tmp_err); ++ goto fallback_to_tmpfs; ++ } ++ ++ ret = do_mount (container, proc_fd_path, pathfd, rel_path, NULL, MS_BIND | MS_RDONLY, NULL, LABEL_MOUNT, &tmp_err); ++ if (LIKELY (ret >= 0)) ++ return ret; ++ ++ /* Bind mount failed - mark as failed and fall back for all future mounts */ ++ private_data->maskdir_bind_failed = true; ++ libcrun_warning ("bind mount failed for %s to %s: %s, falling back to tmpfs", ++ proc_fd_path, rel_path, tmp_err->msg); ++ warn_tmpfs_fallback_once (private_data, tmp_err->msg); ++ crun_error_release (&tmp_err); ++ ++fallback_to_tmpfs: ++ libcrun_debug ("using tmpfs fallback for %s", rel_path); ++ return ret = do_mount (container, "tmpfs", pathfd, rel_path, "tmpfs", MS_RDONLY, "nr_blocks=1,nr_inodes=1", LABEL_MOUNT, err); ++} ++ + static int + do_masked_or_readonly_path (libcrun_container_t *container, const char *rel_path, bool readonly, bool keep_flags, + libcrun_error_t *err) +@@ -1114,7 +1222,7 @@ do_masked_or_readonly_path (libcrun_container_t *container, const char *rel_path + return crun_make_error (err, errno, "cannot stat `%s`", rel_path); + + if ((mode & S_IFMT) == S_IFDIR) +- ret = do_mount (container, "tmpfs", pathfd, rel_path, "tmpfs", MS_RDONLY, "nr_blocks=1,nr_inodes=1", LABEL_MOUNT, err); ++ ret = mount_masked_dir (container, pathfd, rel_path, err); + else + ret = do_mount (container, "/dev/null", pathfd, rel_path, NULL, MS_BIND | MS_RDONLY, NULL, LABEL_MOUNT, err); + if (UNLIKELY (ret < 0)) +diff --git a/src/libcrun/status.c b/src/libcrun/status.c +index 714a31adc7..c786ef6ea9 100644 +--- a/src/libcrun/status.c ++++ b/src/libcrun/status.c +@@ -55,7 +55,7 @@ validate_id (const char *id, libcrun_error_t *err) + return 0; + } + +-static int ++int + get_run_directory (char **out, const char *state_root, libcrun_error_t *err) + { + int ret; +diff --git a/src/libcrun/status.h b/src/libcrun/status.h +index cd6c0ced16..72a94348a5 100644 +--- a/src/libcrun/status.h ++++ b/src/libcrun/status.h +@@ -65,6 +65,7 @@ int libcrun_status_create_exec_fifo (const char *state_root, const char *id, lib + int libcrun_status_write_exec_fifo (const char *state_root, const char *id, libcrun_error_t *err); + int libcrun_status_has_read_exec_fifo (const char *state_root, const char *id, libcrun_error_t *err); + int libcrun_check_pid_valid (libcrun_container_status_t *status, libcrun_error_t *err); ++int get_run_directory (char **out, const char *state_root, libcrun_error_t *err); + + static inline void + libcrun_free_container_listp (void *p) diff --git a/SOURCES/d8a88c0620882fbc989f29ba83d1c46fab3bca09.patch b/SOURCES/d8a88c0620882fbc989f29ba83d1c46fab3bca09.patch new file mode 100644 index 0000000..636c5eb --- /dev/null +++ b/SOURCES/d8a88c0620882fbc989f29ba83d1c46fab3bca09.patch @@ -0,0 +1,237 @@ +From d8a88c0620882fbc989f29ba83d1c46fab3bca09 Mon Sep 17 00:00:00 2001 +From: Giuseppe Scrivano +Date: Tue, 9 Sep 2025 18:16:34 +0200 +Subject: [PATCH] criu: checkpoint correctly the shared empty directory path + +commit 4004e5bed9ff52029a829131fbc16f9a877154b9 introduced the +regression. It is not part of any release. + +Signed-off-by: Giuseppe Scrivano +--- + src/libcrun/criu.c | 76 ++++++++++++++++++++++++++++++------------- + src/libcrun/linux.c | 20 ++++-------- + src/libcrun/seccomp.c | 4 +-- + src/libcrun/status.c | 22 +++++++++++++ + src/libcrun/status.h | 1 + + 5 files changed, 86 insertions(+), 37 deletions(-) + +diff --git a/src/libcrun/criu.c b/src/libcrun/criu.c +index f94c243d6e..45c1cce81b 100644 +--- a/src/libcrun/criu.c ++++ b/src/libcrun/criu.c +@@ -267,6 +267,54 @@ criu_check_mem_track (char *work_path, libcrun_error_t *err) + + # endif + ++static int ++register_masked_paths_mounts (runtime_spec_schema_config_schema *def, libcrun_container_t *container, ++ struct libcriu_wrapper_s *libcriu_wrapper, bool is_restore, libcrun_error_t *err) ++{ ++ cleanup_free char *empty_dir_path = NULL; ++ bool shared_dir_registered = false; ++ size_t i; ++ int ret; ++ ++ for (i = 0; i < def->linux->masked_paths_len; i++) ++ { ++ struct stat statbuf; ++ ret = stat (def->linux->masked_paths[i], &statbuf); ++ if (ret != 0) ++ continue; ++ ++ if (S_ISDIR (statbuf.st_mode)) ++ { ++ if (! shared_dir_registered) ++ { ++ ret = get_shared_empty_directory_path (&empty_dir_path, ++ (container->context ? container->context->state_root : NULL), err); ++ if (UNLIKELY (ret < 0)) ++ return ret; ++ ++ ret = libcriu_wrapper->criu_add_ext_mount (empty_dir_path, empty_dir_path); ++ if (UNLIKELY (ret < 0)) ++ return crun_make_error (err, -ret, "CRIU: failed adding external mount for shared empty directory `%s`", empty_dir_path); ++ ++ shared_dir_registered = true; ++ } ++ ++ ret = libcriu_wrapper->criu_add_ext_mount (def->linux->masked_paths[i], empty_dir_path); ++ if (UNLIKELY (ret < 0)) ++ return crun_make_error (err, -ret, "CRIU: failed adding external mount for masked directory `%s`", def->linux->masked_paths[i]); ++ } ++ else if (S_ISREG (statbuf.st_mode)) ++ { ++ const char *bind_target = is_restore ? "/dev/null" : def->linux->masked_paths[i]; ++ ret = libcriu_wrapper->criu_add_ext_mount (def->linux->masked_paths[i], bind_target); ++ if (UNLIKELY (ret < 0)) ++ return crun_make_error (err, -ret, "CRIU: failed adding external mount to `%s`", bind_target); ++ } ++ } ++ ++ return 0; ++} ++ + static int + restore_cgroup_v1_mount (runtime_spec_schema_config_schema *def, libcrun_error_t *err) + { +@@ -609,17 +657,9 @@ libcrun_container_checkpoint_linux_criu (libcrun_container_status_t *status, lib + } + } + +- for (i = 0; i < def->linux->masked_paths_len; i++) +- { +- struct stat statbuf; +- ret = stat (def->linux->masked_paths[i], &statbuf); +- if (ret == 0 && S_ISREG (statbuf.st_mode)) +- { +- ret = libcriu_wrapper->criu_add_ext_mount (def->linux->masked_paths[i], def->linux->masked_paths[i]); +- if (UNLIKELY (ret < 0)) +- return crun_make_error (err, -ret, "CRIU: failed adding external mount to `%s`", def->linux->masked_paths[i]); +- } +- } ++ ret = register_masked_paths_mounts (def, container, libcriu_wrapper, false, err); ++ if (UNLIKELY (ret < 0)) ++ return ret; + + /* CRIU tries to checkpoint and restore all namespaces. However, + * namespaces could be shared between containers in a pod. +@@ -947,17 +987,9 @@ libcrun_container_restore_linux_criu (libcrun_container_status_t *status, libcru + } + } + +- for (i = 0; i < def->linux->masked_paths_len; i++) +- { +- struct stat statbuf; +- ret = stat (def->linux->masked_paths[i], &statbuf); +- if (ret == 0 && S_ISREG (statbuf.st_mode)) +- { +- ret = libcriu_wrapper->criu_add_ext_mount (def->linux->masked_paths[i], "/dev/null"); +- if (UNLIKELY (ret < 0)) +- return crun_make_error (err, -ret, "CRIU: failed adding external mount to `%s`", "/dev/null"); +- } +- } ++ ret = register_masked_paths_mounts (def, container, libcriu_wrapper, true, err); ++ if (UNLIKELY (ret < 0)) ++ return ret; + + /* do realpath on root */ + bundle_cleanup = realpath (status->bundle, NULL); +diff --git a/src/libcrun/linux.c b/src/libcrun/linux.c +index ce7faa5b24..ed0d888794 100644 +--- a/src/libcrun/linux.c ++++ b/src/libcrun/linux.c +@@ -1087,7 +1087,6 @@ get_shared_empty_dir_cached (libcrun_container_t *container, char **proc_fd_path + { + struct private_data_s *private_data = get_private_data (container); + cleanup_close int fd = -1; +- cleanup_free char *run_dir = NULL; + cleanup_free char *empty_dir_path = NULL; + int ret; + +@@ -1099,16 +1098,7 @@ get_shared_empty_dir_cached (libcrun_container_t *container, char **proc_fd_path + } + + /* Slow path: create directory and cache everything once */ +- ret = get_run_directory (&run_dir, container->context->state_root, err); +- if (UNLIKELY (ret < 0)) +- return ret; +- +- ret = append_paths (&empty_dir_path, err, run_dir, ".empty-directory", NULL); +- if (UNLIKELY (ret < 0)) +- return ret; +- +- /* Ensure the empty directory exists (once per container) */ +- ret = crun_ensure_directory (empty_dir_path, 0555, false, err); ++ ret = get_shared_empty_directory_path (&empty_dir_path, container->context->state_root, err); + if (UNLIKELY (ret < 0)) + return ret; + +@@ -2674,7 +2664,9 @@ do_notify_socket (libcrun_container_t *container, const char *rootfs, libcrun_er + if (notify_socket == NULL) + return 0; + +- ret = libcrun_get_state_directory (&state_dir, container->context->state_root, container->context->id, err); ++ ret = libcrun_get_state_directory (&state_dir, ++ (container->context ? container->context->state_root : NULL), ++ container->context->id, err); + if (UNLIKELY (ret < 0)) + return ret; + +@@ -4637,7 +4629,9 @@ prepare_and_send_dev_mounts (libcrun_container_t *container, int sync_socket_hos + if (! has_userns || is_empty_string (container->context->id) || geteuid () > 0) + return send_mounts (sync_socket_host, dev_fds, how_many, def->linux->devices_len, err); + +- ret = libcrun_get_state_directory (&state_dir, container->context->state_root, container->context->id, err); ++ ret = libcrun_get_state_directory (&state_dir, ++ (container->context ? container->context->state_root : NULL), ++ container->context->id, err); + if (UNLIKELY (ret < 0)) + return ret; + +diff --git a/src/libcrun/seccomp.c b/src/libcrun/seccomp.c +index 6075c87dff..7d769093c8 100644 +--- a/src/libcrun/seccomp.c ++++ b/src/libcrun/seccomp.c +@@ -589,7 +589,7 @@ store_seccomp_cache (struct libcrun_seccomp_gen_ctx_s *ctx, libcrun_error_t *err + if (is_empty_string (ctx->checksum)) + return 0; + +- dirfd = open_rundir_dirfd (container->context->state_root, err); ++ dirfd = open_rundir_dirfd ((container->context ? container->context->state_root : NULL), err); + if (UNLIKELY (dirfd < 0)) + return dirfd; + +@@ -874,7 +874,7 @@ libcrun_open_seccomp_bpf (struct libcrun_seccomp_gen_ctx_s *ctx, int *fd, libcru + if (container == NULL || container->context == NULL) + return crun_make_error (err, EINVAL, "invalid internal state"); + +- dirfd = open_rundir_dirfd (container->context->state_root, err); ++ dirfd = open_rundir_dirfd ((container->context ? container->context->state_root : NULL), err); + if (UNLIKELY (dirfd < 0)) + return dirfd; + +diff --git a/src/libcrun/status.c b/src/libcrun/status.c +index 5e6dd63594..d57e7a3fbe 100644 +--- a/src/libcrun/status.c ++++ b/src/libcrun/status.c +@@ -85,6 +85,28 @@ get_run_directory (char **out, const char *state_root, libcrun_error_t *err) + return 0; + } + ++int ++get_shared_empty_directory_path (char **out, const char *state_root, libcrun_error_t *err) ++{ ++ cleanup_free char *run_dir = NULL; ++ int ret; ++ ++ ret = get_run_directory (&run_dir, state_root, err); ++ if (UNLIKELY (ret < 0)) ++ return ret; ++ ++ ret = append_paths (out, err, run_dir, ".empty-directory", NULL); ++ if (UNLIKELY (ret < 0)) ++ return ret; ++ ++ /* Ensure the empty directory exists */ ++ ret = crun_ensure_directory (*out, 0555, false, err); ++ if (UNLIKELY (ret < 0)) ++ return ret; ++ ++ return 0; ++} ++ + int + libcrun_get_state_directory (char **out, const char *state_root, const char *id, libcrun_error_t *err) + { +diff --git a/src/libcrun/status.h b/src/libcrun/status.h +index 994624e416..d37a8d2a02 100644 +--- a/src/libcrun/status.h ++++ b/src/libcrun/status.h +@@ -65,6 +65,7 @@ int libcrun_status_write_exec_fifo (const char *state_root, const char *id, libc + int libcrun_status_has_read_exec_fifo (const char *state_root, const char *id, libcrun_error_t *err); + int libcrun_check_pid_valid (libcrun_container_status_t *status, libcrun_error_t *err); + int get_run_directory (char **out, const char *state_root, libcrun_error_t *err); ++int get_shared_empty_directory_path (char **out, const char *state_root, libcrun_error_t *err); + + static inline void + libcrun_free_container_listp (void *p) diff --git a/SPECS/crun.spec b/SPECS/crun.spec index 253f007..79a978c 100644 --- a/SPECS/crun.spec +++ b/SPECS/crun.spec @@ -42,10 +42,12 @@ Epoch: 102 # If that's what you're reading, Version must be 0, and will be updated by Packit for # copr and koji builds. # If you're reading this on dist-git, the version is automatically filled in by Packit. -Version: 1.22 -Release: 1%{?dist} +Version: 1.23.1 +Release: 2%{?dist} URL: https://github.com/containers/%{name} Source0: %{url}/releases/download/%{version}/%{name}-%{version}.tar.zst +Patch0: https://github.com/containers/crun/pull/1859.patch +Patch1: https://github.com/containers/crun/commit/d8a88c0620882fbc989f29ba83d1c46fab3bca09.patch License: GPL-2.0-only %if %{defined golang_arches_future} ExclusiveArch: %{golang_arches_future} @@ -140,6 +142,15 @@ rm -rf %{buildroot}%{_prefix}/lib* %endif %changelog +* Fri Sep 19 2025 Jindrich Novy - 1.23.1-2 +- Backport multiple crun fixes to RHEL 9.6 +- Resolves: RHEL-115666 + +* Fri Aug 22 2025 Jindrich Novy - 1.23.1-1 +- update to https://github.com/containers/crun/releases/tag/1.23.1 +- fixes "Bump crun to 1.23.1 in RHEL 9.6" +- Resolves: RHEL-110662 + * Tue Jul 15 2025 Jindrich Novy - 1.22-1 - update to https://github.com/containers/crun/releases/tag/1.22 - Resolves: RHEL-101023