From bb9ee2b0df34423f819ed120719efc5d3a3676db Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Mon, 9 Feb 2026 10:40:18 +1100 Subject: [PATCH 01/11] integration: output debug information in fd leak test Signed-off-by: Aleksa Sarai --- tests/integration/create.bats | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/integration/create.bats b/tests/integration/create.bats index 48b665dc04f..f96c2e2aad7 100644 --- a/tests/integration/create.bats +++ b/tests/integration/create.bats @@ -69,6 +69,8 @@ is_allowed_fdtarget() { if ! is_allowed_fdtarget "$target"; then echo "Violation: FD $fd_name -> '$target'" violation_found=1 + else + echo "Permitted: FD $fd_name -> '$target'" fi done < <(find "/proc/$pid/fd" -type l -print0) [ "$violation_found" -eq 0 ] From 6b757b6aa0dc2f4c0160c5cccd0909caee4c68e8 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Tue, 3 Feb 2026 00:15:10 +0100 Subject: [PATCH 02/11] dockerfile: switch to Debian 13 Debian 13 (trixie) was released a few months ago and it's probably prudent to just upgrade. This is also necessary to get access to riscv64 repositories when we build libpathrs for inclusion in our runc binaries. Signed-off-by: Aleksa Sarai --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index e4f3df3545b..f97f3036bf2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,9 +2,9 @@ ARG GO_VERSION=1.25 ARG BATS_VERSION=v1.12.0 ARG LIBSECCOMP_VERSION=2.6.0 -FROM golang:${GO_VERSION}-bookworm +FROM golang:${GO_VERSION}-trixie ARG DEBIAN_FRONTEND=noninteractive -ARG CRIU_REPO=https://download.opensuse.org/repositories/devel:/tools:/criu/Debian_12 +ARG CRIU_REPO=https://download.opensuse.org/repositories/devel:/tools:/criu/Debian_13 RUN KEYFILE=/usr/share/keyrings/criu-repo-keyring.gpg; \ wget -nv $CRIU_REPO/Release.key -O- | gpg --dearmor > "$KEYFILE" \ From 51ae8de054eb3068c4e4901ba9a6af7f5807d5e4 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Fri, 13 Feb 2026 18:32:33 +1100 Subject: [PATCH 03/11] build: treat armhf as ARMv7 The intention of commit 531e29e19211 ("script/lib.sh: set GOARM=5 for armel, GOARM=6 for armhf") was to properly support older ARM platforms with our release builds. However, we have never been able to support ARMv6 for our builds because we use the Debian compiler to build the libseccomp we statically compile into our binaries and (as per the now-deleted comment itself) Debian treats armhf as being ARMv7 so the final binaries we produced were always only ever compatible with ARMv7+. This was a bit of an oddity before but when building libpathrs for releases we will need to use Rust which makes the target more explicit (and while it does support armhf, we are using the Debian-packaged Rust cross-compiler and thus are in the same dilemma with what Debian considers "armhf" to be). All-in-all, it's better to just bite the bullet and just follow Debian here properly. Fixes: 531e29e19211 ("script/lib.sh: set GOARM=5 for armel, GOARM=6 for armhf") Signed-off-by: Aleksa Sarai --- CHANGELOG.md | 10 ++++++++++ script/lib.sh | 11 +---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cc3328b177..3f6c669467e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 particularly useful and was completely obsoleted by the changes to `/proc/self/exe` sealing we introduced in runc [1.2.0][]. (#5141) +### Changed ### +- Previously we made an attempt to make our `runc.armhf` release binaries work + with ARMv6 (which would allow runc to work on the original Raspberry Pi). + Unfortunately, this has effectively always been broken (because we + cross-compile `libseccomp` within a Debian container and statically link to + it) and so we are now officially matching [the Debian definition of `armhf`][debian-armhf] + (that is, ARMv7). (#5103) + +[debian-armhf]: https://wiki.debian.org/ArmHardFloatPort + ## [1.4.0] - 2025-11-27 > 路漫漫其修远兮,吾将上下而求索! diff --git a/script/lib.sh b/script/lib.sh index 9e4139756a3..89ce8c6c34e 100644 --- a/script/lib.sh +++ b/script/lib.sh @@ -48,16 +48,7 @@ function set_cross_vars() { armhf) HOST=arm-${PLATFORM}eabihf GOARCH=arm - # "armhf" means ARMv7 for Debian, ARMv6 for Raspbian. - # ARMv6 is chosen here for compatibility. - # - # https://wiki.debian.org/RaspberryPi - # - # > Raspberry Pi OS builds a single image for all of the Raspberry families, - # > so you will get an armhf 32-bit, hard floating-point system, but built - # > for the ARMv6 ISA (with VFP2), unlike Debian's ARMv7 ISA (with VFP3) - # > port. - GOARM=6 + GOARM=7 ;; ppc64le) HOST=powerpc64le-${PLATFORM} From 8292574e7ab4cf78246ccbe1eaf64c2eb883fefe Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Mon, 2 Feb 2026 13:04:50 +0100 Subject: [PATCH 04/11] build: rename /opt/libseccomp cdylib directory In a future patch this will contain other cdylibs so it deserves a slightly more general name. Signed-off-by: Aleksa Sarai --- Dockerfile | 10 ++++++---- script/release_build.sh | 23 +++++++++-------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index f97f3036bf2..9f5bbc09642 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,14 +54,16 @@ RUN cd /tmp \ && ./install.sh /usr/local \ && rm -rf /tmp/bats-core +ENV DYLIB_DIR=/opt/runc-dylibs + # install libseccomp ARG LIBSECCOMP_VERSION COPY script/seccomp.sh script/lib.sh /tmp/script/ -RUN mkdir -p /opt/libseccomp \ - && /tmp/script/seccomp.sh "$LIBSECCOMP_VERSION" /opt/libseccomp 386 amd64 arm64 armel armhf ppc64le riscv64 s390x +RUN mkdir -p $DYLIB_DIR \ + && /tmp/script/seccomp.sh "$LIBSECCOMP_VERSION" $DYLIB_DIR 386 amd64 arm64 armel armhf ppc64le riscv64 s390x ENV LIBSECCOMP_VERSION=$LIBSECCOMP_VERSION -ENV LD_LIBRARY_PATH=/opt/libseccomp/lib -ENV PKG_CONFIG_PATH=/opt/libseccomp/lib/pkgconfig +ENV LD_LIBRARY_PATH=$DYLIB_DIR/lib +ENV PKG_CONFIG_PATH=$DYLIB_DIR/lib/pkgconfig # Prevent the "fatal: detected dubious ownership in repository" git complain during build. RUN git config --global --add safe.directory /go/src/github.com/opencontainers/runc diff --git a/script/release_build.sh b/script/release_build.sh index 0e0a221839e..7627a65ca33 100755 --- a/script/release_build.sh +++ b/script/release_build.sh @@ -40,14 +40,14 @@ function build_project() { shift local arches=("$@") - # Assume that if /opt/libseccomp exists, then we are run - # via Dockerfile, and seccomp is already built. - local seccompdir=/opt/libseccomp temp_dir - if [ ! -d "$seccompdir" ]; then - temp_dir="$(mktemp -d)" - seccompdir="$temp_dir" + # Assume that if /opt/runc-dylibs exists, then we are running via + # Dockerfile, and thus seccomp is already built. Otherwise, build it now. + local dylibdir=/opt/runc-dylibs + if ! [ -d "$dylibdir" ]; then + trap 'rm -rf "$dylibdir"' EXIT + dylibdir="$(mktemp -d)" # Download and build libseccomp. - "$root/script/seccomp.sh" "$LIBSECCOMP_VERSION" "$seccompdir" "${arches[@]}" + "$root/script/seccomp.sh" "$LIBSECCOMP_VERSION" "$dylibdir" "${arches[@]}" fi # For reproducible builds, add these to EXTRA_LDFLAGS: @@ -70,7 +70,7 @@ function build_project() { CFLAGS="$original_cflags" set_cross_vars "$arch" make -C "$root" \ - PKG_CONFIG_PATH="$seccompdir/$arch/lib/pkgconfig" \ + PKG_CONFIG_PATH="$dylibdir/$arch/lib/pkgconfig" \ "${make_args[@]}" "$STRIP" "$root/$project" mv "$root/$project" "$builddir/$project.$arch" @@ -85,12 +85,7 @@ function build_project() { fi # Copy libseccomp source tarball. - cp "$seccompdir"/src/* "$builddir" - - # Clean up. - if [ -n "$tempdir" ]; then - rm -rf "$tempdir" - fi + cp "$dylibdir"/src/* "$builddir" } # End of the easy-to-configure portion. From b1a9047c7a493acc540166abb0033d2fd1ae642d Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Tue, 3 Feb 2026 19:52:35 +0100 Subject: [PATCH 05/11] script: seccomp.sh -> build-seccomp.sh This name is far more descriptive. Signed-off-by: Aleksa Sarai --- Dockerfile | 6 ++++-- script/{seccomp.sh => build-seccomp.sh} | 2 +- script/release_build.sh | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) rename script/{seccomp.sh => build-seccomp.sh} (96%) diff --git a/Dockerfile b/Dockerfile index 9f5bbc09642..829acc42c51 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,14 +54,16 @@ RUN cd /tmp \ && ./install.sh /usr/local \ && rm -rf /tmp/bats-core +ARG RELEASE_ARCHES="386 amd64 arm64 armel armhf ppc64le riscv64 s390x" ENV DYLIB_DIR=/opt/runc-dylibs # install libseccomp ARG LIBSECCOMP_VERSION -COPY script/seccomp.sh script/lib.sh /tmp/script/ +COPY script/build-seccomp.sh script/lib.sh /tmp/script/ RUN mkdir -p $DYLIB_DIR \ - && /tmp/script/seccomp.sh "$LIBSECCOMP_VERSION" $DYLIB_DIR 386 amd64 arm64 armel armhf ppc64le riscv64 s390x + && /tmp/script/build-seccomp.sh "$LIBSECCOMP_VERSION" $DYLIB_DIR $RELEASE_ARCHES ENV LIBSECCOMP_VERSION=$LIBSECCOMP_VERSION + ENV LD_LIBRARY_PATH=$DYLIB_DIR/lib ENV PKG_CONFIG_PATH=$DYLIB_DIR/lib/pkgconfig diff --git a/script/seccomp.sh b/script/build-seccomp.sh similarity index 96% rename from script/seccomp.sh rename to script/build-seccomp.sh index c4bbfac1f92..d8a3f9a0e42 100755 --- a/script/seccomp.sh +++ b/script/build-seccomp.sh @@ -65,7 +65,7 @@ function build_libseccomp() { } if [ $# -lt 2 ]; then - echo "Usage: seccomp.sh [ ...]" >&2 + echo "Usage: $0 [ ...]" >&2 exit 1 fi diff --git a/script/release_build.sh b/script/release_build.sh index 7627a65ca33..f84bd5041fd 100755 --- a/script/release_build.sh +++ b/script/release_build.sh @@ -47,7 +47,7 @@ function build_project() { trap 'rm -rf "$dylibdir"' EXIT dylibdir="$(mktemp -d)" # Download and build libseccomp. - "$root/script/seccomp.sh" "$LIBSECCOMP_VERSION" "$dylibdir" "${arches[@]}" + "$root/script/build-seccomp.sh" "$LIBSECCOMP_VERSION" "$dylibdir" "${arches[@]}" fi # For reproducible builds, add these to EXTRA_LDFLAGS: From ab6f75dd25571cbf89ebe05fe4100a24f53ce864 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Thu, 26 Feb 2026 17:43:50 +1100 Subject: [PATCH 06/11] README: document libpathrs build tag Ever since v0.6.0 of github.com/cyphar/filepath-securejoin, pathrs-lite has been able to transparently switch to using libpathrs as the backend for safe path resolution (at compile-time, using a build tag). Note that because build-tags apply globally, this allows for us to easily opt pure-Go dependencies into all using libpathrs as well for our binaries. In a future patch this will likely be enabled by default, but document that this is an option for downstreams that want to opt-in to using libpathrs. Signed-off-by: Aleksa Sarai --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index bca08c2d284..cf1df2369d1 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ make EXTRA_BUILDTAGS="runc_nocriu" | Build Tag | Feature | Enabled by Default | Dependencies | |---------------|---------------------------------------|--------------------|---------------------| | `seccomp` | Syscall filtering using `libseccomp`. | yes | `libseccomp` | +| `libpathrs` | Use [`libpathrs`][] for path safety. | no | [`libpathrs`][] | | `runc_nocriu` | **Disables** runc checkpoint/restore. | no | `criu` | The following build tags were used earlier, but are now obsoleted: @@ -120,6 +121,8 @@ The following build tags were used earlier, but are now obsoleted: - **apparmor** (since runc v1.0.0-rc93 the feature is always enabled) - **selinux** (since runc v1.0.0-rc93 the feature is always enabled) +[`libpathrs`]: https://github.com/cyphar/libpathrs + ### Running the test suite `runc` currently supports running its test suite via Docker. From b58e3427581e8e6f34172be5ce93053c4cd839fa Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Fri, 13 Feb 2026 17:45:47 +1100 Subject: [PATCH 07/11] deps: update to cyphar.com/go-pathrs@v0.2.4 This includes a few fixes for 32-bit builds. Signed-off-by: Aleksa Sarai --- go.mod | 2 +- go.sum | 4 +-- vendor/cyphar.com/go-pathrs/.golangci.yml | 2 +- vendor/cyphar.com/go-pathrs/doc.go | 2 +- vendor/cyphar.com/go-pathrs/handle_linux.go | 10 ++----- .../go-pathrs/internal/fdutils/fd_linux.go | 2 +- .../internal/libpathrs/error_unix.go | 2 +- .../internal/libpathrs/libpathrs_linux.go | 6 ++-- .../go-pathrs/procfs/procfs_linux.go | 15 +++------- vendor/cyphar.com/go-pathrs/root_linux.go | 30 ++----------------- vendor/cyphar.com/go-pathrs/utils_linux.go | 2 +- vendor/modules.txt | 2 +- 12 files changed, 21 insertions(+), 58 deletions(-) diff --git a/go.mod b/go.mod index b9e866a7c66..0d221283b9c 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( ) require ( - cyphar.com/go-pathrs v0.2.1 // indirect + cyphar.com/go-pathrs v0.2.4 // indirect github.com/cilium/ebpf v0.17.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 97d3d7c9562..294d523f93e 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8= -cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= +cyphar.com/go-pathrs v0.2.4 h1:iD/mge36swa1UFKdINkr1Frkpp6wZsy3YYEildj9cLY= +cyphar.com/go-pathrs v0.2.4/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/checkpoint-restore/go-criu/v7 v7.2.0 h1:qGiWA4App1gGlEfIJ68WR9jbezV9J7yZdjzglezcqKo= github.com/checkpoint-restore/go-criu/v7 v7.2.0/go.mod h1:u0LCWLg0w4yqqu14aXhiB4YD3a1qd8EcCEg7vda5dwo= diff --git a/vendor/cyphar.com/go-pathrs/.golangci.yml b/vendor/cyphar.com/go-pathrs/.golangci.yml index 2778a3268ef..a28cbd2a4fd 100644 --- a/vendor/cyphar.com/go-pathrs/.golangci.yml +++ b/vendor/cyphar.com/go-pathrs/.golangci.yml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: MPL-2.0 # # libpathrs: safe path resolution on Linux -# Copyright (C) 2019-2025 Aleksa Sarai # Copyright (C) 2019-2025 SUSE LLC +# Copyright (C) 2026 Aleksa Sarai # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/vendor/cyphar.com/go-pathrs/doc.go b/vendor/cyphar.com/go-pathrs/doc.go index a7ee4bc487f..c3b4eedd0f8 100644 --- a/vendor/cyphar.com/go-pathrs/doc.go +++ b/vendor/cyphar.com/go-pathrs/doc.go @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MPL-2.0 /* * libpathrs: safe path resolution on Linux - * Copyright (C) 2019-2025 Aleksa Sarai * Copyright (C) 2019-2025 SUSE LLC + * Copyright (C) 2026 Aleksa Sarai * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/vendor/cyphar.com/go-pathrs/handle_linux.go b/vendor/cyphar.com/go-pathrs/handle_linux.go index 3221ef67389..6ed0b7af7ad 100644 --- a/vendor/cyphar.com/go-pathrs/handle_linux.go +++ b/vendor/cyphar.com/go-pathrs/handle_linux.go @@ -3,8 +3,8 @@ // SPDX-License-Identifier: MPL-2.0 /* * libpathrs: safe path resolution on Linux - * Copyright (C) 2019-2025 Aleksa Sarai * Copyright (C) 2019-2025 SUSE LLC + * Copyright (C) 2026 Aleksa Sarai * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -30,11 +30,9 @@ import ( // you can try to use [Root.Open] or [Root.OpenFile]. // // It is critical that perform all relevant operations through this [Handle] -// (rather than fetching the file descriptor yourself with [Handle.IntoRaw]), +// (rather than fetching the underlying [os.File] yourself with [Handle.IntoFile]), // because the security properties of libpathrs depend on users doing all // relevant filesystem operations through libpathrs. -// -// [os.File]: https://pkg.go.dev/os#File type Handle struct { inner *os.File } @@ -43,7 +41,7 @@ type Handle struct { // handle will be copied by this method, so the original handle should still be // freed by the caller. // -// This is effectively the inverse operation of [Handle.IntoRaw], and is used +// This is effectively the inverse operation of [Handle.IntoFile], and is used // for "deserialising" pathrs root handles. func HandleFromFile(file *os.File) (*Handle, error) { newFile, err := fdutils.DupFile(file) @@ -92,8 +90,6 @@ func (h *Handle) OpenFile(flags int) (*os.File, error) { // calling [Handle.Close] will also close any copies of the returned [os.File]. // If you want to get an independent copy, use [Handle.Clone] followed by // [Handle.IntoFile] on the cloned [Handle]. -// -// [os.File]: https://pkg.go.dev/os#File func (h *Handle) IntoFile() *os.File { // TODO: Figure out if we really don't want to make a copy. // TODO: We almost certainly want to clear r.inner here, but we can't do diff --git a/vendor/cyphar.com/go-pathrs/internal/fdutils/fd_linux.go b/vendor/cyphar.com/go-pathrs/internal/fdutils/fd_linux.go index 41aea3e4b3d..418b298149e 100644 --- a/vendor/cyphar.com/go-pathrs/internal/fdutils/fd_linux.go +++ b/vendor/cyphar.com/go-pathrs/internal/fdutils/fd_linux.go @@ -3,8 +3,8 @@ // SPDX-License-Identifier: MPL-2.0 /* * libpathrs: safe path resolution on Linux - * Copyright (C) 2019-2025 Aleksa Sarai * Copyright (C) 2019-2025 SUSE LLC + * Copyright (C) 2026 Aleksa Sarai * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/vendor/cyphar.com/go-pathrs/internal/libpathrs/error_unix.go b/vendor/cyphar.com/go-pathrs/internal/libpathrs/error_unix.go index c9f416de01f..8f610ca564e 100644 --- a/vendor/cyphar.com/go-pathrs/internal/libpathrs/error_unix.go +++ b/vendor/cyphar.com/go-pathrs/internal/libpathrs/error_unix.go @@ -5,8 +5,8 @@ // SPDX-License-Identifier: MPL-2.0 /* * libpathrs: safe path resolution on Linux - * Copyright (C) 2019-2025 Aleksa Sarai * Copyright (C) 2019-2025 SUSE LLC + * Copyright (C) 2026 Aleksa Sarai * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/vendor/cyphar.com/go-pathrs/internal/libpathrs/libpathrs_linux.go b/vendor/cyphar.com/go-pathrs/internal/libpathrs/libpathrs_linux.go index c07b80e3071..d54497a5b70 100644 --- a/vendor/cyphar.com/go-pathrs/internal/libpathrs/libpathrs_linux.go +++ b/vendor/cyphar.com/go-pathrs/internal/libpathrs/libpathrs_linux.go @@ -3,8 +3,8 @@ // SPDX-License-Identifier: MPL-2.0 /* * libpathrs: safe path resolution on Linux - * Copyright (C) 2019-2025 Aleksa Sarai * Copyright (C) 2019-2025 SUSE LLC + * Copyright (C) 2026 Aleksa Sarai * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -100,7 +100,7 @@ func InRootReadlink(rootFd uintptr, path string) (string, error) { size := 128 for { linkBuf := make([]byte, size) - n := C.pathrs_inroot_readlink(C.int(rootFd), cPath, C.cast_ptr(unsafe.Pointer(&linkBuf[0])), C.ulong(len(linkBuf))) + n := C.pathrs_inroot_readlink(C.int(rootFd), cPath, C.cast_ptr(unsafe.Pointer(&linkBuf[0])), C.size_t(len(linkBuf))) switch { case int(n) < C.__PATHRS_MAX_ERR_VALUE: return "", fetchError(n) @@ -301,7 +301,7 @@ func ProcReadlinkat(procRootFd int, base ProcBase, path string) (string, error) linkBuf := make([]byte, size) n := C.pathrs_proc_readlinkat( C.int(procRootFd), cBase, cPath, - C.cast_ptr(unsafe.Pointer(&linkBuf[0])), C.ulong(len(linkBuf))) + C.cast_ptr(unsafe.Pointer(&linkBuf[0])), C.size_t(len(linkBuf))) switch { case int(n) < C.__PATHRS_MAX_ERR_VALUE: return "", fetchError(n) diff --git a/vendor/cyphar.com/go-pathrs/procfs/procfs_linux.go b/vendor/cyphar.com/go-pathrs/procfs/procfs_linux.go index 5533c427cb7..915e9ccdb58 100644 --- a/vendor/cyphar.com/go-pathrs/procfs/procfs_linux.go +++ b/vendor/cyphar.com/go-pathrs/procfs/procfs_linux.go @@ -3,8 +3,8 @@ // SPDX-License-Identifier: MPL-2.0 /* * libpathrs: safe path resolution on Linux - * Copyright (C) 2019-2025 Aleksa Sarai * Copyright (C) 2019-2025 SUSE LLC + * Copyright (C) 2026 Aleksa Sarai * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -56,16 +56,15 @@ var ( // *before* you call wait(2)or any equivalent method that could reap // zombies). func ProcPid(pid int) ProcBase { - if pid < 0 || pid >= 1<<31 { + if pid < 0 || uint64(pid) >= 1<<31 { panic("invalid ProcBasePid value") // TODO: should this be an error? } - return ProcBase{inner: libpathrs.ProcPid(uint32(pid))} + pid32 := uint32(pid) //nolint:gosec // G115 false positive + return ProcBase{inner: libpathrs.ProcPid(pid32)} } // ThreadCloser is a callback that needs to be called when you are done // operating on an [os.File] fetched using [Handle.OpenThreadSelf]. -// -// [os.File]: https://pkg.go.dev/os#File type ThreadCloser func() // Handle is a wrapper around an *os.File handle to "/proc", which can be @@ -181,8 +180,6 @@ func (proc *Handle) OpenRoot(path string, flags int) (*os.File, error) { // Unlike [Handle.OpenThreadSelf], this method does not involve locking // the goroutine to the current OS thread and so is simpler to use and // theoretically has slightly less overhead. -// -// [runtime.LockOSThread]: https://pkg.go.dev/runtime#LockOSThread func (proc *Handle) OpenSelf(path string, flags int) (*os.File, error) { file, closer, err := proc.open(ProcSelf, path, flags) if closer != nil { @@ -228,10 +225,6 @@ func (proc *Handle) OpenPid(pid int, path string, flags int) (*os.File, error) { // callback MUST be called AFTER you have finished using the returned // [os.File]. This callback is completely separate to [os.File.Close], so it // must be called regardless of how you close the handle. -// -// [runtime.LockOSThread]: https://pkg.go.dev/runtime#LockOSThread -// [os.File]: https://pkg.go.dev/os#File -// [os.File.Close]: https://pkg.go.dev/os#File.Close func (proc *Handle) OpenThreadSelf(path string, flags int) (*os.File, ThreadCloser, error) { return proc.open(ProcThreadSelf, path, flags) } diff --git a/vendor/cyphar.com/go-pathrs/root_linux.go b/vendor/cyphar.com/go-pathrs/root_linux.go index edc9e4c87f9..5bc2e907170 100644 --- a/vendor/cyphar.com/go-pathrs/root_linux.go +++ b/vendor/cyphar.com/go-pathrs/root_linux.go @@ -3,8 +3,8 @@ // SPDX-License-Identifier: MPL-2.0 /* * libpathrs: safe path resolution on Linux - * Copyright (C) 2019-2025 Aleksa Sarai * Copyright (C) 2019-2025 SUSE LLC + * Copyright (C) 2026 Aleksa Sarai * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -54,8 +54,6 @@ func OpenRoot(path string) (*Root, error) { // still be closed by the caller. // // This is effectively the inverse operation of [Root.IntoFile]. -// -// [os.File]: https://pkg.go.dev/os#File func RootFromFile(file *os.File) (*Root, error) { newFile, err := fdutils.DupFile(file) if err != nil { @@ -109,8 +107,6 @@ func (r *Root) ResolveNoFollow(path string) (*Handle, error) { // ergonomic to use. // // This is effectively equivalent to [os.Open]. -// -// [os.Open]: https://pkg.go.dev/os#Open func (r *Root) Open(path string) (*os.File, error) { return r.OpenFile(path, os.O_RDONLY) } @@ -127,8 +123,6 @@ func (r *Root) Open(path string) (*os.File, error) { // // This is effectively equivalent to [os.OpenFile], except that os.O_CREAT is // not supported. -// -// [os.OpenFile]: https://pkg.go.dev/os#OpenFile func (r *Root) OpenFile(path string, flags int) (*os.File, error) { return fdutils.WithFileFd(r.inner, func(rootFd uintptr) (*os.File, error) { fd, err := libpathrs.InRootOpen(rootFd, path, flags) @@ -145,8 +139,6 @@ func (r *Root) OpenFile(path string, flags int) (*os.File, error) { // // Unlike [os.Create], if the file already exists an error is created rather // than the file being opened and truncated. -// -// [os.Create]: https://pkg.go.dev/os#Create func (r *Root) Create(path string, flags int, mode os.FileMode) (*os.File, error) { unixMode, err := toUnixMode(mode, false) if err != nil { @@ -194,8 +186,6 @@ func (r *Root) RemoveFile(path string) error { // directory tree. // // This is effectively equivalent to [os.Remove]. -// -// [os.Remove]: https://pkg.go.dev/os#Remove func (r *Root) Remove(path string) error { // In order to match os.Remove's implementation we need to also do both // syscalls unconditionally and adjust the error based on whether @@ -219,8 +209,6 @@ func (r *Root) Remove(path string) error { // RemoveAll recursively deletes a path and all of its children. // // This is effectively equivalent to [os.RemoveAll]. -// -// [os.RemoveAll]: https://pkg.go.dev/os#RemoveAll func (r *Root) RemoveAll(path string) error { _, err := fdutils.WithFileFd(r.inner, func(rootFd uintptr) (struct{}, error) { err := libpathrs.InRootRemoveAll(rootFd, path) @@ -233,8 +221,6 @@ func (r *Root) RemoveAll(path string) error { // mode is used for the new directory (the process's umask applies). // // This is effectively equivalent to [os.Mkdir]. -// -// [os.Mkdir]: https://pkg.go.dev/os#Mkdir func (r *Root) Mkdir(path string, mode os.FileMode) error { unixMode, err := toUnixMode(mode, false) if err != nil { @@ -253,8 +239,6 @@ func (r *Root) Mkdir(path string, mode os.FileMode) error { // directories created by this function (the process's umask applies). // // This is effectively equivalent to [os.MkdirAll]. -// -// [os.MkdirAll]: https://pkg.go.dev/os#MkdirAll func (r *Root) MkdirAll(path string, mode os.FileMode) (*Handle, error) { unixMode, err := toUnixMode(mode, false) if err != nil { @@ -278,9 +262,7 @@ func (r *Root) MkdirAll(path string, mode os.FileMode) (*Handle, error) { // directory tree. The provided mode is used for the new directory (the // process's umask applies). // -// This is effectively equivalent to [unix.Mknod]. -// -// [unix.Mknod]: https://pkg.go.dev/golang.org/x/sys/unix#Mknod +// This is effectively equivalent to [golang.org/x/sys/unix.Mknod]. func (r *Root) Mknod(path string, mode os.FileMode, dev uint64) error { unixMode, err := toUnixMode(mode, true) if err != nil { @@ -298,8 +280,6 @@ func (r *Root) Mknod(path string, mode os.FileMode, dev uint64) error { // created at path and is a link to target. // // This is effectively equivalent to [os.Symlink]. -// -// [os.Symlink]: https://pkg.go.dev/os#Symlink func (r *Root) Symlink(path, target string) error { _, err := fdutils.WithFileFd(r.inner, func(rootFd uintptr) (struct{}, error) { err := libpathrs.InRootSymlink(rootFd, path, target) @@ -314,8 +294,6 @@ func (r *Root) Symlink(path, target string) error { // host). // // This is effectively equivalent to [os.Link]. -// -// [os.Link]: https://pkg.go.dev/os#Link func (r *Root) Hardlink(path, target string) error { _, err := fdutils.WithFileFd(r.inner, func(rootFd uintptr) (struct{}, error) { err := libpathrs.InRootHardlink(rootFd, path, target) @@ -327,8 +305,6 @@ func (r *Root) Hardlink(path, target string) error { // Readlink returns the target of a symlink with a [Root]'s directory tree. // // This is effectively equivalent to [os.Readlink]. -// -// [os.Readlink]: https://pkg.go.dev/os#Readlink func (r *Root) Readlink(path string) (string, error) { return fdutils.WithFileFd(r.inner, func(rootFd uintptr) (string, error) { return libpathrs.InRootReadlink(rootFd, path) @@ -345,8 +321,6 @@ func (r *Root) Readlink(path string) (string, error) { // calling [Root.Close] will also close any copies of the returned [os.File]. // If you want to get an independent copy, use [Root.Clone] followed by // [Root.IntoFile] on the cloned [Root]. -// -// [os.File]: https://pkg.go.dev/os#File func (r *Root) IntoFile() *os.File { // TODO: Figure out if we really don't want to make a copy. // TODO: We almost certainly want to clear r.inner here, but we can't do diff --git a/vendor/cyphar.com/go-pathrs/utils_linux.go b/vendor/cyphar.com/go-pathrs/utils_linux.go index 2208d608f8d..b4e7e08e7df 100644 --- a/vendor/cyphar.com/go-pathrs/utils_linux.go +++ b/vendor/cyphar.com/go-pathrs/utils_linux.go @@ -3,8 +3,8 @@ // SPDX-License-Identifier: MPL-2.0 /* * libpathrs: safe path resolution on Linux - * Copyright (C) 2019-2025 Aleksa Sarai * Copyright (C) 2019-2025 SUSE LLC + * Copyright (C) 2026 Aleksa Sarai * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/vendor/modules.txt b/vendor/modules.txt index 58dd5a1088c..7137dc8bd22 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ -# cyphar.com/go-pathrs v0.2.1 +# cyphar.com/go-pathrs v0.2.4 ## explicit; go 1.18 cyphar.com/go-pathrs cyphar.com/go-pathrs/internal/fdutils From 8689e50cbed0393cf70771603a0e6a0ac8a63fb5 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Tue, 3 Feb 2026 21:31:12 +0100 Subject: [PATCH 08/11] build: enable builds with libpathrs pathrs-lite supports transparently switching to libpathrs.so as the backend with the "libpathrs" build tag. In order to make this work properly with our CI and release build scripts, we we need to have a similar setup as with we do with libseccomp. Signed-off-by: Aleksa Sarai --- Dockerfile | 28 +++++-- script/build-libpathrs.sh | 151 ++++++++++++++++++++++++++++++++++++++ script/build-seccomp.sh | 2 +- script/lib.sh | 47 ++++++++---- script/release_build.sh | 5 +- script/release_sign.sh | 6 ++ 6 files changed, 213 insertions(+), 26 deletions(-) create mode 100755 script/build-libpathrs.sh diff --git a/Dockerfile b/Dockerfile index 829acc42c51..0864fbab43b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,7 @@ ARG GO_VERSION=1.25 ARG BATS_VERSION=v1.12.0 ARG LIBSECCOMP_VERSION=2.6.0 +ARG LIBPATHRS_VERSION=0.2.4 FROM golang:${GO_VERSION}-trixie ARG DEBIAN_FRONTEND=noninteractive @@ -9,10 +10,13 @@ ARG CRIU_REPO=https://download.opensuse.org/repositories/devel:/tools:/criu/Debi RUN KEYFILE=/usr/share/keyrings/criu-repo-keyring.gpg; \ wget -nv $CRIU_REPO/Release.key -O- | gpg --dearmor > "$KEYFILE" \ && echo "deb [signed-by=$KEYFILE] $CRIU_REPO/ /" > /etc/apt/sources.list.d/criu.list \ - && dpkg --add-architecture i386 \ + && printf "%s\n" i386 armel armhf arm64 ppc64el s390x riscv64 | xargs -t -n1 -- dpkg --add-architecture \ && apt-get update \ && apt-get install -y --no-install-recommends \ build-essential \ + cargo \ + cargo-auditable \ + clang \ criu \ gcc \ gcc-multilib \ @@ -22,6 +26,7 @@ RUN KEYFILE=/usr/share/keyrings/criu-repo-keyring.gpg; \ iptables \ jq \ kmod \ + lld \ pkg-config \ python3-minimal \ sshfs \ @@ -29,13 +34,13 @@ RUN KEYFILE=/usr/share/keyrings/criu-repo-keyring.gpg; \ uidmap \ iproute2 \ && apt-get install -y --no-install-recommends \ - libc-dev:i386 libgcc-s1:i386 \ - gcc-aarch64-linux-gnu libc-dev-arm64-cross \ - gcc-arm-linux-gnueabi libc-dev-armel-cross \ - gcc-arm-linux-gnueabihf libc-dev-armhf-cross \ - gcc-powerpc64le-linux-gnu libc-dev-ppc64el-cross \ - gcc-s390x-linux-gnu libc-dev-s390x-cross \ - gcc-riscv64-linux-gnu libc-dev-riscv64-cross \ + libc-dev:i386 libgcc-s1:i386 gcc-i686-linux-gnu libstd-rust-dev:i386 \ + gcc-aarch64-linux-gnu libc-dev-arm64-cross libstd-rust-dev:arm64 \ + gcc-arm-linux-gnueabi libc-dev-armel-cross libstd-rust-dev:armel \ + gcc-arm-linux-gnueabihf libc-dev-armhf-cross libstd-rust-dev:armhf \ + gcc-powerpc64le-linux-gnu libc-dev-ppc64el-cross libstd-rust-dev:ppc64el \ + gcc-s390x-linux-gnu libc-dev-s390x-cross libstd-rust-dev:s390x \ + gcc-riscv64-linux-gnu libc-dev-riscv64-cross libstd-rust-dev:riscv64 \ && apt-get clean \ && rm -rf /var/cache/apt /var/lib/apt/lists/* /etc/apt/sources.list.d/*.list @@ -64,6 +69,13 @@ RUN mkdir -p $DYLIB_DIR \ && /tmp/script/build-seccomp.sh "$LIBSECCOMP_VERSION" $DYLIB_DIR $RELEASE_ARCHES ENV LIBSECCOMP_VERSION=$LIBSECCOMP_VERSION +# install libpathrs +ARG LIBPATHRS_VERSION +COPY script/build-libpathrs.sh /tmp/script/ +RUN mkdir -p $DYLIB_DIR \ + && /tmp/script/build-libpathrs.sh "$LIBPATHRS_VERSION" $DYLIB_DIR $RELEASE_ARCHES +ENV LIBPATHRS_VERSION=$LIBPATHRS_VERSION + ENV LD_LIBRARY_PATH=$DYLIB_DIR/lib ENV PKG_CONFIG_PATH=$DYLIB_DIR/lib/pkgconfig diff --git a/script/build-libpathrs.sh b/script/build-libpathrs.sh new file mode 100755 index 00000000000..28bdc67f4ea --- /dev/null +++ b/script/build-libpathrs.sh @@ -0,0 +1,151 @@ +#!/bin/bash +# Copyright (C) 2026 Open Containers Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -xEeuo pipefail + +# shellcheck source=./script/lib.sh +source "$(dirname "${BASH_SOURCE[0]}")/lib.sh" + +PLATFORM="$(get_platform)" + +declare -A GOARCH_TO_RUST_TARGET=( + ["386"]=i686-unknown-linux-gnu + ["amd64"]=x86_64-unknown-linux-gnu + ["arm64"]=aarch64-unknown-linux-gnu + ["armel"]=armv5te-unknown-linux-gnueabi + ["armhf"]=armv7-unknown-linux-gnueabihf + ["ppc64le"]=powerpc64le-unknown-linux-gnu + ["s390x"]=s390x-unknown-linux-gnu + ["riscv64"]=riscv64gc-unknown-linux-gnu +) + +declare -A RUST_TARGET_TO_CC=( + ["i686-unknown-linux-gnu"]="i686-${PLATFORM}-gcc" + ["x86_64-unknown-linux-gnu"]="x86_64-${PLATFORM}-gcc" + ["aarch64-unknown-linux-gnu"]="aarch64-${PLATFORM}-gcc" + ["armv5te-unknown-linux-gnueabi"]="arm-${PLATFORM}eabi-gcc" + ["armv7-unknown-linux-gnueabihf"]="arm-${PLATFORM}eabihf-gcc" + ["powerpc64le-unknown-linux-gnu"]="powerpc64le-${PLATFORM}-gcc" + ["s390x-unknown-linux-gnu"]="s390x-${PLATFORM}-gcc" + ["riscv64gc-unknown-linux-gnu"]="riscv64-${PLATFORM}-gcc" +) + +# sha256 checksums for libpathrs release tarballs. +declare -A LIBPATHRS_SHA256=( + ["0.2.4"]=45aca68e698b844fae0cf7c459bc441519b0e7b48397caa7d3936cfc68d73f77 +) + +function generate_cargo_config() { + for rust_target in "${GOARCH_TO_RUST_TARGET[@]}"; do + local target_gcc="${RUST_TARGET_TO_CC[$rust_target]}" + + # Based on . + cat <<-EOF + [target.$rust_target] + linker = "$target_gcc" + rustflags = ["-L", "$(rustc --print sysroot)/lib/rustlib/$rust_target/lib"] + EOF + done +} + +# Due to libpathrs being MPLv2/LGPLv3 we must include its sources, so +# download, install and build against it. +# Parameters: +# $1 -- libpathrs version to download and build. +# $2 -- destination directory. +# $@ -- additional architectures to cross-compile for (optional) +function build_libpathrs() { + local ver="$1" + shift + local dest="$1" + shift + local go_arches=("$@") + local tar="libpathrs-${ver}.tar.xz" + + # Download, check, and extract. + wget "https://github.com/cyphar/libpathrs/releases/download/v${ver}/${tar}"{,.asc} + sha256sum --strict --check - <<<"${LIBPATHRS_SHA256[${ver}]} *${tar}" + + local srcdir + srcdir="$(mktemp -d)" + tar xf "$tar" -C "$srcdir" + pushd "$srcdir/libpathrs-$ver" || return + + # Use cargo-auditable if available. + if cargo auditable --version &>/dev/null; then + export CARGO="cargo auditable" + fi + local extra_cargo_flags=("--locked") + + # If we are being asked to install this in a system library directory + # (i.e., --prefix=/usr or something similar), the correct place to put + # libpathrs.so depends very strongly on the distro we are running on, and + # detecting this in a generic way is quite difficult. + # + # The simplest solution is to use a disto-packaged binary to detect where + # libc.so is installed and use the same import path, so we look at + # /proc/self/maps and parse out the parent directory of the libc.so being + # used. + local native_libdir libdir= + native_libdir="$(awk '$NF ~ /\/libc\>.*\.so/ { print $NF; }' /proc/self/maps | + sort -u | head -n1 | xargs dirname)" + if [[ "$native_libdir" == "$dest/"* ]]; then + libdir="$native_libdir" + fi + + # Install native version for Dockerfile builds. + make \ + EXTRA_CARGO_FLAGS="${extra_cargo_flags[*]}" \ + release + ./install.sh \ + --prefix="$dest" \ + --libdir="$libdir" + cargo clean + + local cargo_config + cargo_config="$(mktemp --tmpdir runc-libpathrs-cargo.toml.XXXXXX)" + # shellcheck disable=SC2064 # We want to resolve the path here. + trap "rm -f '$cargo_config'" EXIT + + # Only use the cross-compile config when we actually need to cross-compile. + # RedHat-based distros insist on calling their targets "$ARCH-redhat-linux" + # which breaks our above logic, but we don't ever need to cross-compile on + # RedHat distros so we can ignore this. + generate_cargo_config >"$cargo_config" + extra_cargo_flags+=("--config=$cargo_config") + + for go_arch in "${go_arches[@]}"; do + local rust_target="${GOARCH_TO_RUST_TARGET[$go_arch]}" + make \ + EXTRA_CARGO_FLAGS="${extra_cargo_flags[*]} --target=$rust_target" \ + release + ./install.sh \ + --rust-target="$rust_target" \ + --prefix="$dest/$go_arch" + cargo clean + done + + # Place the source tarball to $dest/src. + popd || return + mkdir -p "$dest"/src + mv "$tar"{,.asc} "$dest"/src +} + +if [ $# -lt 2 ]; then + echo "Usage: $0 [ ...]" >&2 + exit 1 +fi + +build_libpathrs "$@" diff --git a/script/build-seccomp.sh b/script/build-seccomp.sh index d8a3f9a0e42..1c1b9671e66 100755 --- a/script/build-seccomp.sh +++ b/script/build-seccomp.sh @@ -60,7 +60,7 @@ function build_libseccomp() { # Place the source tarball to $dest/src. popd || return - mkdir "$dest"/src + mkdir -p "$dest"/src mv "$tar"{,.asc} "$dest"/src } diff --git a/script/lib.sh b/script/lib.sh index 89ce8c6c34e..e8683ab1b6c 100644 --- a/script/lib.sh +++ b/script/lib.sh @@ -1,41 +1,56 @@ #!/bin/bash +# get_platform computes the platform section of target triples on this OS. +function get_platform() { + # Fedora doesn't have ID_LIKE and only has ID=fedora, so we need to + # construct a fake ID_LIKE to treat AlmaLinux and Fedora the same way. + local ID_LIKE + # shellcheck source=/etc/os-release disable=SC1091 # outside our sources + ID_LIKE="$( + source /etc/os-release + echo "${ID:-} ${ID_LIKE:-}" + )" + + local PLATFORM + case "$ID_LIKE" in + *suse*) + PLATFORM=suse-linux + ;; + *rhel* | *fedora* | *centos*) + PLATFORM=redhat-linux + ;; + *) + PLATFORM=linux-gnu + ;; + esac + echo "$PLATFORM" +} + # set_cross_vars sets a few environment variables used for cross-compiling, # based on the architecture specified in $1. function set_cross_vars() { GOARCH="$1" # default, may be overridden below unset GOARM - PLATFORM=linux-gnu - # openSUSE has a custom PLATFORM - if grep -iq "ID_LIKE=.*suse" /etc/os-release; then - PLATFORM=suse-linux - is_suse=1 - fi + PLATFORM="$(get_platform)" + [[ "$PLATFORM" == *suse* ]] && is_suse=1 - case $1 in + case "$1" in 386) # Always use the 64-bit compiler to build the 386 binary, which works # for the more common cross-build method for x86 (namely, the # equivalent of dpkg --add-architecture). local cpu_type if [ -v is_suse ]; then - # There is no x86_64-suse-linux-gcc, so use the native one. - HOST= cpu_type=i586 else - HOST=x86_64-${PLATFORM} cpu_type=i686 fi + HOST=x86_64-${PLATFORM} CFLAGS="-m32 -march=$cpu_type ${CFLAGS[*]}" ;; amd64) - if [ -n "${is_suse:-}" ]; then - # There is no x86_64-suse-linux-gcc, so use the native one. - HOST= - else - HOST=x86_64-${PLATFORM} - fi + HOST=x86_64-${PLATFORM} ;; arm64) HOST=aarch64-${PLATFORM} diff --git a/script/release_build.sh b/script/release_build.sh index f84bd5041fd..e6d3e64a32d 100755 --- a/script/release_build.sh +++ b/script/release_build.sh @@ -20,6 +20,7 @@ set -e # Project-specific options and functions. In *theory* you shouldn't need to # touch anything else in this script in order to use this elsewhere. : "${LIBSECCOMP_VERSION:=2.6.0}" +: "${LIBPATHRS_VERSION:=0.2.4}" project="runc" root="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")" @@ -48,6 +49,8 @@ function build_project() { dylibdir="$(mktemp -d)" # Download and build libseccomp. "$root/script/build-seccomp.sh" "$LIBSECCOMP_VERSION" "$dylibdir" "${arches[@]}" + # Download and build libpathrs. + "$root/script/build-libpathrs.sh" "$LIBPATHRS_VERSION" "$dylibdir" "${arches[@]}" fi # For reproducible builds, add these to EXTRA_LDFLAGS: @@ -84,7 +87,7 @@ function build_project() { exit 1 fi - # Copy libseccomp source tarball. + # Copy dylib source tarballs. cp "$dylibdir"/src/* "$builddir" } diff --git a/script/release_sign.sh b/script/release_sign.sh index 80f8c0731e2..444720bf08d 100755 --- a/script/release_sign.sh +++ b/script/release_sign.sh @@ -112,6 +112,9 @@ tmp_seccomp_gpgflags=("--homedir=$tmp_gpgdir" "--no-default-keyring" "--keyring= gpg "${tmp_seccomp_gpgflags[@]}" --recv-keys 0x47A68FCE37C7D7024FD65E11356CE62C2B524099 gpg "${tmp_seccomp_gpgflags[@]}" --recv-keys 0x7100AADFAE6E6E940D2E0AD655E45A5AE8CA7C8A +tmp_libpathrs_gpgflags=("--homedir=$tmp_gpgdir" "--no-default-keyring" "--keyring=libpathrs.keyring" "--keyserver=keys.openpgp.org") +gpg "${tmp_libpathrs_gpgflags[@]}" --recv-keys 0xC9C370B246B09F6DBCFC744C34401015D1D2D386 + gpg "${gpgflags[@]}" --clear-sign <<<"[This is test text used for $project release scripts. $(date --rfc-email)]" | gpg "${tmp_runc_gpgflags[@]}" --verify || bail "Signing key ${keyid:-DEFAULT} is not in trusted $project.keyring list!" @@ -156,4 +159,7 @@ gpg "${tmp_runc_gpgflags[@]}" --verify "$project.$hashcmd" # Verify seccomp tarball. gpg "${tmp_seccomp_gpgflags[@]}" --verify libseccomp*.asc +# Verify libpathrs tarball. +gpg "${tmp_libpathrs_gpgflags[@]}" --verify libpathrs*.asc + popd From 7322b05f41d0457807ff3f822c46017bcf540f07 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Mon, 9 Feb 2026 10:12:48 +1100 Subject: [PATCH 09/11] ci: build and install libpathrs libpathrs will be opt-out in a future patch so we need to test with it in our CI. Signed-off-by: Aleksa Sarai --- .cirrus.yml | 6 +++++- .github/workflows/test.yml | 28 ++++++++++++++++++++++++++-- .github/workflows/validate.yml | 12 +++++++++--- script/setup_host_fedora.sh | 9 +++++++-- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 4e58de832c8..42c0474a4c8 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -13,7 +13,8 @@ task: CIRRUS_WORKING_DIR: /home/runc GO_VER_PREFIX: "1.25." BATS_VERSION: "v1.12.0" - RPMS: gcc git-core iptables jq glibc-static libseccomp-devel make criu fuse-sshfs container-selinux policycoreutils + LIBPATHRS_VERSION: "0.2.4" + RPMS: gcc git-core iptables jq glibc-static libseccomp-devel make criu fuse-sshfs container-selinux policycoreutils cargo lld wget # yamllint disable rule:key-duplicates matrix: - DISTRO: almalinux-8 @@ -63,6 +64,9 @@ task: dnf -y install criu esac + # Install libpathrs. + /home/runc/script/build-libpathrs.sh "$LIBPATHRS_VERSION" /usr + # Install Go. URL_PREFIX="https://go.dev/dl/" # Find out the latest minor release URL. diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b6be7321ed8..a15e0d6c1f8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,6 +15,7 @@ permissions: contents: read env: + LIBPATHRS_VERSION: "0.2.4" # Don't ignore C warnings. Note that the output of "go env CGO_CFLAGS" by default is "-g -O2", so we keep them. CGO_CFLAGS: -g -O2 -Werror @@ -75,7 +76,11 @@ jobs: - name: install deps run: | sudo apt update - sudo apt -y install libseccomp-dev sshfs uidmap + sudo apt -y install libseccomp-dev sshfs uidmap lld + + - name: install libpathrs ${{ env.LIBPATHRS_VERSION }} + run: | + sudo -E PATH="$PATH" ./script/build-libpathrs.sh "$LIBPATHRS_VERSION" /usr - name: install CRIU if: ${{ matrix.criu == '' }} @@ -170,7 +175,23 @@ jobs: sudo add-apt-repository -y ppa:criu/ppa || sudo add-apt-repository -y ppa:criu/ppa # apt-add-repository runs apt update so we don't have to. - sudo apt -qy install libseccomp-dev libseccomp-dev:i386 gcc-multilib libgcc-s1:i386 criu + GCC_VERSION="$(gcc -dumpversion)" + sudo apt -qy install \ + lld criu \ + libseccomp-dev libseccomp-dev:i386 \ + libc-dev:i386 libgcc-s1:i386 libgcc-${GCC_VERSION}-dev:i386 gcc-i686-linux-gnu + + # When cross-compiling, GCC 13 and earlier will look for a linker that + # is marked for cross-compilation, which the Ubuntu lld package doesn't + # provide. The solution is to create a symlink ourselves. GCC 14 fixed + # this, see . + ln -sv "$(which ld.lld)" /usr/local/bin/i686-linux-gnu-ld.lld + - run: rustup target add i686-unknown-linux-gnu + + - name: install libpathrs ${{ env.LIBPATHRS_VERSION }} + run: | + sudo -E PATH="$PATH" ./script/build-libpathrs.sh "$LIBPATHRS_VERSION" /usr 386 + sudo ldconfig /usr/386/lib - name: install go uses: actions/setup-go@v6 @@ -179,6 +200,9 @@ jobs: check-latest: true - name: unit test + env: + CC: i686-linux-gnu-gcc + PKG_CONFIG_PATH: /usr/386/lib/pkgconfig run: sudo -E PATH="$PATH" -- make GOARCH=386 localunittest fedora: diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 1db0d2a7bf5..1fc66987d2a 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -8,11 +8,14 @@ on: - release-* pull_request: workflow_dispatch: -env: - GO_VERSION: 1.25 + permissions: contents: read +env: + GO_VERSION: 1.25 + LIBPATHRS_VERSION: "0.2.4" + jobs: keyring: runs-on: ubuntu-24.04 @@ -85,7 +88,10 @@ jobs: - name: install deps run: | sudo apt update - sudo apt -y install libseccomp-dev + sudo apt -y install libseccomp-dev lld + - name: install libpathrs ${{ env.LIBPATHRS_VERSION }} + run: | + sudo -E PATH="$PATH" ./script/build-libpathrs.sh "$LIBPATHRS_VERSION" /usr - name: compile with no build tags run: make BUILDTAGS="" - name: compile with runc_nocriu build tag diff --git a/script/setup_host_fedora.sh b/script/setup_host_fedora.sh index efe6a001dc1..20ffa56dbb8 100755 --- a/script/setup_host_fedora.sh +++ b/script/setup_host_fedora.sh @@ -1,7 +1,7 @@ #!/bin/bash set -eux -o pipefail DNF=(dnf -y --setopt=install_weak_deps=False --setopt=tsflags=nodocs --exclude="kernel,kernel-core") -RPMS=(bats git-core glibc-static golang jq libseccomp-devel make) +RPMS=(bats git-core glibc-static golang jq libseccomp-devel cargo lld make wget) # Work around dnf mirror failures by retrying a few times. for i in $(seq 0 2); do sleep "$i" @@ -16,11 +16,16 @@ fi dnf clean all +SCRIPTDIR="$(dirname "${BASH_SOURCE[0]}")" + +LIBPATHRS_VERSION="0.2.4" +"$SCRIPTDIR"/build-libpathrs.sh "$LIBPATHRS_VERSION" /usr + # To avoid "avc: denied { nosuid_transition }" from SELinux as we run tests on /tmp. mount -o remount,suid /tmp # Setup rootless user. -"$(dirname "${BASH_SOURCE[0]}")"/setup_rootless.sh +"$SCRIPTDIR"/setup_rootless.sh # Delegate cgroup v2 controllers to rootless user via --systemd-cgroup mkdir -p /etc/systemd/system/user@.service.d From e2c989b7e172a30728da796bd2e30a4413ed0bae Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Wed, 4 Feb 2026 18:38:02 +0100 Subject: [PATCH 10/11] build: enable libpathrs by default libpathrs has better hardening against certain attacks (most notably on older kernels) so we should use it by default. This opens the door to us using cyphar.com/go-pathrs in the future in order to remove some of our internal/pathrs wrappers (that reimplement bits of libpathrs). Signed-off-by: Aleksa Sarai --- CHANGELOG.md | 7 +++++++ Makefile | 2 +- README.md | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f6c669467e..12a2e305849 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `/proc/self/exe` sealing we introduced in runc [1.2.0][]. (#5141) ### Changed ### +- Our release binaries and default build configuration now use [libpathrs][] by + default, providiung better hardening against certain kinds of attacks. Users + of runc should not see any changes as a result of this, but pacakgers will + need to adjust their packaging accordingly. runc can still be built without + libpathrs (by building without the `libpathrs` build tag), but we currently + plan to make runc 1.6 *require* libpathrs. (#5103) - Previously we made an attempt to make our `runc.armhf` release binaries work with ARMv6 (which would allow runc to work on the original Raspberry Pi). Unfortunately, this has effectively always been broken (because we @@ -54,6 +60,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 it) and so we are now officially matching [the Debian definition of `armhf`][debian-armhf] (that is, ARMv7). (#5103) +[libpathrs]: https://github.com/cyphar/libpathrs [debian-armhf]: https://wiki.debian.org/ArmHardFloatPort ## [1.4.0] - 2025-11-27 diff --git a/Makefile b/Makefile index 0a00e660731..de5cc2e6aae 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ GIT_BRANCH_CLEAN := $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g") RUNC_IMAGE := runc_dev$(if $(GIT_BRANCH_CLEAN),:$(GIT_BRANCH_CLEAN)) PROJECT := github.com/opencontainers/runc EXTRA_BUILDTAGS := -BUILDTAGS := seccomp urfave_cli_no_docs +BUILDTAGS := seccomp urfave_cli_no_docs libpathrs BUILDTAGS += $(EXTRA_BUILDTAGS) COMMIT := $(shell git describe --dirty --long --always) diff --git a/README.md b/README.md index cf1df2369d1..84f6b9e9c33 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ make EXTRA_BUILDTAGS="runc_nocriu" | Build Tag | Feature | Enabled by Default | Dependencies | |---------------|---------------------------------------|--------------------|---------------------| | `seccomp` | Syscall filtering using `libseccomp`. | yes | `libseccomp` | -| `libpathrs` | Use [`libpathrs`][] for path safety. | no | [`libpathrs`][] | +| `libpathrs` | Use [`libpathrs`][] for path safety. | yes | [`libpathrs`][] | | `runc_nocriu` | **Disables** runc checkpoint/restore. | no | `criu` | The following build tags were used earlier, but are now obsoleted: From 0e1fe368a2a8da703a026bb80d5c3252a6c6b92c Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Fri, 6 Mar 2026 14:21:22 +1100 Subject: [PATCH 11/11] gha: test both with and without libpathrs We do plan to make libpathrs required in the future, but in the meantime we should test both with and without libpathrs in our CI to catch regressions for users that will not use libpathrs initially. Signed-off-by: Aleksa Sarai --- .github/workflows/test.yml | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a15e0d6c1f8..62c46719dae 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,6 +27,7 @@ jobs: matrix: os: [ubuntu-24.04, ubuntu-24.04-arm] go-version: [1.24.x, 1.25.x, 1.26.x] + libpathrs: ["libpathrs", ""] rootless: ["rootless", ""] race: ["-race", ""] criu: ["", "criu-dev"] @@ -79,9 +80,17 @@ jobs: sudo apt -y install libseccomp-dev sshfs uidmap lld - name: install libpathrs ${{ env.LIBPATHRS_VERSION }} + if: ${{ matrix.libpathrs != '' }} run: | sudo -E PATH="$PATH" ./script/build-libpathrs.sh "$LIBPATHRS_VERSION" /usr + - name: configure custom BUILDTAGS + if: ${{ matrix.libpathrs == '' }} + run: |- + # Strip out libpathrs from the default buildtags. + CUSTOM_BUILDTAGS="$(make -pn | sed -En '/^BUILDTAGS :?=/ { s/.*=// ; s/\blibpathrs\b//p }')" + echo "CUSTOM_BUILDTAGS=$CUSTOM_BUILDTAGS" >>"$GITHUB_ENV" + - name: install CRIU if: ${{ matrix.criu == '' }} env: @@ -112,7 +121,11 @@ jobs: check-latest: true - name: build - run: sudo -E PATH="$PATH" make EXTRA_FLAGS="${{ matrix.race }}" all + run: | + sudo -E PATH="$PATH" make \ + ${CUSTOM_BUILDTAGS:+BUILDTAGS="$CUSTOM_BUILDTAGS"} \ + EXTRA_FLAGS="${{ matrix.race }}" \ + all - name: Setup Bats and bats libs uses: bats-core/bats-action@4.0.0 @@ -131,7 +144,7 @@ jobs: - name: unit test if: matrix.rootless != 'rootless' - run: sudo -E PATH="$PATH" -- make TESTFLAGS="${{ matrix.race }}" localunittest + run: sudo -E PATH="$PATH" -- make ${CUSTOM_BUILDTAGS:+BUILDTAGS="$CUSTOM_BUILDTAGS"} TESTFLAGS="${{ matrix.race }}" localunittest - name: add rootless user if: matrix.rootless == 'rootless' @@ -141,7 +154,7 @@ jobs: - name: integration test (fs driver) continue-on-error: ${{ matrix.criu != '' }} # Don't let criu-dev errors fail CI. - run: sudo -E PATH="$PATH" script -e -c 'make local${{ matrix.rootless }}integration' + run: sudo -E PATH="$PATH" script -e -c 'make ${CUSTOM_BUILDTAGS:+BUILDTAGS="$CUSTOM_BUILDTAGS"} local${{ matrix.rootless }}integration' - name: integration test (systemd driver) continue-on-error: ${{ matrix.criu != '' }} # Don't let criu-dev errors fail CI. @@ -152,7 +165,7 @@ jobs: printf "[Service]\nDelegate=yes\n" | sudo tee /etc/systemd/system/user@.service.d/delegate.conf sudo systemctl daemon-reload # Run the tests. - sudo -E PATH="$PATH" script -e -c 'make RUNC_USE_SYSTEMD=yes local${{ matrix.rootless }}integration' + sudo -E PATH="$PATH" script -e -c 'make RUNC_USE_SYSTEMD=yes ${CUSTOM_BUILDTAGS:+BUILDTAGS="$CUSTOM_BUILDTAGS"} local${{ matrix.rootless }}integration' # We need to continue support for 32-bit ARM. # However, we do not have 32-bit ARM CI, so we use i386 for testing 32bit stuff.