From 4320b5d6322df26d615cf49f7519ef1cf7cefa57 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Fri, 31 Mar 2023 21:15:28 +0200 Subject: [PATCH 1/3] libocispec: sync Signed-off-by: Giuseppe Scrivano --- libocispec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libocispec b/libocispec index 23aed835ee..4c2abee1b0 160000 --- a/libocispec +++ b/libocispec @@ -1 +1 @@ -Subproject commit 23aed835eed8d81d124977583551a81abe595a0c +Subproject commit 4c2abee1b08090eb4383fde4a2321a114168c0b7 From bfa4f48d5b96fb442257f9e2ba707cd4714c9a91 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 29 Mar 2023 23:45:09 +0200 Subject: [PATCH 2/3] linux: create PID namespace as part of the last step otherwise the grandchild process will be in the same PID namespace as the middle process. Signed-off-by: Giuseppe Scrivano --- src/libcrun/linux.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/libcrun/linux.c b/src/libcrun/linux.c index 6a0df093d4..719f47e099 100644 --- a/src/libcrun/linux.c +++ b/src/libcrun/linux.c @@ -4516,6 +4516,18 @@ libcrun_run_linux_container (libcrun_container_t *container, container_entrypoin first_clone_args = init_status.namespaces_to_unshare & ~(CLONE_NEWTIME | CLONE_NEWCGROUP); } + init_status.namespaces_to_unshare &= ~first_clone_args; + + /* Check if there are still namespaces that require a fork(). */ + if (init_status.namespaces_to_unshare & (CLONE_NEWPID | CLONE_NEWTIME)) + { + /* If we need to make another fork(), make sure the NEWPID is always + created as part of that. */ + first_clone_args &= ~CLONE_NEWPID; + init_status.namespaces_to_unshare |= CLONE_NEWPID; + init_status.must_fork = true; + } + pid = -1; if (cgroup_dirfd && *cgroup_dirfd->dirfd >= 0) { @@ -4541,12 +4553,6 @@ libcrun_run_linux_container (libcrun_container_t *container, container_entrypoin return crun_make_error (err, errno, "clone"); } - init_status.namespaces_to_unshare &= ~first_clone_args; - - /* Check if there are still namespaces that require a fork(). */ - if (init_status.namespaces_to_unshare & (CLONE_NEWPID | CLONE_NEWTIME)) - init_status.must_fork = true; - if (pid) { __attribute__ ((unused)) cleanup_pid pid_t pid_to_clean = pid; From bf79b090eb646eef9a8ef4c9b90d77ad227e08a4 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano Date: Wed, 29 Mar 2023 22:39:09 +0200 Subject: [PATCH 3/3] src: wire the runtime spec time namespace and drop the custom annotation. Signed-off-by: Giuseppe Scrivano --- Makefile.am | 3 +- crun.1 | 5 --- crun.1.md | 5 --- src/libcrun/linux.c | 23 ++++++++++--- tests/test_time.py | 77 ++++++++++++++++++++++++++++++++++++++++++++ tests/tests_utils.py | 4 ++- 6 files changed, 100 insertions(+), 17 deletions(-) create mode 100644 tests/test_time.py diff --git a/Makefile.am b/Makefile.am index 75d27f4d8c..3582d0fe22 100644 --- a/Makefile.am +++ b/Makefile.am @@ -179,7 +179,8 @@ PYTHON_TESTS = tests/test_capabilities.py \ tests/test_resources.py \ tests/test_start.py \ tests/test_exec.py \ - tests/test_seccomp.py + tests/test_seccomp.py \ + tests/test_time.py TESTS = $(PYTHON_TESTS) $(UNIT_TESTS) diff --git a/crun.1 b/crun.1 index 7bd5fd8477..4cddde0f25 100644 --- a/crun.1 +++ b/crun.1 @@ -583,11 +583,6 @@ chown -R the_user.the_user /sys/fs/cgroup/systemd .fi .RE -.SH \fB\fCrun.oci.timens_offset=ID SEC NSEC\fR -.PP -Specify the offset to be written to /proc/self/timens_offsets when creating -a time namespace. - .SH \fB\fCrun.oci.systemd.subgroup=SUBGROUP\fR .PP Override the name for the systemd sub cgroup created under the systemd diff --git a/crun.1.md b/crun.1.md index ff066755d3..1ed8f2be42 100644 --- a/crun.1.md +++ b/crun.1.md @@ -466,11 +466,6 @@ mount cgroup -t cgroup /sys/fs/cgroup/systemd -o none,name=systemd,xattr chown -R the_user.the_user /sys/fs/cgroup/systemd ``` -## `run.oci.timens_offset=ID SEC NSEC` - -Specify the offset to be written to /proc/self/timens_offsets when creating -a time namespace. - ## `run.oci.systemd.subgroup=SUBGROUP` Override the name for the systemd sub cgroup created under the systemd diff --git a/src/libcrun/linux.c b/src/libcrun/linux.c index 719f47e099..7ec36e41b0 100644 --- a/src/libcrun/linux.c +++ b/src/libcrun/linux.c @@ -4326,14 +4326,27 @@ init_container (libcrun_container_t *container, int sync_socket_container, struc return crun_make_error (err, errno, "unshare"); } - if (init_status->all_namespaces & CLONE_NEWTIME) + if (def->linux->time_offsets) { - const char *v = find_annotation (container, "run.oci.timens_offset"); - if (v) + char fmt_buffer[128]; + cleanup_close int fd = -1; + + fd = open ("/proc/self/timens_offsets", O_WRONLY | O_CLOEXEC); + if (UNLIKELY (fd < 0)) + return crun_make_error (err, errno, "open /proc/self/timens_offsets"); + if (def->linux->time_offsets->boottime) { - ret = write_file ("/proc/self/timens_offsets", v, strlen (v), err); + sprintf (fmt_buffer, "boottime %" PRIi64 " %" PRIu32, def->linux->time_offsets->boottime->secs, def->linux->time_offsets->boottime->nanosecs); + ret = write (fd, fmt_buffer, strlen (fmt_buffer)); if (UNLIKELY (ret < 0)) - return ret; + return crun_make_error (err, errno, "write /proc/self/timens_offsets"); + } + if (def->linux->time_offsets->monotonic) + { + sprintf (fmt_buffer, "monotonic %" PRIi64 " %" PRIu32, def->linux->time_offsets->monotonic->secs, def->linux->time_offsets->monotonic->nanosecs); + ret = write (fd, fmt_buffer, strlen (fmt_buffer)); + if (UNLIKELY (ret < 0)) + return crun_make_error (err, errno, "write /proc/self/timens_offsets"); } } diff --git a/tests/test_time.py b/tests/test_time.py new file mode 100644 index 0000000000..5f7598b6c9 --- /dev/null +++ b/tests/test_time.py @@ -0,0 +1,77 @@ +#!/bin/env python3 +# crun - OCI runtime written in C +# +# Copyright (C) 2017, 2018, 2019 Giuseppe Scrivano +# crun is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# crun is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with crun. If not, see . + +import time +import subprocess +import os +import os.path +import threading +import socket +import json +from tests_utils import * + +def test_time_namespace(): + timens_offsets = "/proc/self/timens_offsets" + + if not os.path.exists(timens_offsets): + return 77 + if is_rootless(): + return 77 + + time_offsets = { + "monotonic": { + "secs": 1, + "nanosecs": 2 + }, + "boottime": { + "secs": 3, + "nanosecs": 4 + } + } + + conf = base_config() + conf['process']['args'] = ['/init', 'cat', timens_offsets] + conf['linux']['timeOffsets'] = time_offsets + add_all_namespaces(conf,time=True) + try: + out, cid = run_and_get_output(conf, command='run') + + for line in out.split("\n"): + parts = line.split() + if len(parts) != 3: + continue + if parts[0] == "monotonic": + if parts[1] != "1": + return -1 + if parts[2] != "2": + return -1 + if parts[0] == "boottime": + if parts[1] != "3": + return -1 + if parts[2] != "4": + return -1 + return 0 + except: + return -1 + return 0 + +all_tests = { + "time-namespace": test_time_namespace, +} + +if __name__ == "__main__": + tests_main(all_tests) diff --git a/tests/tests_utils.py b/tests/tests_utils.py index 568fec682b..d3f6eb6f92 100755 --- a/tests/tests_utils.py +++ b/tests/tests_utils.py @@ -143,7 +143,7 @@ def parse_proc_status(content): r[k] = v.strip() return r -def add_all_namespaces(conf, cgroupns=False, userns=False, netns=True, ipcns=True, utsns=True, pidns=True): +def add_all_namespaces(conf, cgroupns=False, userns=False, netns=True, ipcns=True, utsns=True, pidns=True,time=False): has = {} for i in conf['linux']['namespaces']: has[i['type']] = i['type'] @@ -160,6 +160,8 @@ def add_all_namespaces(conf, cgroupns=False, userns=False, netns=True, ipcns=Tru namespaces = namespaces + ["user"] if netns: namespaces = namespaces + ["network"] + if time: + namespaces = namespaces + ["time"] for i in namespaces: if i not in has: conf['linux']['namespaces'].append({"type" : i})