From 4f785bbeecf8b0d3520309960df071e6b80b2a50 Mon Sep 17 00:00:00 2001 From: zhongjiawei Date: Fri, 24 May 2024 09:32:10 +0800 Subject: [PATCH 1/7] runc:fix CVE-2024-3154 Signed-off-by: zhongjiawei --- git-commit | 2 +- patch/0039-runc-fix-CVE-2024-3154.patch | 47 +++++++++++++++++++++++++ runc.spec | 8 ++++- series.conf | 1 + 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 patch/0039-runc-fix-CVE-2024-3154.patch diff --git a/git-commit b/git-commit index 6c73169..9e80ce5 100644 --- a/git-commit +++ b/git-commit @@ -1 +1 @@ -8fc687c6fb64e110866466035e90c718d58f2c7f +d40eb8bbaaf0365a8d2468625474125181b65c83 diff --git a/patch/0039-runc-fix-CVE-2024-3154.patch b/patch/0039-runc-fix-CVE-2024-3154.patch new file mode 100644 index 0000000..baabd2f --- /dev/null +++ b/patch/0039-runc-fix-CVE-2024-3154.patch @@ -0,0 +1,47 @@ +From 3107921b5e32c860e476fe413dcd70174dd38401 Mon Sep 17 00:00:00 2001 +From: zhongjiawei +Date: Thu, 23 May 2024 19:15:34 +0800 +Subject: [PATCH] features: implement returning + potentiallyUnsafeConfigAnnotations list + +--- + features.go | 5 +++++ + types/features/features.go | 6 ++++++ + 2 files changed, 11 insertions(+) + +diff --git a/features.go b/features.go +index c9cd15c..7f76e7a 100644 +--- a/features.go ++++ b/features.go +@@ -55,6 +55,11 @@ var featuresCommand = cli.Command{ + Enabled: &tru, + }, + }, ++ PotentiallyUnsafeConfigAnnotations: []string{ ++ "bundle", ++ "org.systemd.property.", // prefix form ++ "org.criu.config", ++ }, + } + + if seccomp.Enabled { +diff --git a/types/features/features.go b/types/features/features.go +index c6269ca..8b467f7 100644 +--- a/types/features/features.go ++++ b/types/features/features.go +@@ -25,6 +25,12 @@ type Features struct { + // Annotations contains implementation-specific annotation strings, + // such as the implementation version, and third-party extensions. + Annotations map[string]string `json:"annotations,omitempty"` ++ ++ // PotentiallyUnsafeConfigAnnotations the list of the potential unsafe annotations ++ // that may appear in `config.json`. ++ // ++ // A value that ends with "." is interpreted as a prefix of annotations. ++ PotentiallyUnsafeConfigAnnotations []string `json:"potentiallyUnsafeConfigAnnotations,omitempty"` + } + + // Linux is specific to Linux. +-- +2.33.0 + diff --git a/runc.spec b/runc.spec index 0800d73..b45d882 100644 --- a/runc.spec +++ b/runc.spec @@ -3,7 +3,7 @@ Name: runc Version: 1.1.8 -Release: 13 +Release: 14 Summary: runc is a CLI tool for spawning and running containers according to the OCI specification. License: ASL 2.0 @@ -54,6 +54,12 @@ install -p -m 755 runc $RPM_BUILD_ROOT/%{_bindir}/runc %{_bindir}/runc %changelog +* Fri May 24 2024 zhongjiawei - 1.1.8-14 +- Type:CVE +- CVE:CVE-2024-3154 +- SUG:NA +- DESC:fix CVE-2024-3154 + * Thu Feb 22 2024 zhongjiawei - 1.1.8-13 - Type:bugfix - CVE:NA diff --git a/series.conf b/series.conf index 6674de1..681bf5b 100644 --- a/series.conf +++ b/series.conf @@ -35,3 +35,4 @@ patch/0035-runc-Fix-undefined-behavior.patch patch/0036-runc-increase-the-number-of-cgroup-deletion-retries.patch patch/0037-runc-fix-CVE-2024-21626.patch patch/0038-runc-check-cmd-exist.patch +patch/0039-runc-fix-CVE-2024-3154.patch -- Gitee From c482ca6da491bc48baa12b94fc3c589ffb54ae46 Mon Sep 17 00:00:00 2001 From: zhongjiawei Date: Fri, 30 Aug 2024 11:48:13 +0800 Subject: [PATCH 2/7] runc:sync some patches Signed-off-by: zhongjiawei --- git-commit | 2 +- ...0040-add-loongarch64-seccomp-support.patch | 150 ++++++++++++++++++ patch/0042-runc-fix-a-data-race.patch | 26 +++ ...pport-set-umask-through-native.umask.patch | 48 ++++++ runc.spec | 8 +- series.conf | 3 + 6 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 patch/0040-add-loongarch64-seccomp-support.patch create mode 100644 patch/0042-runc-fix-a-data-race.patch create mode 100644 patch/0043-runc-do-not-support-set-umask-through-native.umask.patch diff --git a/git-commit b/git-commit index 9e80ce5..d2e1135 100644 --- a/git-commit +++ b/git-commit @@ -1 +1 @@ -d40eb8bbaaf0365a8d2468625474125181b65c83 +b41140eddc29b390cf030de10de67c9878d50ae6 diff --git a/patch/0040-add-loongarch64-seccomp-support.patch b/patch/0040-add-loongarch64-seccomp-support.patch new file mode 100644 index 0000000..bfa9bee --- /dev/null +++ b/patch/0040-add-loongarch64-seccomp-support.patch @@ -0,0 +1,150 @@ +diff --git a/libcontainer/seccomp/config.go b/libcontainer/seccomp/config.go +index 2b15576..841f9d9 100644 +--- a/libcontainer/seccomp/config.go ++++ b/libcontainer/seccomp/config.go +@@ -69,6 +69,7 @@ var archs = map[string]string{ + "SCMP_ARCH_RISCV64": "riscv64", + "SCMP_ARCH_S390": "s390", + "SCMP_ARCH_S390X": "s390x", ++ "SCMP_ARCH_LOONGARCH64": "loong64", + } + + // KnownArchs returns the list of the known archs. +diff --git a/libcontainer/seccomp/patchbpf/enosys_linux.go b/libcontainer/seccomp/patchbpf/enosys_linux.go +index 6376512..391c319 100644 +--- a/libcontainer/seccomp/patchbpf/enosys_linux.go ++++ b/libcontainer/seccomp/patchbpf/enosys_linux.go +@@ -75,6 +75,7 @@ const uint32_t C_AUDIT_ARCH_PPC64LE = AUDIT_ARCH_PPC64LE; + const uint32_t C_AUDIT_ARCH_S390 = AUDIT_ARCH_S390; + const uint32_t C_AUDIT_ARCH_S390X = AUDIT_ARCH_S390X; + const uint32_t C_AUDIT_ARCH_RISCV64 = AUDIT_ARCH_RISCV64; ++const uint32_t C_AUDIT_ARCH_LOONGARCH64 = AUDIT_ARCH_LOONGARCH64; + */ + import "C" + +@@ -212,6 +213,8 @@ func archToNative(arch libseccomp.ScmpArch) (nativeArch, error) { + return nativeArch(C.C_AUDIT_ARCH_S390X), nil + case libseccomp.ArchRISCV64: + return nativeArch(C.C_AUDIT_ARCH_RISCV64), nil ++ case libseccomp.ArchLOONGARCH64: ++ return nativeArch(C.C_AUDIT_ARCH_LOONGARCH64), nil + default: + return invalidArch, fmt.Errorf("unknown architecture: %v", arch) + } +diff --git a/libcontainer/seccomp/patchbpf/enosys_linux_test.go b/libcontainer/seccomp/patchbpf/enosys_linux_test.go +index e2d363a..a66fe35 100644 +--- a/libcontainer/seccomp/patchbpf/enosys_linux_test.go ++++ b/libcontainer/seccomp/patchbpf/enosys_linux_test.go +@@ -105,6 +105,7 @@ var testArches = []string{ + "ppc64le", + "s390", + "s390x", ++ "loong64", + } + + func testEnosysStub(t *testing.T, defaultAction configs.Action, arches []string) { +diff --git a/libcontainer/system/syscall_linux_64.go b/libcontainer/system/syscall_linux_64.go +index 97f1ba0..5db345a 100644 +--- a/libcontainer/system/syscall_linux_64.go ++++ b/libcontainer/system/syscall_linux_64.go +@@ -1,6 +1,6 @@ + //go:build linux && (arm64 || amd64 || mips || mipsle || mips64 || mips64le || loong64 || ppc || ppc64 || ppc64le || riscv64 || s390x) + // +build linux +-// +build arm64 amd64 mips mipsle mips64 mips64le ppc ppc64 ppc64le riscv64 s390x ++// +build arm64 amd64 mips mipsle mips64 mips64le ppc ppc64 ppc64le riscv64 s390x loong64 + + package system + +diff --git a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go +index 135f74a..e32af2f 100644 +--- a/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go ++++ b/vendor/github.com/opencontainers/runtime-spec/specs-go/config.go +@@ -643,6 +643,7 @@ const ( + ArchPARISC Arch = "SCMP_ARCH_PARISC" + ArchPARISC64 Arch = "SCMP_ARCH_PARISC64" + ArchRISCV64 Arch = "SCMP_ARCH_RISCV64" ++ ArchLOONGARCH64 Arch = "SCMP_ARCH_LOONGARCH64" + ) + + // LinuxSeccompAction taken upon Seccomp rule match +diff --git a/vendor/github.com/seccomp/libseccomp-golang/seccomp.go b/vendor/github.com/seccomp/libseccomp-golang/seccomp.go +index 8dad12f..2552394 100644 +--- a/vendor/github.com/seccomp/libseccomp-golang/seccomp.go ++++ b/vendor/github.com/seccomp/libseccomp-golang/seccomp.go +@@ -174,6 +174,8 @@ const ( + ArchPARISC64 + // ArchRISCV64 represents RISCV64 + ArchRISCV64 ++ // ArchLOONGARCH64 represents 64-bit LoongArch System syscalls ++ ArchLOONGARCH64 + ) + + const ( +@@ -305,6 +307,8 @@ func GetArchFromString(arch string) (ScmpArch, error) { + return ArchPARISC64, nil + case "riscv64": + return ArchRISCV64, nil ++ case "loongarch64", "loong64": ++ return ArchLOONGARCH64, nil + default: + return ArchInvalid, fmt.Errorf("cannot convert unrecognized string %q", arch) + } +@@ -351,6 +355,8 @@ func (a ScmpArch) String() string { + return "parisc64" + case ArchRISCV64: + return "riscv64" ++ case ArchLOONGARCH64: ++ return "loong64" + case ArchNative: + return "native" + case ArchInvalid: +diff --git a/vendor/github.com/seccomp/libseccomp-golang/seccomp_internal.go b/vendor/github.com/seccomp/libseccomp-golang/seccomp_internal.go +index df4dfb7..1997fb3 100644 +--- a/vendor/github.com/seccomp/libseccomp-golang/seccomp_internal.go ++++ b/vendor/github.com/seccomp/libseccomp-golang/seccomp_internal.go +@@ -68,6 +68,10 @@ const uint32_t C_ARCH_BAD = ARCH_BAD; + #define SCMP_ARCH_RISCV64 ARCH_BAD + #endif + ++#ifndef SCMP_ARCH_LOONGARCH64 ++#define SCMP_ARCH_LOONGARCH64 ARCH_BAD ++#endif ++ + const uint32_t C_ARCH_NATIVE = SCMP_ARCH_NATIVE; + const uint32_t C_ARCH_X86 = SCMP_ARCH_X86; + const uint32_t C_ARCH_X86_64 = SCMP_ARCH_X86_64; +@@ -88,6 +92,7 @@ const uint32_t C_ARCH_S390X = SCMP_ARCH_S390X; + const uint32_t C_ARCH_PARISC = SCMP_ARCH_PARISC; + const uint32_t C_ARCH_PARISC64 = SCMP_ARCH_PARISC64; + const uint32_t C_ARCH_RISCV64 = SCMP_ARCH_RISCV64; ++const uint32_t C_ARCH_LOONGARCH64 = SCMP_ARCH_LOONGARCH64; + + #ifndef SCMP_ACT_LOG + #define SCMP_ACT_LOG 0x7ffc0000U +@@ -291,7 +296,7 @@ const ( + scmpError C.int = -1 + // Comparison boundaries to check for architecture validity + archStart ScmpArch = ArchNative +- archEnd ScmpArch = ArchRISCV64 ++ archEnd ScmpArch = ArchLOONGARCH64 + // Comparison boundaries to check for action validity + actionStart ScmpAction = ActKillThread + actionEnd ScmpAction = ActKillProcess +@@ -551,6 +556,8 @@ func archFromNative(a C.uint32_t) (ScmpArch, error) { + return ArchPARISC64, nil + case C.C_ARCH_RISCV64: + return ArchRISCV64, nil ++ case C.C_ARCH_LOONGARCH64: ++ return ArchLOONGARCH64, nil + default: + return 0x0, fmt.Errorf("unrecognized architecture %#x", uint32(a)) + } +@@ -597,6 +604,8 @@ func (a ScmpArch) toNative() C.uint32_t { + return C.C_ARCH_PARISC64 + case ArchRISCV64: + return C.C_ARCH_RISCV64 ++ case ArchLOONGARCH64: ++ return C.C_ARCH_LOONGARCH64 + case ArchNative: + return C.C_ARCH_NATIVE + default: diff --git a/patch/0042-runc-fix-a-data-race.patch b/patch/0042-runc-fix-a-data-race.patch new file mode 100644 index 0000000..2a42c4f --- /dev/null +++ b/patch/0042-runc-fix-a-data-race.patch @@ -0,0 +1,26 @@ +From d1ef3ab619c7743d389fc882ec65df38d140fc08 Mon Sep 17 00:00:00 2001 +From: zhongjiawei +Date: Mon, 17 Jun 2024 23:22:39 +0800 +Subject: [PATCH] libct/config: fix a data race + +Reference:https://github.com/opencontainers/runc/commit/c342872276d4a3d5f662684115e282cbb20bf227 +--- + libcontainer/configs/config.go | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go +index f85ade3f..c9ecc3cb 100644 +--- a/libcontainer/configs/config.go ++++ b/libcontainer/configs/config.go +@@ -455,7 +455,7 @@ func (c Command) Run(s *specs.State) error { + return err + case <-timerCh: + cmd.Process.Kill() +- cmd.Wait() ++ <-errC + return fmt.Errorf("hook ran past specified timeout of %.1fs", c.Timeout.Seconds()) + case <-timeAfter: + if c.Timeout != nil { +-- +2.33.0 + diff --git a/patch/0043-runc-do-not-support-set-umask-through-native.umask.patch b/patch/0043-runc-do-not-support-set-umask-through-native.umask.patch new file mode 100644 index 0000000..5528a6f --- /dev/null +++ b/patch/0043-runc-do-not-support-set-umask-through-native.umask.patch @@ -0,0 +1,48 @@ +From 19a4209a82132f930fe55cbb2255eb453b465e56 Mon Sep 17 00:00:00 2001 +From: zhongjiawei +Date: Thu, 11 Jul 2024 20:18:01 +0800 +Subject: [PATCH] runc:do not support set umask through native.umask + +Signed-off-by: zhongjiawei +--- + libcontainer/rootfs_linux.go | 6 ------ + libcontainer/setns_init_linux.go | 6 ------ + 2 files changed, 12 deletions(-) + +diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go +index c42e388..499d753 100644 +--- a/libcontainer/rootfs_linux.go ++++ b/libcontainer/rootfs_linux.go +@@ -192,12 +192,6 @@ func finalizeRootfs(config *configs.Config) (err error) { + } else { + unix.Umask(0o022) + } +- umask := utils.SearchLabels(config.Labels, "native.umask") +- if umask == "normal" { +- unix.Umask(0o022) +- } else { +- unix.Umask(0o027) +- } + return nil + } + +diff --git a/libcontainer/setns_init_linux.go b/libcontainer/setns_init_linux.go +index f1dcab6..d8cdfdf 100644 +--- a/libcontainer/setns_init_linux.go ++++ b/libcontainer/setns_init_linux.go +@@ -56,12 +56,6 @@ func (l *linuxSetnsInit) Init() error { + return err + } + } +- umask := utils.SearchLabels(l.config.Config.Labels, "native.umask") +- if umask == "normal" { +- unix.Umask(0o022) +- } else { +- unix.Umask(0o027) +- } + if l.config.NoNewPrivileges { + if err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil { + return err +-- +2.33.0 + diff --git a/runc.spec b/runc.spec index b45d882..0b8c43d 100644 --- a/runc.spec +++ b/runc.spec @@ -3,7 +3,7 @@ Name: runc Version: 1.1.8 -Release: 14 +Release: 15 Summary: runc is a CLI tool for spawning and running containers according to the OCI specification. License: ASL 2.0 @@ -54,6 +54,12 @@ install -p -m 755 runc $RPM_BUILD_ROOT/%{_bindir}/runc %{_bindir}/runc %changelog +* Fri Aug 30 2024 zhongjiawei - 1.1.8-15 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:sync some patches + * Fri May 24 2024 zhongjiawei - 1.1.8-14 - Type:CVE - CVE:CVE-2024-3154 diff --git a/series.conf b/series.conf index 681bf5b..9546ca3 100644 --- a/series.conf +++ b/series.conf @@ -36,3 +36,6 @@ patch/0036-runc-increase-the-number-of-cgroup-deletion-retries.patch patch/0037-runc-fix-CVE-2024-21626.patch patch/0038-runc-check-cmd-exist.patch patch/0039-runc-fix-CVE-2024-3154.patch +patch/0040-add-loongarch64-seccomp-support.patch +patch/0042-runc-fix-a-data-race.patch +patch/0043-runc-do-not-support-set-umask-through-native.umask.patch -- Gitee From 1205521fa0df046484f12efd46475b3cf025cf3d Mon Sep 17 00:00:00 2001 From: zhongjiawei Date: Fri, 30 Aug 2024 16:04:24 +0800 Subject: [PATCH 3/7] runc:format log instead panic when procError missing payload --- git-commit | 2 +- ...instead-panic-when-procError-missing.patch | 25 +++++++++++++++++++ runc.spec | 8 +++++- series.conf | 1 + 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 patch/0044-runc-format-log-instead-panic-when-procError-missing.patch diff --git a/git-commit b/git-commit index d2e1135..ebb5ea7 100644 --- a/git-commit +++ b/git-commit @@ -1 +1 @@ -b41140eddc29b390cf030de10de67c9878d50ae6 +b5df7029488e0b42b65b5df8e23c7bd9e8884099 diff --git a/patch/0044-runc-format-log-instead-panic-when-procError-missing.patch b/patch/0044-runc-format-log-instead-panic-when-procError-missing.patch new file mode 100644 index 0000000..5699852 --- /dev/null +++ b/patch/0044-runc-format-log-instead-panic-when-procError-missing.patch @@ -0,0 +1,25 @@ +From 46b7c30e013c15fc254a17e5151f05c99da6def4 Mon Sep 17 00:00:00 2001 +From: zhongjiawei +Date: Fri, 30 Aug 2024 15:55:15 +0800 +Subject: [PATCH] runc:format log instead panic when procError missing payload + +--- + libcontainer/sync.go | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libcontainer/sync.go b/libcontainer/sync.go +index 25dc286..e6029fd 100644 +--- a/libcontainer/sync.go ++++ b/libcontainer/sync.go +@@ -115,7 +115,7 @@ func parseSync(pipe io.Reader, fn func(*syncT) error) error { + return ierr + } + // Programmer error. +- panic("No error following JSON procError payload.") ++ return errors.New("procError missing error payload") + } + + if err := fn(&sync); err != nil { +-- +2.33.0 + diff --git a/runc.spec b/runc.spec index 0b8c43d..d460949 100644 --- a/runc.spec +++ b/runc.spec @@ -3,7 +3,7 @@ Name: runc Version: 1.1.8 -Release: 15 +Release: 20 Summary: runc is a CLI tool for spawning and running containers according to the OCI specification. License: ASL 2.0 @@ -54,6 +54,12 @@ install -p -m 755 runc $RPM_BUILD_ROOT/%{_bindir}/runc %{_bindir}/runc %changelog +* Fri Aug 30 2024 zhongjiawei - 1.1.8-20 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:format log instead panic when procError missing payload + * Fri Aug 30 2024 zhongjiawei - 1.1.8-15 - Type:bugfix - CVE:NA diff --git a/series.conf b/series.conf index 9546ca3..593edb6 100644 --- a/series.conf +++ b/series.conf @@ -39,3 +39,4 @@ patch/0039-runc-fix-CVE-2024-3154.patch patch/0040-add-loongarch64-seccomp-support.patch patch/0042-runc-fix-a-data-race.patch patch/0043-runc-do-not-support-set-umask-through-native.umask.patch +patch/0044-runc-format-log-instead-panic-when-procError-missing.patch -- Gitee From 47dc31742da8b4b0cc3345f3d47d82d57dea9172 Mon Sep 17 00:00:00 2001 From: Song Zhang Date: Tue, 10 Sep 2024 15:54:14 +0800 Subject: [PATCH 4/7] runc: fix CVE-2024-45310 Signed-off-by: Song Zhang (cherry picked from commit ab4cc0729c31453147018b290e97d51db51f3c13) --- git-commit | 2 +- ...onsolidate-mountpoint-creation-logic.patch | 344 +++++++++++++++++ ...ope-MkdirAll-to-stay-inside-the-root.patch | 356 ++++++++++++++++++ runc.spec | 8 +- series.conf | 2 + 5 files changed, 710 insertions(+), 2 deletions(-) create mode 100644 patch/0045-rootfs-consolidate-mountpoint-creation-logic.patch create mode 100644 patch/0046-rootfs-try-to-scope-MkdirAll-to-stay-inside-the-root.patch diff --git a/git-commit b/git-commit index ebb5ea7..92d8930 100644 --- a/git-commit +++ b/git-commit @@ -1 +1 @@ -b5df7029488e0b42b65b5df8e23c7bd9e8884099 +1251c89d252bb9f8136d47c5892497829e78683f diff --git a/patch/0045-rootfs-consolidate-mountpoint-creation-logic.patch b/patch/0045-rootfs-consolidate-mountpoint-creation-logic.patch new file mode 100644 index 0000000..0bec2b8 --- /dev/null +++ b/patch/0045-rootfs-consolidate-mountpoint-creation-logic.patch @@ -0,0 +1,344 @@ +From 161ddbfb05c69e010d50d788533a19d51607b937 Mon Sep 17 00:00:00 2001 +From: Aleksa Sarai +Date: Mon, 1 Jul 2024 15:12:01 +1000 +Subject: [PATCH 1/2] rootfs: consolidate mountpoint creation logic + +The logic for how we create mountpoints is spread over each mountpoint +preparation function, when in reality the behaviour is pretty uniform +with only a handful of exceptions. So just move it all to one function +that is easier to understand. + +Signed-off-by: Aleksa Sarai +--- + libcontainer/container_linux.go | 28 ++---- + libcontainer/rootfs_linux.go | 160 ++++++++++++++----------------- + libcontainer/utils/utils_unix.go | 15 +++ + 3 files changed, 94 insertions(+), 109 deletions(-) + +diff --git a/libcontainer/container_linux.go b/libcontainer/container_linux.go +index 5086d50..e8b00c8 100644 +--- a/libcontainer/container_linux.go ++++ b/libcontainer/container_linux.go +@@ -1278,8 +1278,7 @@ func (c *linuxContainer) restoreNetwork(req *criurpc.CriuReq, criuOpts *CriuOpts + // restore using CRIU. This function is inspired from the code in + // rootfs_linux.go + func (c *linuxContainer) makeCriuRestoreMountpoints(m *configs.Mount) error { +- switch m.Device { +- case "cgroup": ++ if m.Device == "cgroup" { + // No mount point(s) need to be created: + // + // * for v1, mount points are saved by CRIU because +@@ -1288,26 +1287,11 @@ func (c *linuxContainer) makeCriuRestoreMountpoints(m *configs.Mount) error { + // * for v2, /sys/fs/cgroup is a real mount, but + // the mountpoint appears as soon as /sys is mounted + return nil +- case "bind": +- // The prepareBindMount() function checks if source +- // exists. So it cannot be used for other filesystem types. +- // TODO: pass something else than nil? Not sure if criu is +- // impacted by issue #2484 +- if err := prepareBindMount(m, c.config.Rootfs, nil); err != nil { +- return err +- } +- default: +- // for all other filesystems just create the mountpoints +- dest, err := securejoin.SecureJoin(c.config.Rootfs, m.Destination) +- if err != nil { +- return err +- } +- if err := checkProcMount(c.config.Rootfs, dest, ""); err != nil { +- return err +- } +- if err := os.MkdirAll(dest, 0o755); err != nil { +- return err +- } ++ } ++ // TODO: pass something else than nil? Not sure if criu is ++ // impacted by issue #2484 ++ if _, err := createMountpoint(c.config.Rootfs, m, nil, ""); err != nil { ++ return fmt.Errorf("create criu restore mount for %s mount: %w", m.Destination, err) + } + return nil + } +diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go +index 499d753..ea554d3 100644 +--- a/libcontainer/rootfs_linux.go ++++ b/libcontainer/rootfs_linux.go +@@ -225,36 +225,6 @@ func mountCmd(cmd configs.Command) error { + return nil + } + +-func prepareBindMount(m *configs.Mount, rootfs string, mountFd *int) error { +- source := m.Source +- if mountFd != nil { +- source = "/proc/self/fd/" + strconv.Itoa(*mountFd) +- } +- +- stat, err := os.Stat(source) +- if err != nil { +- // error out if the source of a bind mount does not exist as we will be +- // unable to bind anything to it. +- return err +- } +- // ensure that the destination of the bind mount is resolved of symlinks at mount time because +- // any previous mounts can invalidate the next mount's destination. +- // this can happen when a user specifies mounts within other mounts to cause breakouts or other +- // evil stuff to try to escape the container's rootfs. +- var dest string +- if dest, err = securejoin.SecureJoin(rootfs, m.Destination); err != nil { +- return err +- } +- if err := checkProcMount(rootfs, dest, source); err != nil { +- return err +- } +- if err := createIfNotExists(dest, stat.IsDir()); err != nil { +- return err +- } +- +- return nil +-} +- + func mountCgroupV1(m *configs.Mount, c *mountConfig) error { + binds, err := getCgroupMounts(m) + if err != nil { +@@ -283,6 +253,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error { + for _, b := range binds { + if c.cgroupns { + subsystemPath := filepath.Join(c.root, b.Destination) ++ subsystemName := filepath.Base(b.Destination) + if err := os.MkdirAll(subsystemPath, 0o755); err != nil { + return err + } +@@ -293,7 +264,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error { + } + var ( + source = "cgroup" +- data = filepath.Base(subsystemPath) ++ data = subsystemName + ) + if data == "systemd" { + data = cgroups.CgroupNamePrefix + data +@@ -323,14 +294,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error { + } + + func mountCgroupV2(m *configs.Mount, c *mountConfig) error { +- dest, err := securejoin.SecureJoin(c.root, m.Destination) +- if err != nil { +- return err +- } +- if err := os.MkdirAll(dest, 0o755); err != nil { +- return err +- } +- err = utils.WithProcfd(c.root, m.Destination, func(procfd string) error { ++ err := utils.WithProcfd(c.root, m.Destination, func(procfd string) error { + return mount(m.Source, m.Destination, procfd, "cgroup2", uintptr(m.Flags), m.Data) + }) + if err == nil || !(errors.Is(err, unix.EPERM) || errors.Is(err, unix.EBUSY)) { +@@ -412,6 +376,70 @@ func doTmpfsCopyUp(m *configs.Mount, rootfs, mountLabel string) (Err error) { + }) + } + ++var errRootfsToFile = errors.New("config tries to change rootfs to file") ++ ++func createMountpoint(rootfs string, m *configs.Mount, mountFd *int, source string) (string, error) { ++ dest, err := securejoin.SecureJoin(rootfs, m.Destination) ++ if err != nil { ++ return "", err ++ } ++ if err := checkProcMount(rootfs, dest, m, source); err != nil { ++ return "", fmt.Errorf("check proc-safety of %s mount: %w", m.Destination, err) ++ } ++ ++ switch m.Device { ++ case "bind": ++ source := m.Source ++ if mountFd != nil { ++ source = "/proc/self/fd/" + strconv.Itoa(*mountFd) ++ } ++ ++ fi, err := os.Stat(source) ++ if err != nil { ++ // Error out if the source of a bind mount does not exist as we ++ // will be unable to bind anything to it. ++ return "", fmt.Errorf("bind mount source stat: %w", err) ++ } ++ // If the original source is not a directory, make the target a file. ++ if !fi.IsDir() { ++ // Make sure we aren't tricked into trying to make the root a file. ++ if rootfs == dest { ++ return "", fmt.Errorf("%w: file bind mount over rootfs", errRootfsToFile) ++ } ++ // Make the parent directory. ++ if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { ++ return "", fmt.Errorf("make parent dir of file bind-mount: %w", err) ++ } ++ // Make the target file. ++ f, err := os.OpenFile(dest, os.O_CREATE, 0o755) ++ if err != nil { ++ return "", fmt.Errorf("create target of file bind-mount: %w", err) ++ } ++ _ = f.Close() ++ // Nothing left to do. ++ return dest, nil ++ } ++ ++ case "tmpfs": ++ // If the original target exists, copy the mode for the tmpfs mount. ++ if stat, err := os.Stat(dest); err == nil { ++ dt := fmt.Sprintf("mode=%04o", syscallMode(stat.Mode())) ++ if m.Data != "" { ++ dt = dt + "," + m.Data ++ } ++ m.Data = dt ++ ++ // Nothing left to do. ++ return dest, nil ++ } ++ } ++ ++ if err := os.MkdirAll(dest, 0o755); err != nil { ++ return "", err ++ } ++ return dest, nil ++} ++ + func mountToRootfs(m *configs.Mount, c *mountConfig) error { + rootfs := c.root + +@@ -446,46 +474,27 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) error { + return mountPropagate(m, rootfs, "", nil) + } + +- mountLabel := c.label + mountFd := c.fd +- dest, err := securejoin.SecureJoin(rootfs, m.Destination) ++ dest, err := createMountpoint(rootfs, m, mountFd, m.Source) + if err != nil { +- return err ++ return fmt.Errorf("create mount destination for %s mount: %w", m.Destination, err) + } ++ mountLabel := c.label + + switch m.Device { + case "mqueue": +- if err := os.MkdirAll(dest, 0o755); err != nil { +- return err +- } + if err := mountPropagate(m, rootfs, "", nil); err != nil { + return err + } + return label.SetFileLabel(dest, mountLabel) + case "tmpfs": +- if stat, err := os.Stat(dest); err != nil { +- if err := os.MkdirAll(dest, 0o755); err != nil { +- return err +- } +- } else { +- dt := fmt.Sprintf("mode=%04o", stat.Mode()) +- if m.Data != "" { +- dt = dt + "," + m.Data +- } +- m.Data = dt +- } +- + if m.Extensions&configs.EXT_COPYUP == configs.EXT_COPYUP { + err = doTmpfsCopyUp(m, rootfs, mountLabel) + } else { + err = mountPropagate(m, rootfs, mountLabel, nil) + } +- + return err + case "bind": +- if err := prepareBindMount(m, rootfs, mountFd); err != nil { +- return err +- } + if err := mountPropagate(m, rootfs, mountLabel, mountFd); err != nil { + return err + } +@@ -513,12 +522,6 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) error { + } + return mountCgroupV1(m, c) + default: +- if err := checkProcMount(rootfs, dest, m.Source); err != nil { +- return err +- } +- if err := os.MkdirAll(dest, 0o755); err != nil { +- return err +- } + return mountPropagate(m, rootfs, mountLabel, mountFd) + } + if err := setRecAttr(m, rootfs); err != nil { +@@ -729,6 +732,9 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error { + if err != nil { + return err + } ++ if dest == rootfs { ++ return fmt.Errorf("%w: mknod over rootfs", errRootfsToFile) ++ } + if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { + return err + } +@@ -995,26 +1001,6 @@ func chroot() error { + return nil + } + +-// createIfNotExists creates a file or a directory only if it does not already exist. +-func createIfNotExists(path string, isDir bool) error { +- if _, err := os.Stat(path); err != nil { +- if os.IsNotExist(err) { +- if isDir { +- return os.MkdirAll(path, 0o755) +- } +- if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { +- return err +- } +- f, err := os.OpenFile(path, os.O_CREATE, 0o755) +- if err != nil { +- return err +- } +- _ = f.Close() +- } +- } +- return nil +-} +- + // readonlyPath will make a path read only. + func readonlyPath(path string) error { + if err := mount(path, path, "", "", unix.MS_BIND|unix.MS_REC, ""); err != nil { +diff --git a/libcontainer/utils/utils_unix.go b/libcontainer/utils/utils_unix.go +index f57f087..6fe0096 100644 +--- a/libcontainer/utils/utils_unix.go ++++ b/libcontainer/utils/utils_unix.go +@@ -10,6 +10,7 @@ import ( + "path/filepath" + "runtime" + "strconv" ++ "strings" + "sync" + _ "unsafe" // for go:linkname + +@@ -156,6 +157,20 @@ func NewSockPair(name string) (parent, child *os.File, err error) { + return os.NewFile(uintptr(fds[1]), name+"-p"), os.NewFile(uintptr(fds[0]), name+"-c"), nil + } + ++// IsLexicallyInRoot is shorthand for strings.HasPrefix(path+"/", root+"/"), ++// but properly handling the case where path or root are "/". ++// ++// NOTE: The return value only make sense if the path doesn't contain "..". ++func IsLexicallyInRoot(root, path string) bool { ++ if root != "/" { ++ root += "/" ++ } ++ if path != "/" { ++ path += "/" ++ } ++ return strings.HasPrefix(path, root) ++} ++ + // WithProcfd runs the passed closure with a procfd path (/proc/self/fd/...) + // corresponding to the unsafePath resolved within the root. Before passing the + // fd, this path is verified to have been inside the root -- so operating on it +-- +2.33.0 + diff --git a/patch/0046-rootfs-try-to-scope-MkdirAll-to-stay-inside-the-root.patch b/patch/0046-rootfs-try-to-scope-MkdirAll-to-stay-inside-the-root.patch new file mode 100644 index 0000000..7376329 --- /dev/null +++ b/patch/0046-rootfs-try-to-scope-MkdirAll-to-stay-inside-the-root.patch @@ -0,0 +1,356 @@ +From ef3e5b4ea8f0b2eaac05df8b16f6656aebf05998 Mon Sep 17 00:00:00 2001 +From: Aleksa Sarai +Date: Tue, 2 Jul 2024 20:58:43 +1000 +Subject: [PATCH 2/2] rootfs: try to scope MkdirAll to stay inside the rootfs + +While we use SecureJoin to try to make all of our target paths inside +the container safe, SecureJoin is not safe against an attacker than can +change the path after we "resolve" it. + +os.MkdirAll can inadvertently follow symlinks and thus an attacker could +end up tricking runc into creating empty directories on the host (note +that the container doesn't get access to these directories, and the host +just sees empty directories). However, this could potentially cause DoS +issues by (for instance) creating a directory in a conf.d directory for +a daemon that doesn't handle subdirectories properly. + +In addition, the handling for creating file bind-mounts did a plain +open(O_CREAT) on the SecureJoin'd path, which is even more obviously +unsafe (luckily we didn't use O_TRUNC, or this bug could've allowed an +attacker to cause data loss...). Regardless of the symlink issue, +opening an untrusted file could result in a DoS if the file is a hung +tty or some other "nasty" file. We can use mknodat to safely create a +regular file without opening anything anyway (O_CREAT|O_EXCL would also +work but it makes the logic a bit more complicated, and we don't want to +open the file for any particular reason anyway). + +libpathrs[1] is the long-term solution for these kinds of problems, but +for now we can patch this particular issue by creating a more restricted +MkdirAll that refuses to resolve symlinks and does the creation using +file descriptors. This is loosely based on a more secure version that +filepath-securejoin now has[2] and will be added to libpathrs soon[3]. + +[1]: https://github.com/openSUSE/libpathrs +[2]: https://github.com/cyphar/filepath-securejoin/releases/tag/v0.3.0 +[3]: https://github.com/openSUSE/libpathrs/issues/10 + +Fixes: CVE-2024-45310 +Signed-off-by: Aleksa Sarai +--- + libcontainer/mount_linux.go | 18 +++++ + libcontainer/rootfs_linux.go | 33 ++++++--- + libcontainer/system/linux.go | 41 +++++++++++ + libcontainer/utils/utils_unix.go | 112 +++++++++++++++++++++++++++++++ + 4 files changed, 193 insertions(+), 11 deletions(-) + +diff --git a/libcontainer/mount_linux.go b/libcontainer/mount_linux.go +index 5f49de9..948b6c0 100644 +--- a/libcontainer/mount_linux.go ++++ b/libcontainer/mount_linux.go +@@ -1,6 +1,7 @@ + package libcontainer + + import ( ++ "io/fs" + "strconv" + + "golang.org/x/sys/unix" +@@ -81,3 +82,20 @@ func unmount(target string, flags int) error { + } + return nil + } ++ ++// syscallMode returns the syscall-specific mode bits from Go's portable mode bits. ++// Copy from https://cs.opensource.google/go/go/+/refs/tags/go1.20.7:src/os/file_posix.go;l=61-75 ++func syscallMode(i fs.FileMode) (o uint32) { ++ o |= uint32(i.Perm()) ++ if i&fs.ModeSetuid != 0 { ++ o |= unix.S_ISUID ++ } ++ if i&fs.ModeSetgid != 0 { ++ o |= unix.S_ISGID ++ } ++ if i&fs.ModeSticky != 0 { ++ o |= unix.S_ISVTX ++ } ++ // No mapping for Go's ModeTemporary (plan9 only). ++ return ++} +diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go +index ea554d3..4678c76 100644 +--- a/libcontainer/rootfs_linux.go ++++ b/libcontainer/rootfs_linux.go +@@ -254,7 +254,7 @@ func mountCgroupV1(m *configs.Mount, c *mountConfig) error { + if c.cgroupns { + subsystemPath := filepath.Join(c.root, b.Destination) + subsystemName := filepath.Base(b.Destination) +- if err := os.MkdirAll(subsystemPath, 0o755); err != nil { ++ if err := utils.MkdirAllInRoot(c.root, subsystemPath, 0o755); err != nil { + return err + } + if err := utils.WithProcfd(c.root, b.Destination, func(procfd string) error { +@@ -383,7 +383,7 @@ func createMountpoint(rootfs string, m *configs.Mount, mountFd *int, source stri + if err != nil { + return "", err + } +- if err := checkProcMount(rootfs, dest, m, source); err != nil { ++ if err := checkProcMount(rootfs, dest, source); err != nil { + return "", fmt.Errorf("check proc-safety of %s mount: %w", m.Destination, err) + } + +@@ -407,15 +407,26 @@ func createMountpoint(rootfs string, m *configs.Mount, mountFd *int, source stri + return "", fmt.Errorf("%w: file bind mount over rootfs", errRootfsToFile) + } + // Make the parent directory. +- if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { ++ destDir, destBase := filepath.Split(dest) ++ destDirFd, err := utils.MkdirAllInRootOpen(rootfs, destDir, 0o755) ++ if err != nil { + return "", fmt.Errorf("make parent dir of file bind-mount: %w", err) + } +- // Make the target file. +- f, err := os.OpenFile(dest, os.O_CREATE, 0o755) +- if err != nil { +- return "", fmt.Errorf("create target of file bind-mount: %w", err) ++ defer destDirFd.Close() ++ // Make the target file. We want to avoid opening any file that is ++ // already there because it could be a "bad" file like an invalid ++ // device or hung tty that might cause a DoS, so we use mknodat. ++ // destBase does not contain any "/" components, and mknodat does ++ // not follow trailing symlinks, so we can safely just call mknodat ++ // here. ++ if err := unix.Mknodat(int(destDirFd.Fd()), destBase, unix.S_IFREG|0o644, 0); err != nil { ++ // If we get EEXIST, there was already an inode there and ++ // we can consider that a success. ++ if !errors.Is(err, unix.EEXIST) { ++ err = &os.PathError{Op: "mknod regular file", Path: dest, Err: err} ++ return "", fmt.Errorf("create target of file bind-mount: %w", err) ++ } + } +- _ = f.Close() + // Nothing left to do. + return dest, nil + } +@@ -434,7 +445,7 @@ func createMountpoint(rootfs string, m *configs.Mount, mountFd *int, source stri + } + } + +- if err := os.MkdirAll(dest, 0o755); err != nil { ++ if err := utils.MkdirAllInRoot(rootfs, dest, 0o755); err != nil { + return "", err + } + return dest, nil +@@ -467,7 +478,7 @@ func mountToRootfs(m *configs.Mount, c *mountConfig) error { + if strings.HasPrefix(m.Destination, "/proc/sys/") { + return nil + } +- if err := os.MkdirAll(dest, 0o755); err != nil { ++ if err := utils.MkdirAllInRoot(rootfs, dest, 0o755); err != nil { + return err + } + // Selinux kernels do not support labeling of /proc or /sys. +@@ -735,7 +746,7 @@ func createDeviceNode(rootfs string, node *devices.Device, bind bool) error { + if dest == rootfs { + return fmt.Errorf("%w: mknod over rootfs", errRootfsToFile) + } +- if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { ++ if err := utils.MkdirAllInRoot(rootfs, filepath.Dir(dest), 0o755); err != nil { + return err + } + if bind { +diff --git a/libcontainer/system/linux.go b/libcontainer/system/linux.go +index e1d6eb1..0f97045 100644 +--- a/libcontainer/system/linux.go ++++ b/libcontainer/system/linux.go +@@ -6,6 +6,8 @@ package system + import ( + "os" + "os/exec" ++ "runtime" ++ "strings" + "unsafe" + + "golang.org/x/sys/unix" +@@ -102,3 +104,42 @@ func GetSubreaper() (int, error) { + + return int(i), nil + } ++ ++func prepareAt(dir *os.File, path string) (int, string) { ++ if dir == nil { ++ return unix.AT_FDCWD, path ++ } ++ ++ // Rather than just filepath.Join-ing path here, do it manually so the ++ // error and handle correctly indicate cases like path=".." as being ++ // relative to the correct directory. The handle.Name() might end up being ++ // wrong but because this is (currently) only used in MkdirAllInRoot, that ++ // isn't a problem. ++ dirName := dir.Name() ++ if !strings.HasSuffix(dirName, "/") { ++ dirName += "/" ++ } ++ fullPath := dirName + path ++ ++ return int(dir.Fd()), fullPath ++} ++ ++func Openat(dir *os.File, path string, flags int, mode uint32) (*os.File, error) { ++ dirFd, fullPath := prepareAt(dir, path) ++ fd, err := unix.Openat(dirFd, path, flags, mode) ++ if err != nil { ++ return nil, &os.PathError{Op: "openat", Path: fullPath, Err: err} ++ } ++ runtime.KeepAlive(dir) ++ return os.NewFile(uintptr(fd), fullPath), nil ++} ++ ++func Mkdirat(dir *os.File, path string, mode uint32) error { ++ dirFd, fullPath := prepareAt(dir, path) ++ err := unix.Mkdirat(dirFd, path, mode) ++ if err != nil { ++ err = &os.PathError{Op: "mkdirat", Path: fullPath, Err: err} ++ } ++ runtime.KeepAlive(dir) ++ return err ++} +diff --git a/libcontainer/utils/utils_unix.go b/libcontainer/utils/utils_unix.go +index 6fe0096..66d12e5 100644 +--- a/libcontainer/utils/utils_unix.go ++++ b/libcontainer/utils/utils_unix.go +@@ -4,6 +4,7 @@ + package utils + + import ( ++ "errors" + "fmt" + "math" + "os" +@@ -14,6 +15,8 @@ import ( + "sync" + _ "unsafe" // for go:linkname + ++ "github.com/opencontainers/runc/libcontainer/system" ++ + securejoin "github.com/cyphar/filepath-securejoin" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" +@@ -171,6 +174,115 @@ func IsLexicallyInRoot(root, path string) bool { + return strings.HasPrefix(path, root) + } + ++// MkdirAllInRootOpen attempts to make ++// ++// path, _ := securejoin.SecureJoin(root, unsafePath) ++// os.MkdirAll(path, mode) ++// os.Open(path) ++// ++// safer against attacks where components in the path are changed between ++// SecureJoin returning and MkdirAll (or Open) being called. In particular, we ++// try to detect any symlink components in the path while we are doing the ++// MkdirAll. ++// ++// NOTE: Unlike os.MkdirAll, mode is not Go's os.FileMode, it is the unix mode ++// (the suid/sgid/sticky bits are not the same as for os.FileMode). ++// ++// NOTE: If unsafePath is a subpath of root, we assume that you have already ++// called SecureJoin and so we use the provided path verbatim without resolving ++// any symlinks (this is done in a way that avoids symlink-exchange races). ++// This means that the path also must not contain ".." elements, otherwise an ++// error will occur. ++// ++// This is a somewhat less safe alternative to ++// , but it should ++// detect attempts to trick us into creating directories outside of the root. ++// We should migrate to securejoin.MkdirAll once it is merged. ++func MkdirAllInRootOpen(root, unsafePath string, mode uint32) (_ *os.File, Err error) { ++ // If the path is already "within" the root, use it verbatim. ++ fullPath := unsafePath ++ if !IsLexicallyInRoot(root, unsafePath) { ++ var err error ++ fullPath, err = securejoin.SecureJoin(root, unsafePath) ++ if err != nil { ++ return nil, err ++ } ++ } ++ subPath, err := filepath.Rel(root, fullPath) ++ if err != nil { ++ return nil, err ++ } ++ ++ // Check for any silly mode bits. ++ if mode&^0o7777 != 0 { ++ return nil, fmt.Errorf("tried to include non-mode bits in MkdirAll mode: 0o%.3o", mode) ++ } ++ ++ currentDir, err := os.OpenFile(root, unix.O_DIRECTORY|unix.O_CLOEXEC, 0) ++ if err != nil { ++ return nil, fmt.Errorf("open root handle: %w", err) ++ } ++ defer func() { ++ if Err != nil { ++ currentDir.Close() ++ } ++ }() ++ ++ for _, part := range strings.Split(subPath, string(filepath.Separator)) { ++ switch part { ++ case "", ".": ++ // Skip over no-op components. ++ continue ++ case "..": ++ return nil, fmt.Errorf("possible breakout detected: found %q component in SecureJoin subpath %s", part, subPath) ++ } ++ ++ nextDir, err := system.Openat(currentDir, part, unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) ++ switch { ++ case err == nil: ++ // Update the currentDir. ++ _ = currentDir.Close() ++ currentDir = nextDir ++ ++ case errors.Is(err, unix.ENOTDIR): ++ // This might be a symlink or some other random file. Either way, ++ // error out. ++ return nil, fmt.Errorf("cannot mkdir in %s/%s: %w", currentDir.Name(), part, unix.ENOTDIR) ++ ++ case errors.Is(err, os.ErrNotExist): ++ // Luckily, mkdirat will not follow trailing symlinks, so this is ++ // safe to do as-is. ++ if err := system.Mkdirat(currentDir, part, mode); err != nil { ++ return nil, err ++ } ++ // Open the new directory. There is a race here where an attacker ++ // could swap the directory with a different directory, but ++ // MkdirAll's fuzzy semantics mean we don't care about that. ++ nextDir, err := system.Openat(currentDir, part, unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) ++ if err != nil { ++ return nil, fmt.Errorf("open newly created directory: %w", err) ++ } ++ // Update the currentDir. ++ _ = currentDir.Close() ++ currentDir = nextDir ++ ++ default: ++ return nil, err ++ } ++ } ++ return currentDir, nil ++} ++ ++// MkdirAllInRoot is a wrapper around MkdirAllInRootOpen which closes the ++// returned handle, for callers that don't need to use it. ++func MkdirAllInRoot(root, unsafePath string, mode uint32) error { ++ f, err := MkdirAllInRootOpen(root, unsafePath, mode) ++ if err == nil { ++ _ = f.Close() ++ } ++ return err ++} ++ + // WithProcfd runs the passed closure with a procfd path (/proc/self/fd/...) + // corresponding to the unsafePath resolved within the root. Before passing the + // fd, this path is verified to have been inside the root -- so operating on it +-- +2.33.0 + diff --git a/runc.spec b/runc.spec index d460949..9118128 100644 --- a/runc.spec +++ b/runc.spec @@ -3,7 +3,7 @@ Name: runc Version: 1.1.8 -Release: 20 +Release: 21 Summary: runc is a CLI tool for spawning and running containers according to the OCI specification. License: ASL 2.0 @@ -54,6 +54,12 @@ install -p -m 755 runc $RPM_BUILD_ROOT/%{_bindir}/runc %{_bindir}/runc %changelog +* Tue Sep 10 2024 Song Zhang - 1.1.8-21 +- Type:CVE +- CVE:CVE-2024-45310 +- SUG:NA +- DESC:fix CVE-2024-45310 + * Fri Aug 30 2024 zhongjiawei - 1.1.8-20 - Type:bugfix - CVE:NA diff --git a/series.conf b/series.conf index 593edb6..9d22af3 100644 --- a/series.conf +++ b/series.conf @@ -40,3 +40,5 @@ patch/0040-add-loongarch64-seccomp-support.patch patch/0042-runc-fix-a-data-race.patch patch/0043-runc-do-not-support-set-umask-through-native.umask.patch patch/0044-runc-format-log-instead-panic-when-procError-missing.patch +patch/0045-rootfs-consolidate-mountpoint-creation-logic.patch +patch/0046-rootfs-try-to-scope-MkdirAll-to-stay-inside-the-root.patch -- Gitee From ac7de49ee4abf5e907602e7d0d9c3e9796cf3cca Mon Sep 17 00:00:00 2001 From: zhongjiawei Date: Sun, 29 Sep 2024 15:09:07 +0800 Subject: [PATCH 5/7] runc:fix can't set cpuset-cpus and cpuset-mems at the same time (cherry picked from commit 970358fda95448452400a3c1f6da54250db5ee61) --- git-commit | 2 +- ...et-cpuset-cpus-and-cpuset-mems-at-th.patch | 42 +++++++++++++++++++ runc.spec | 8 +++- series.conf | 1 + 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 patch/0047-runc-fix-can-t-set-cpuset-cpus-and-cpuset-mems-at-th.patch diff --git a/git-commit b/git-commit index 92d8930..563348a 100644 --- a/git-commit +++ b/git-commit @@ -1 +1 @@ -1251c89d252bb9f8136d47c5892497829e78683f +1b7091b305556e0de2c50f193cd7bf50af035c01 diff --git a/patch/0047-runc-fix-can-t-set-cpuset-cpus-and-cpuset-mems-at-th.patch b/patch/0047-runc-fix-can-t-set-cpuset-cpus-and-cpuset-mems-at-th.patch new file mode 100644 index 0000000..a2a2dc3 --- /dev/null +++ b/patch/0047-runc-fix-can-t-set-cpuset-cpus-and-cpuset-mems-at-th.patch @@ -0,0 +1,42 @@ +From 316c33ea56636e2e19be332362973b7def6d1a2a Mon Sep 17 00:00:00 2001 +From: zhongjiawei +Date: Fri, 27 Sep 2024 14:11:49 +0800 +Subject: [PATCH] runc:fix can't set cpuset-cpus and cpuset-mems at the same + time + +--- + libcontainer/cgroups/fs/cpuset.go | 4 +++- + libcontainer/cgroups/fs/cpuset_test.go | 2 +- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/libcontainer/cgroups/fs/cpuset.go b/libcontainer/cgroups/fs/cpuset.go +index 32719600..d33e9f91 100644 +--- a/libcontainer/cgroups/fs/cpuset.go ++++ b/libcontainer/cgroups/fs/cpuset.go +@@ -146,7 +146,9 @@ func (s *CpusetGroup) setCpuset(path, cpuset string) error { + func (s *CpusetGroup) Set(path string, r *configs.Resources) error { + var ret error + if r.CpusetCpus != "" { +- return s.setCpuset(path, r.CpusetCpus) ++ if err := s.setCpuset(path, r.CpusetCpus); err != nil { ++ return err ++ } + } + if r.CpusetMems != "" { + if err := cgroups.WriteFile(path, "cpuset.mems", r.CpusetMems); err != nil { +diff --git a/libcontainer/cgroups/fs/cpuset_test.go b/libcontainer/cgroups/fs/cpuset_test.go +index 0c72c7db..a8a6caae 100644 +--- a/libcontainer/cgroups/fs/cpuset_test.go ++++ b/libcontainer/cgroups/fs/cpuset_test.go +@@ -81,7 +81,7 @@ type cpusetTestCase struct { + + func getCpusetTestcaes() []cpusetTestCase { + testDir := "/sys/fs/cgroup/cpuset/runc-test" +- errStr1 := "failed to set" ++ errStr1 := "failed to stat" + errStr2 := "invalid cpuset format" + errStr3 := "invalid preferred_cpus" + return []cpusetTestCase{ +-- +2.33.0 + diff --git a/runc.spec b/runc.spec index 9118128..f99a6e5 100644 --- a/runc.spec +++ b/runc.spec @@ -3,7 +3,7 @@ Name: runc Version: 1.1.8 -Release: 21 +Release: 24 Summary: runc is a CLI tool for spawning and running containers according to the OCI specification. License: ASL 2.0 @@ -54,6 +54,12 @@ install -p -m 755 runc $RPM_BUILD_ROOT/%{_bindir}/runc %{_bindir}/runc %changelog +* Sun Sep 29 2024 zhongjiawei - 1.1.8-24 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:fix can't set cpuset-cpus and cpuset-mems at the same time + * Tue Sep 10 2024 Song Zhang - 1.1.8-21 - Type:CVE - CVE:CVE-2024-45310 diff --git a/series.conf b/series.conf index 9d22af3..5841a44 100644 --- a/series.conf +++ b/series.conf @@ -42,3 +42,4 @@ patch/0043-runc-do-not-support-set-umask-through-native.umask.patch patch/0044-runc-format-log-instead-panic-when-procError-missing.patch patch/0045-rootfs-consolidate-mountpoint-creation-logic.patch patch/0046-rootfs-try-to-scope-MkdirAll-to-stay-inside-the-root.patch +patch/0047-runc-fix-can-t-set-cpuset-cpus-and-cpuset-mems-at-th.patch -- Gitee From 469421d89f2afed433c9cca96909db16061be689 Mon Sep 17 00:00:00 2001 From: Wenlong Zhang Date: Tue, 15 Oct 2024 21:04:11 +0800 Subject: [PATCH 6/7] fix build error on loongarch64 --- runc.spec | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/runc.spec b/runc.spec index f99a6e5..2da6444 100644 --- a/runc.spec +++ b/runc.spec @@ -3,7 +3,7 @@ Name: runc Version: 1.1.8 -Release: 24 +Release: 25 Summary: runc is a CLI tool for spawning and running containers according to the OCI specification. License: ASL 2.0 @@ -40,6 +40,9 @@ export GO111MODULE=off export GOPATH=`pwd`/.gopath ln -sf `pwd` .gopath/src/github.com/opencontainers/runc cd .gopath/src/github.com/opencontainers/runc +%ifarch loongarch64 +sed '1s/mips64le/mips64le || loong64/g' -i libcontainer/system/syscall_linux_64.go +%endif make BUILDTAGS="seccomp selinux" static rm -rf .gopath strip runc @@ -54,6 +57,9 @@ install -p -m 755 runc $RPM_BUILD_ROOT/%{_bindir}/runc %{_bindir}/runc %changelog +* Tue Oct 15 2024 Wenlong Zhang - 1.1.8-25 +- fix build error on loongarch64 + * Sun Sep 29 2024 zhongjiawei - 1.1.8-24 - Type:bugfix - CVE:NA -- Gitee From f12f7966a70121ee2cc18532bf473f9c71394642 Mon Sep 17 00:00:00 2001 From: dongyuzhen Date: Wed, 26 Mar 2025 10:02:09 +0800 Subject: [PATCH 7/7] runc:allowing libct/cap to work with nil capabilities --- git-commit | 2 +- patch/0048-runc-libct-cap-allow-New-nil.patch | 59 +++++++++++++++++++ runc.spec | 8 ++- series.conf | 1 + 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 patch/0048-runc-libct-cap-allow-New-nil.patch diff --git a/git-commit b/git-commit index 563348a..b59dc61 100644 --- a/git-commit +++ b/git-commit @@ -1 +1 @@ -1b7091b305556e0de2c50f193cd7bf50af035c01 +eff48af98905b28ba5f4aa3ed92d1aff97f1ef59 diff --git a/patch/0048-runc-libct-cap-allow-New-nil.patch b/patch/0048-runc-libct-cap-allow-New-nil.patch new file mode 100644 index 0000000..9e042c8 --- /dev/null +++ b/patch/0048-runc-libct-cap-allow-New-nil.patch @@ -0,0 +1,59 @@ +From b55c8fbbb8ecfd407a1d9eeec850b8c4885f4331 Mon Sep 17 00:00:00 2001 +From: Kir Kolyshkin +Date: Wed, 8 Jan 2025 12:25:42 -0800 +Subject: [PATCH] libct/cap: allow New(nil) + +In runtime-spec, capabilities property is optional, but +libcontainer/capabilities panics when New(nil) is called. + +Because of this, there's a kludge in finalizeNamespace to ensure +capabilities.New is not called with nil argument, and there's a +TestProcessEmptyCaps to ensure runc won't panic. + +Let's fix this at the source, allowing libct/cap to work with nil +capabilities. + +(The caller is fixed by the next commit.) + +Signed-off-by: Kir Kolyshkin +--- + libcontainer/capabilities/capabilities.go | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/libcontainer/capabilities/capabilities.go b/libcontainer/capabilities/capabilities.go +index d38b8a7c..49b842ca 100644 +--- a/libcontainer/capabilities/capabilities.go ++++ b/libcontainer/capabilities/capabilities.go +@@ -54,6 +54,9 @@ func New(capConfig *configs.Capabilities) (*Caps, error) { + err error + c Caps + ) ++ if capConfig == nil { ++ return &c, nil ++ } + + unknownCaps := make(map[string]struct{}) + c.caps = map[capability.CapType][]capability.Cap{ +@@ -108,6 +111,9 @@ type Caps struct { + + // ApplyBoundingSet sets the capability bounding set to those specified in the whitelist. + func (c *Caps) ApplyBoundingSet() error { ++ if c.pid == nil { ++ return nil ++ } + c.pid.Clear(capability.BOUNDING) + c.pid.Set(capability.BOUNDING, c.caps[capability.BOUNDING]...) + return c.pid.Apply(capability.BOUNDING) +@@ -115,6 +121,9 @@ func (c *Caps) ApplyBoundingSet() error { + + // Apply sets all the capabilities for the current process in the config. + func (c *Caps) ApplyCaps() error { ++ if c.pid == nil { ++ return nil ++ } + c.pid.Clear(allCapabilityTypes) + for _, g := range capTypes { + c.pid.Set(g, c.caps[g]...) +-- +2.33.0 + diff --git a/runc.spec b/runc.spec index 2da6444..094ba48 100644 --- a/runc.spec +++ b/runc.spec @@ -3,7 +3,7 @@ Name: runc Version: 1.1.8 -Release: 25 +Release: 26 Summary: runc is a CLI tool for spawning and running containers according to the OCI specification. License: ASL 2.0 @@ -57,6 +57,12 @@ install -p -m 755 runc $RPM_BUILD_ROOT/%{_bindir}/runc %{_bindir}/runc %changelog +* Wed Mar 26 2025 dongyuzhen - 1.1.8-26 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:allowing libct/cap to work with nil capabilities + * Tue Oct 15 2024 Wenlong Zhang - 1.1.8-25 - fix build error on loongarch64 diff --git a/series.conf b/series.conf index 5841a44..d635eaa 100644 --- a/series.conf +++ b/series.conf @@ -43,3 +43,4 @@ patch/0044-runc-format-log-instead-panic-when-procError-missing.patch patch/0045-rootfs-consolidate-mountpoint-creation-logic.patch patch/0046-rootfs-try-to-scope-MkdirAll-to-stay-inside-the-root.patch patch/0047-runc-fix-can-t-set-cpuset-cpus-and-cpuset-mems-at-th.patch +patch/0048-runc-libct-cap-allow-New-nil.patch \ No newline at end of file -- Gitee