diff --git a/.cirrus.yml b/.cirrus.yml index e182c61063f..2244a72ba34 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 + LIBPATHRS_VERSION: "0.2.3" + RPMS: gcc git-core iptables jq glibc-static libseccomp-devel make criu fuse-sshfs container-selinux cargo lld # 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..1f3cc6f3735 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,6 +26,7 @@ jobs: matrix: os: [ubuntu-24.04, ubuntu-24.04-arm] go-version: [1.24.x, 1.25.x, 1.26.x] + libpathrs-version: [&LIBPATHRS_VERSION "0.2.3"] rootless: ["rootless", ""] race: ["-race", ""] criu: ["", "criu-dev"] @@ -75,7 +76,13 @@ 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 v${{ matrix.libpathrs-version }} + env: + LIBPATHRS_VERSION: (${{ matrix.libpathrs-version }}) + run: | + sudo -E PATH="$PATH" ./script/build-libpathrs.sh "$LIBPATHRS_VERSION" /usr - name: install CRIU if: ${{ matrix.criu == '' }} @@ -170,7 +177,24 @@ 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 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. + # 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: *LIBPATHRS_VERSION + run: | + sudo -E PATH="$PATH" ./script/build-libpathrs.sh "$LIBPATHRS_VERSION" /usr 386 - name: install go uses: actions/setup-go@v6 @@ -179,6 +203,8 @@ jobs: check-latest: true - name: unit test + env: + 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 ab8c4e2538a..e90a486a189 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -85,7 +85,12 @@ 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: "0.2.3" + 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/Dockerfile b/Dockerfile index e4f3df3545b..ce6707b3301 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,22 @@ ARG GO_VERSION=1.25 ARG BATS_VERSION=v1.12.0 ARG LIBSECCOMP_VERSION=2.6.0 +ARG LIBPATHRS_VERSION=0.2.3 -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" \ && 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 @@ -54,14 +59,25 @@ 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/ -RUN mkdir -p /opt/libseccomp \ - && /tmp/script/seccomp.sh "$LIBSECCOMP_VERSION" /opt/libseccomp 386 amd64 arm64 armel armhf ppc64le riscv64 s390x +COPY script/build-seccomp.sh script/lib.sh /tmp/script/ +RUN mkdir -p $DYLIB_DIR \ + && /tmp/script/build-seccomp.sh "$LIBSECCOMP_VERSION" $DYLIB_DIR $RELEASE_ARCHES ENV LIBSECCOMP_VERSION=$LIBSECCOMP_VERSION -ENV LD_LIBRARY_PATH=/opt/libseccomp/lib -ENV PKG_CONFIG_PATH=/opt/libseccomp/lib/pkgconfig + +# 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 # 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/Makefile b/Makefile index dba7d7a8f81..91b4b84a99b 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 bca08c2d284..84f6b9e9c33 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. | yes | [`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. diff --git a/go.mod b/go.mod index b9e866a7c66..814f1bc6d84 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-0.20260212074101-c4e98bea7862 // 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..499c4a13d80 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-0.20260212074101-c4e98bea7862 h1:Kcs6zS5vHgOP/0NV7KaTLUJcB+BTlDufueHvBbqwnSg= +cyphar.com/go-pathrs v0.2.4-0.20260212074101-c4e98bea7862/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/script/build-libpathrs.sh b/script/build-libpathrs.sh new file mode 100755 index 00000000000..20ebedb6734 --- /dev/null +++ b/script/build-libpathrs.sh @@ -0,0 +1,153 @@ +#!/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.2"]=95978036c0f0d2e67f628fc06ccac090656606bee6632e437eac21d68b00504f + ["0.2.3"]=1e7826b64e41940e8f62ca92dde7ae1459a35727a3aeb172cc11d48b4727aeed +) + +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. +function build_libpathrs() { + local ver="$1" + shift + local dest="$1" + shift + local go_arches=("$@") + local tar="libpathrs-${ver}.tar.gz" + + # Download, check, and extract. + # TODO: Signatures and releases. + #wget "https://github.com/cyphar/libpathrs/releases/download/v${ver}/${tar}"{,.asc} + #wget "https://github.com/cyphar/libpathrs/archive/refs/tags/v${ver}.tar.gz" -O "$tar" + #sha256sum --strict --check - <<<"${LIBPATHRS_SHA256[${ver}]} *${tar}" + + local srcdir + srcdir="$(mktemp -d)" + #tar xf "$tar" -C "$srcdir" + #pushd "$srcdir/libpathrs-$ver" || return + git clone https://github.com/cyphar/libpathrs.git "$srcdir/libpathrs" + pushd "$srcdir/libpathrs" || return + + # Use cargo-auditable if available. + if cargo auditable --version &>/dev/null; then + export CARGO="cargo auditable" + fi + 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 configure cross-compile config when we 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 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 + + popd +} + +if [ $# -lt 2 ]; then + echo "Usage: $0 [ ...]" >&2 + exit 1 +fi + +build_libpathrs "$@" 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/lib.sh b/script/lib.sh index 9e4139756a3..d3e68f646f6 100644 --- a/script/lib.sh +++ b/script/lib.sh @@ -1,41 +1,53 @@ #!/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 + 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} @@ -48,16 +60,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} diff --git a/script/release_build.sh b/script/release_build.sh index 0e0a221839e..bd4369d02cb 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.3}" project="runc" root="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")" @@ -40,14 +41,16 @@ 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/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: @@ -70,7 +73,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 +88,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. diff --git a/script/setup_host_fedora.sh b/script/setup_host_fedora.sh index efe6a001dc1..e36fd0d804e 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) # 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.3 +"$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 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 ] 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..c77776631ad 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-0.20260212074101-c4e98bea7862 ## explicit; go 1.18 cyphar.com/go-pathrs cyphar.com/go-pathrs/internal/fdutils