Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Step:FrontRunner
kubevirt.28006
0002-CVE-2022-29162-Bump-runc-dependency-to-1.1...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0002-CVE-2022-29162-Bump-runc-dependency-to-1.1.2.patch of Package kubevirt.28006
From 660ca5e367ed4f7eb0f35c903bac5815bb03f297 Mon Sep 17 00:00:00 2001 From: orenc1 <ocohen@redhat.com> Date: Thu, 14 Jul 2022 19:44:23 +0300 Subject: [PATCH 2/3] [CVE-2022-29162] Bump runc dependency to 1.1.2 CVE-2022-29162 has been found in runc version that is currently being used. Bumping it to v1.1.2 as suggested in: GHSA-f3fp-gc8g-vw66 runc v1.1.2 introduced some changes: rootless flag is now embedded in the Cgroup config struct runc_fs.NewManager() now returns also an error value. This PR accommodates these updates. Signed-off-by: orenc1 <ocohen@redhat.com> --- cmd/virt-chroot/cgroup.go | 18 +- go.mod | 12 +- go.sum | 23 +- pkg/virt-handler/cgroup/cgroup.go | 5 +- pkg/virt-handler/cgroup/cgroup_v1_manager.go | 13 +- pkg/virt-handler/cgroup/cgroup_v2_manager.go | 10 +- vendor/github.com/cilium/ebpf/.gitignore | 1 + vendor/github.com/cilium/ebpf/.golangci.yaml | 1 - vendor/github.com/cilium/ebpf/ARCHITECTURE.md | 4 +- vendor/github.com/cilium/ebpf/BUILD.bazel | 1 + vendor/github.com/cilium/ebpf/CONTRIBUTING.md | 4 +- vendor/github.com/cilium/ebpf/Makefile | 5 +- vendor/github.com/cilium/ebpf/README.md | 62 ++- vendor/github.com/cilium/ebpf/asm/func.go | 6 + .../github.com/cilium/ebpf/asm/func_string.go | 10 +- .../github.com/cilium/ebpf/asm/instruction.go | 5 + .../cilium/ebpf/attachtype_string.go | 65 +++ vendor/github.com/cilium/ebpf/collection.go | 508 ++++++++++-------- vendor/github.com/cilium/ebpf/elf_reader.go | 180 ++++++- .../github.com/cilium/ebpf/elf_reader_fuzz.go | 1 + vendor/github.com/cilium/ebpf/info.go | 36 +- .../cilium/ebpf/internal/BUILD.bazel | 1 + .../github.com/cilium/ebpf/internal/align.go | 6 + .../cilium/ebpf/internal/btf/BUILD.bazel | 2 + .../cilium/ebpf/internal/btf/btf.go | 293 +++++----- .../cilium/ebpf/internal/btf/btf_types.go | 11 +- .../cilium/ebpf/internal/btf/core.go | 33 +- .../cilium/ebpf/internal/btf/ext_info.go | 15 +- .../cilium/ebpf/internal/btf/fuzz.go | 1 + .../cilium/ebpf/internal/btf/info.go | 48 ++ .../cilium/ebpf/internal/btf/strings.go | 8 +- .../cilium/ebpf/internal/btf/syscalls.go | 31 ++ .../cilium/ebpf/internal/btf/types.go | 188 ++++--- vendor/github.com/cilium/ebpf/internal/cpu.go | 4 +- .../cilium/ebpf/internal/ptr_32_be.go | 1 + .../cilium/ebpf/internal/ptr_32_le.go | 1 + .../github.com/cilium/ebpf/internal/ptr_64.go | 4 +- .../cilium/ebpf/internal/syscall.go | 59 ++ .../cilium/ebpf/internal/unix/types_linux.go | 16 +- .../cilium/ebpf/internal/unix/types_other.go | 14 +- .../cilium/ebpf/internal/version.go | 4 +- .../github.com/cilium/ebpf/link/BUILD.bazel | 2 + .../github.com/cilium/ebpf/link/freplace.go | 88 +++ vendor/github.com/cilium/ebpf/link/kprobe.go | 38 +- vendor/github.com/cilium/ebpf/link/link.go | 14 +- .../github.com/cilium/ebpf/link/perf_event.go | 7 +- .../github.com/cilium/ebpf/link/syscalls.go | 9 +- .../github.com/cilium/ebpf/link/tracepoint.go | 8 +- vendor/github.com/cilium/ebpf/link/uprobe.go | 133 +++-- vendor/github.com/cilium/ebpf/linker.go | 23 +- vendor/github.com/cilium/ebpf/map.go | 95 ++-- vendor/github.com/cilium/ebpf/marshalers.go | 63 ++- vendor/github.com/cilium/ebpf/prog.go | 123 +++-- vendor/github.com/cilium/ebpf/run-tests.sh | 22 +- vendor/github.com/cilium/ebpf/syscalls.go | 94 ++-- vendor/github.com/cilium/ebpf/types.go | 36 +- vendor/github.com/cilium/ebpf/types_string.go | 69 +-- .../cyphar/filepath-securejoin/.travis.yml | 8 +- .../cyphar/filepath-securejoin/BUILD.bazel | 1 - .../cyphar/filepath-securejoin/README.md | 20 +- .../cyphar/filepath-securejoin/VERSION | 2 +- .../cyphar/filepath-securejoin/join.go | 25 +- .../cyphar/filepath-securejoin/vendor.conf | 1 - vendor/github.com/godbus/dbus/v5/BUILD.bazel | 1 + .../dbus/v5/{README.markdown => README.md} | 12 +- vendor/github.com/godbus/dbus/v5/auth.go | 15 +- vendor/github.com/godbus/dbus/v5/conn.go | 36 +- vendor/github.com/godbus/dbus/v5/decoder.go | 16 +- vendor/github.com/godbus/dbus/v5/encoder.go | 61 ++- vendor/github.com/godbus/dbus/v5/export.go | 37 +- vendor/github.com/godbus/dbus/v5/message.go | 73 ++- vendor/github.com/godbus/dbus/v5/sig.go | 66 ++- .../godbus/dbus/v5/transport_generic.go | 10 +- .../godbus/dbus/v5/transport_unix.go | 24 +- .../dbus/v5/transport_unixcred_netbsd.go | 14 + .../github.com/moby/sys/mountinfo/BUILD.bazel | 6 + .../moby/sys/mountinfo/mounted_linux.go | 11 +- .../moby/sys/mountinfo/mounted_unix.go | 16 +- .../moby/sys/mountinfo/mountinfo.go | 9 +- .../moby/sys/mountinfo/mountinfo_bsd.go | 9 +- .../moby/sys/mountinfo/mountinfo_linux.go | 27 +- .../sys/mountinfo/mountinfo_unsupported.go | 3 +- .../runc/libcontainer/cgroups/BUILD.bazel | 17 +- .../runc/libcontainer/cgroups/cgroups.go | 2 - .../cgroups/cgroups_unsupported.go | 3 - .../libcontainer/cgroups/devices/BUILD.bazel | 12 +- .../cgroups/devices/devices_emulator.go | 66 +-- .../libcontainer/cgroups/ebpf/BUILD.bazel | 2 - .../cgroups/ebpf/devicefilter/BUILD.bazel | 1 - .../cgroups/ebpf/devicefilter/devicefilter.go | 22 +- .../libcontainer/cgroups/ebpf/ebpf_linux.go | 4 +- .../runc/libcontainer/cgroups/file.go | 60 ++- .../runc/libcontainer/cgroups/fs/BUILD.bazel | 42 +- .../runc/libcontainer/cgroups/fs/blkio.go | 18 +- .../runc/libcontainer/cgroups/fs/cpu.go | 22 +- .../runc/libcontainer/cgroups/fs/cpuacct.go | 46 +- .../runc/libcontainer/cgroups/fs/cpuset.go | 47 +- .../runc/libcontainer/cgroups/fs/devices.go | 13 +- .../runc/libcontainer/cgroups/fs/error.go | 15 + .../runc/libcontainer/cgroups/fs/freezer.go | 6 +- .../runc/libcontainer/cgroups/fs/fs.go | 318 +++-------- .../runc/libcontainer/cgroups/fs/hugetlb.go | 17 +- .../runc/libcontainer/cgroups/fs/memory.go | 56 +- .../runc/libcontainer/cgroups/fs/name.go | 8 +- .../runc/libcontainer/cgroups/fs/net_cls.go | 6 +- .../runc/libcontainer/cgroups/fs/net_prio.go | 6 +- .../runc/libcontainer/cgroups/fs/paths.go | 186 +++++++ .../libcontainer/cgroups/fs/perf_event.go | 6 +- .../runc/libcontainer/cgroups/fs/pids.go | 28 +- .../runc/libcontainer/cgroups/fs/rdma.go | 25 + .../libcontainer/cgroups/fs/unsupported.go | 3 - .../runc/libcontainer/cgroups/fs2/BUILD.bazel | 30 +- .../runc/libcontainer/cgroups/fs2/cpu.go | 10 +- .../runc/libcontainer/cgroups/fs2/cpuset.go | 2 - .../libcontainer/cgroups/fs2/defaultpath.go | 26 +- .../runc/libcontainer/cgroups/fs2/devices.go | 11 +- .../runc/libcontainer/cgroups/fs2/freezer.go | 18 +- .../runc/libcontainer/cgroups/fs2/fs2.go | 52 +- .../runc/libcontainer/cgroups/fs2/hugetlb.go | 10 +- .../runc/libcontainer/cgroups/fs2/io.go | 13 +- .../runc/libcontainer/cgroups/fs2/memory.go | 29 +- .../runc/libcontainer/cgroups/fs2/pids.go | 29 +- .../libcontainer/cgroups/fscommon/BUILD.bazel | 19 +- .../libcontainer/cgroups/fscommon/rdma.go | 121 +++++ .../libcontainer/cgroups/fscommon/utils.go | 34 +- .../runc/libcontainer/cgroups/getallpids.go | 27 + .../runc/libcontainer/cgroups/stats.go | 16 +- .../runc/libcontainer/cgroups/utils.go | 115 ++-- .../runc/libcontainer/cgroups/v1_utils.go | 19 +- .../runc/libcontainer/configs/BUILD.bazel | 5 +- .../runc/libcontainer/configs/cgroup_linux.go | 20 +- .../configs/cgroup_unsupported.go | 1 + .../runc/libcontainer/configs/config.go | 34 +- .../runc/libcontainer/configs/config_linux.go | 17 +- .../libcontainer/configs/configs_fuzzer.go | 1 + .../runc/libcontainer/configs/intelrdt.go | 3 + .../runc/libcontainer/configs/mount.go | 9 + .../configs/namespaces_syscall.go | 1 + .../configs/namespaces_syscall_unsupported.go | 1 + .../configs/namespaces_unsupported.go | 1 + .../runc/libcontainer/configs/rdma.go | 9 + .../runc/libcontainer/devices/device_unix.go | 12 +- .../runc/libcontainer/user/lookup_unix.go | 1 + .../runc/libcontainer/user/user.go | 21 +- .../runc/libcontainer/user/user_fuzzer.go | 1 + .../runc/libcontainer/userns/userns_fuzzer.go | 1 + .../libcontainer/userns/userns_unsupported.go | 1 + .../runc/libcontainer/utils/cmsg.go | 11 +- .../runc/libcontainer/utils/utils.go | 14 +- .../runc/libcontainer/utils/utils_unix.go | 5 +- vendor/modules.txt | 20 +- 151 files changed, 3019 insertions(+), 1845 deletions(-) create mode 100644 vendor/github.com/cilium/ebpf/attachtype_string.go create mode 100644 vendor/github.com/cilium/ebpf/internal/align.go create mode 100644 vendor/github.com/cilium/ebpf/internal/btf/info.go create mode 100644 vendor/github.com/cilium/ebpf/internal/btf/syscalls.go create mode 100644 vendor/github.com/cilium/ebpf/link/freplace.go delete mode 100644 vendor/github.com/cyphar/filepath-securejoin/vendor.conf rename vendor/github.com/godbus/dbus/v5/{README.markdown => README.md} (81%) create mode 100644 vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go delete mode 100644 vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups_unsupported.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/error.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/paths.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/rdma.go delete mode 100644 vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/unsupported.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/rdma.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/cgroups/getallpids.go create mode 100644 vendor/github.com/opencontainers/runc/libcontainer/configs/rdma.go diff --git a/cmd/virt-chroot/cgroup.go b/cmd/virt-chroot/cgroup.go index 0b315177d..034c35229 100644 --- a/cmd/virt-chroot/cgroup.go +++ b/cmd/virt-chroot/cgroup.go @@ -54,15 +54,16 @@ func decodePaths(marshalledPathsHash string) (map[string]string, error) { func setCgroupResources(paths map[string]string, resources *runc_configs.Resources, isRootless bool, isV2 bool) error { config := &runc_configs.Cgroup{ Path: cgroup.HostCgroupBasePath, - Resources: &runc_configs.Resources{}, + Resources: resources, + Rootless: isRootless, } var err error if isV2 { - err = setCgroupResourcesV2(paths, resources, isRootless, config) + err = setCgroupResourcesV2(paths, resources, config) } else { - err = setCgroupResourcesV1(paths, resources, isRootless, config) + err = setCgroupResourcesV1(paths, resources, config) } if err != nil { @@ -72,17 +73,20 @@ func setCgroupResources(paths map[string]string, resources *runc_configs.Resourc return nil } -func setCgroupResourcesV1(paths map[string]string, resources *runc_configs.Resources, isRootless bool, config *runc_configs.Cgroup) error { +func setCgroupResourcesV1(paths map[string]string, resources *runc_configs.Resources, config *runc_configs.Cgroup) error { return RunWithChroot(cgroup.HostCgroupBasePath, func() error { - cgroupManager := runc_fs.NewManager(config, paths, isRootless) + cgroupManager, err := runc_fs.NewManager(config, paths) + if err != nil { + return fmt.Errorf("cannot create cgroups v1 manager. err: %v", err) + } return cgroupManager.Set(resources) }) } -func setCgroupResourcesV2(paths map[string]string, resources *runc_configs.Resources, isRootless bool, config *runc_configs.Cgroup) error { +func setCgroupResourcesV2(paths map[string]string, resources *runc_configs.Resources, config *runc_configs.Cgroup) error { cgroupDirPath := paths[""] - cgroupManager, err := runc_fs2.NewManager(config, cgroupDirPath, isRootless) + cgroupManager, err := runc_fs2.NewManager(config, cgroupDirPath) if err != nil { return fmt.Errorf("cannot create cgroups v2 manager. err: %v", err) } diff --git a/go.mod b/go.mod index 19a474ac3..1ca93ba9a 100644 --- a/go.mod +++ b/go.mod @@ -35,11 +35,11 @@ require ( github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed - github.com/moby/sys/mountinfo v0.4.1 + github.com/moby/sys/mountinfo v0.5.0 github.com/onsi/ginkgo/v2 v2.1.3 github.com/onsi/gomega v1.18.1 - github.com/opencontainers/runc v1.0.3 - github.com/opencontainers/selinux v1.8.2 + github.com/opencontainers/runc v1.1.2 + github.com/opencontainers/selinux v1.10.0 github.com/openshift/api v0.0.0 github.com/openshift/client-go v0.0.0 github.com/openshift/library-go v0.0.0-20210205203934-9eb0d970f2f4 @@ -95,9 +95,9 @@ require ( github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect - github.com/cilium/ebpf v0.6.2 // indirect + github.com/cilium/ebpf v0.7.0 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect - github.com/cyphar/filepath-securejoin v0.2.2 // indirect + github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/go-logfmt/logfmt v0.5.0 // indirect @@ -110,7 +110,7 @@ require ( github.com/go-openapi/swag v0.21.1 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect - github.com/godbus/dbus/v5 v5.0.4 // indirect + github.com/godbus/dbus/v5 v5.0.6 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/go-cmp v0.5.6 // indirect github.com/google/go-querystring v1.0.0 // indirect diff --git a/go.sum b/go.sum index 053375564..1a5ebddb8 100644 --- a/go.sum +++ b/go.sum @@ -192,6 +192,7 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -200,8 +201,9 @@ github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLI github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.6.1/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.6.2 h1:iHsfF/t4aW4heW2YKfeHrVPGdtYTL4C4KocpM8KTSnI= github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.7.0 h1:1k/q3ATgxSXRdrmPfH8d7YK0GfqVsEKZAX9dQZvs56k= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -235,6 +237,7 @@ github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= @@ -336,8 +339,9 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= @@ -607,8 +611,9 @@ github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblf github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -987,8 +992,9 @@ github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQ github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM= github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.5.0 h1:2Ks8/r6lopsxWi9m58nlwjaeSzUX9iiL1vj5qB/9ObI= +github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1059,8 +1065,9 @@ github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rm github.com/opencontainers/runc v1.0.0-rc90/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= github.com/opencontainers/runc v1.0.0/go.mod h1:MU2S3KEB2ZExnhnAQYbwjdYV6HwKtDlNbA2Z2OeNDeA= -github.com/opencontainers/runc v1.0.3 h1:1hbqejyQWCJBvtKAfdO0b1FmaEf2z/bxnjqbARass5k= github.com/opencontainers/runc v1.0.3/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runc v1.1.2 h1:2VSZwLx5k/BfsBxMMipG/LYUnmqOD/BPkIVgQUcTlLw= +github.com/opencontainers/runc v1.1.2/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -1207,8 +1214,9 @@ github.com/satori/go.uuid v0.0.0-20160603004225-b111a074d5ef/go.mod h1:dA0hQrYB0 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sclevine/spec v1.0.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921 h1:58EBmR2dMNL2n/FnbQewK3D14nXr0V9CObDSvMJLq+Y= +github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -1696,7 +1704,10 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/virt-handler/cgroup/cgroup.go b/pkg/virt-handler/cgroup/cgroup.go index 113a8f8de..7a8e98058 100644 --- a/pkg/virt-handler/cgroup/cgroup.go +++ b/pkg/virt-handler/cgroup/cgroup.go @@ -72,12 +72,13 @@ func NewManagerFromPid(pid int) (manager Manager, err error) { config := &configs.Cgroup{ Path: HostCgroupBasePath, Resources: &configs.Resources{}, + Rootless: isRootless, } if runc_cgroups.IsCgroup2UnifiedMode() { version = V2 slicePath := filepath.Join(cgroupBasePath, controllerPaths[""]) - manager, err = newV2Manager(config, slicePath, isRootless) + manager, err = newV2Manager(config, slicePath) } else { version = V1 for subsystem, path := range controllerPaths { @@ -87,7 +88,7 @@ func NewManagerFromPid(pid int) (manager Manager, err error) { controllerPaths[subsystem] = filepath.Join("/", subsystem, path) } - manager, err = newV1Manager(config, controllerPaths, isRootless) + manager, err = newV1Manager(config, controllerPaths) } if err != nil { diff --git a/pkg/virt-handler/cgroup/cgroup_v1_manager.go b/pkg/virt-handler/cgroup/cgroup_v1_manager.go index 7d7af652c..97c690aec 100644 --- a/pkg/virt-handler/cgroup/cgroup_v1_manager.go +++ b/pkg/virt-handler/cgroup/cgroup_v1_manager.go @@ -23,17 +23,20 @@ type v1Manager struct { getCurrentlyDefinedRules getCurrentlyDefinedRulesFunc } -func newV1Manager(config *runc_configs.Cgroup, controllerPaths map[string]string, rootless bool) (Manager, error) { - return newCustomizedV1Manager(config, controllerPaths, rootless, execVirtChrootCgroups, getCurrentlyDefinedRules) +func newV1Manager(config *runc_configs.Cgroup, controllerPaths map[string]string) (Manager, error) { + return newCustomizedV1Manager(config, controllerPaths, execVirtChrootCgroups, getCurrentlyDefinedRules) } -func newCustomizedV1Manager(config *runc_configs.Cgroup, controllerPaths map[string]string, rootless bool, +func newCustomizedV1Manager(config *runc_configs.Cgroup, controllerPaths map[string]string, execVirtChroot execVirtChrootFunc, getCurrentlyDefinedRules getCurrentlyDefinedRulesFunc) (Manager, error) { - runcManager := runc_fs.NewManager(config, controllerPaths, rootless) + runcManager, err := runc_fs.NewManager(config, controllerPaths) + if err != nil { + return nil, fmt.Errorf("cannot initialize new cgroup manager. err: %v", err) + } manager := v1Manager{ runcManager, controllerPaths, - rootless, + config.Rootless, execVirtChroot, getCurrentlyDefinedRules, } diff --git a/pkg/virt-handler/cgroup/cgroup_v2_manager.go b/pkg/virt-handler/cgroup/cgroup_v2_manager.go index c4717912d..932cabf8f 100644 --- a/pkg/virt-handler/cgroup/cgroup_v2_manager.go +++ b/pkg/virt-handler/cgroup/cgroup_v2_manager.go @@ -16,16 +16,16 @@ type v2Manager struct { execVirtChroot execVirtChrootFunc } -func newV2Manager(config *runc_configs.Cgroup, dirPath string, rootless bool) (Manager, error) { - return newCustomizedV2Manager(config, dirPath, rootless, execVirtChrootCgroups) +func newV2Manager(config *runc_configs.Cgroup, dirPath string) (Manager, error) { + return newCustomizedV2Manager(config, dirPath, execVirtChrootCgroups) } -func newCustomizedV2Manager(config *runc_configs.Cgroup, dirPath string, rootless bool, execVirtChroot execVirtChrootFunc) (Manager, error) { - runcManager, err := runc_fs.NewManager(config, dirPath, rootless) +func newCustomizedV2Manager(config *runc_configs.Cgroup, dirPath string, execVirtChroot execVirtChrootFunc) (Manager, error) { + runcManager, err := runc_fs.NewManager(config, dirPath) manager := v2Manager{ runcManager, dirPath, - rootless, + config.Rootless, execVirtChroot, } diff --git a/vendor/github.com/cilium/ebpf/.gitignore b/vendor/github.com/cilium/ebpf/.gitignore index 38b15653c..b46162b8e 100644 --- a/vendor/github.com/cilium/ebpf/.gitignore +++ b/vendor/github.com/cilium/ebpf/.gitignore @@ -5,6 +5,7 @@ *.so *.dylib *.o +!*_bpf*.o # Test binary, build with `go test -c` *.test diff --git a/vendor/github.com/cilium/ebpf/.golangci.yaml b/vendor/github.com/cilium/ebpf/.golangci.yaml index a88374197..dc62dd6d0 100644 --- a/vendor/github.com/cilium/ebpf/.golangci.yaml +++ b/vendor/github.com/cilium/ebpf/.golangci.yaml @@ -24,6 +24,5 @@ linters: # Could be enabled later: # - gocyclo - # - prealloc # - maligned # - gosec diff --git a/vendor/github.com/cilium/ebpf/ARCHITECTURE.md b/vendor/github.com/cilium/ebpf/ARCHITECTURE.md index aee9c0a0d..6cbb31b64 100644 --- a/vendor/github.com/cilium/ebpf/ARCHITECTURE.md +++ b/vendor/github.com/cilium/ebpf/ARCHITECTURE.md @@ -57,7 +57,7 @@ Objects loading a spec will fail because the kernel is too old, or a feature is not enabled. There are multiple ways the library deals with that: -* Fallback: older kernels don't allowing naming programs and maps. The library +* Fallback: older kernels don't allow naming programs and maps. The library automatically detects support for names, and omits them during load if necessary. This works since name is primarily a debug aid. @@ -68,7 +68,7 @@ enabled. There are multiple ways the library deals with that: Once program and map objects are loaded they expose the kernel's low-level API, e.g. `NextKey`. Often this API is awkward to use in Go, so there are safer wrappers on top of the low-level API, like `MapIterator`. The low-level API is -useful as an out when our higher-level API doesn't support a particular use case. +useful when our higher-level API doesn't support a particular use case. Links --- diff --git a/vendor/github.com/cilium/ebpf/BUILD.bazel b/vendor/github.com/cilium/ebpf/BUILD.bazel index 7ff348721..0a0bbbdbf 100644 --- a/vendor/github.com/cilium/ebpf/BUILD.bazel +++ b/vendor/github.com/cilium/ebpf/BUILD.bazel @@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "go_default_library", srcs = [ + "attachtype_string.go", "collection.go", "doc.go", "elf_reader.go", diff --git a/vendor/github.com/cilium/ebpf/CONTRIBUTING.md b/vendor/github.com/cilium/ebpf/CONTRIBUTING.md index 72ceb4378..0d29eae81 100644 --- a/vendor/github.com/cilium/ebpf/CONTRIBUTING.md +++ b/vendor/github.com/cilium/ebpf/CONTRIBUTING.md @@ -6,8 +6,8 @@ are welcome. Please take a look at [the architecture](ARCHITECTURE.md) to get a better understanding for the high-level goals. New features must be accompanied by tests. Before starting work on any large -feature, please [join](https://cilium.herokuapp.com/) the -[#libbpf-go](https://cilium.slack.com/messages/libbpf-go) channel on Slack to +feature, please [join](https://ebpf.io/slack) the +[#ebpf-go](https://cilium.slack.com/messages/ebpf-go) channel on Slack to discuss the design first. When submitting pull requests, consider writing details about what problem you diff --git a/vendor/github.com/cilium/ebpf/Makefile b/vendor/github.com/cilium/ebpf/Makefile index 5dd342c5b..0bc15c081 100644 --- a/vendor/github.com/cilium/ebpf/Makefile +++ b/vendor/github.com/cilium/ebpf/Makefile @@ -18,11 +18,14 @@ TARGETS := \ testdata/loader-clang-7 \ testdata/loader-clang-9 \ testdata/loader-$(CLANG) \ + testdata/btf_map_init \ testdata/invalid_map \ testdata/raw_tracepoint \ testdata/invalid_map_static \ - testdata/initialized_btf_map \ + testdata/invalid_btf_map_init \ testdata/strings \ + testdata/freplace \ + testdata/iproute2_map_compat \ internal/btf/testdata/relocs .PHONY: all clean docker-all docker-shell diff --git a/vendor/github.com/cilium/ebpf/README.md b/vendor/github.com/cilium/ebpf/README.md index 76c3c303b..01e2fff92 100644 --- a/vendor/github.com/cilium/ebpf/README.md +++ b/vendor/github.com/cilium/ebpf/README.md @@ -2,28 +2,16 @@ [![PkgGoDev](https://pkg.go.dev/badge/github.com/cilium/ebpf)](https://pkg.go.dev/github.com/cilium/ebpf) +![HoneyGopher](.github/images/cilium-ebpf.png) + eBPF is a pure Go library that provides utilities for loading, compiling, and debugging eBPF programs. It has minimal external dependencies and is intended to be used in long running processes. -* [asm](https://pkg.go.dev/github.com/cilium/ebpf/asm) contains a basic - assembler -* [link](https://pkg.go.dev/github.com/cilium/ebpf/link) allows attaching eBPF - to various hooks -* [perf](https://pkg.go.dev/github.com/cilium/ebpf/perf) allows reading from a - `PERF_EVENT_ARRAY` -* [cmd/bpf2go](https://pkg.go.dev/github.com/cilium/ebpf/cmd/bpf2go) allows - compiling and embedding eBPF programs in Go code - The library is maintained by [Cloudflare](https://www.cloudflare.com) and -[Cilium](https://www.cilium.io). Feel free to -[join](https://cilium.herokuapp.com/) the -[#libbpf-go](https://cilium.slack.com/messages/libbpf-go) channel on Slack. - -## Current status +[Cilium](https://www.cilium.io). -The package is production ready, but **the API is explicitly unstable right -now**. Expect to update your code if you want to follow along. +See [ebpf.io](https://ebpf.io) for other projects from the eBPF ecosystem. ## Getting Started @@ -33,21 +21,37 @@ your own tools can be found under [examples/](examples/). Contributions are highly encouraged, as they highlight certain use cases of eBPF and the library, and help shape the future of the project. +## Getting Help + +Please +[join](https://ebpf.io/slack) the +[#ebpf-go](https://cilium.slack.com/messages/ebpf-go) channel on Slack if you +have questions regarding the library. + +## Packages + +This library includes the following packages: + +* [asm](https://pkg.go.dev/github.com/cilium/ebpf/asm) contains a basic + assembler, allowing you to write eBPF assembly instructions directly + within your Go code. (You don't need to use this if you prefer to write your eBPF program in C.) +* [cmd/bpf2go](https://pkg.go.dev/github.com/cilium/ebpf/cmd/bpf2go) allows + compiling and embedding eBPF programs written in C within Go code. As well as + compiling the C code, it auto-generates Go code for loading and manipulating + the eBPF program and map objects. +* [link](https://pkg.go.dev/github.com/cilium/ebpf/link) allows attaching eBPF + to various hooks +* [perf](https://pkg.go.dev/github.com/cilium/ebpf/perf) allows reading from a + `PERF_EVENT_ARRAY` +* [ringbuf](https://pkg.go.dev/github.com/cilium/ebpf/ringbuf) allows reading from a + `BPF_MAP_TYPE_RINGBUF` map + + ## Requirements * A version of Go that is [supported by upstream](https://golang.org/doc/devel/release.html#policy) -* Linux 4.9, 4.19 or 5.4 (versions in-between should work, but are not tested) - -## Useful resources - -* [eBPF.io](https://ebpf.io) (recommended) -* [Cilium eBPF documentation](https://docs.cilium.io/en/latest/bpf/#bpf-guide) - (recommended) -* [Linux documentation on - BPF](https://www.kernel.org/doc/html/latest/networking/filter.html) -* [eBPF features by Linux - version](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md) +* Linux >= 4.9. CI is run against LTS releases. ## Regenerating Testdata @@ -60,3 +64,7 @@ The toolchain image build files are kept in [testdata/docker/](testdata/docker/) ## License MIT + +### eBPF Gopher + +The eBPF honeygopher is based on the Go gopher designed by Renee French. diff --git a/vendor/github.com/cilium/ebpf/asm/func.go b/vendor/github.com/cilium/ebpf/asm/func.go index aee2c7ac8..bfa5d59c9 100644 --- a/vendor/github.com/cilium/ebpf/asm/func.go +++ b/vendor/github.com/cilium/ebpf/asm/func.go @@ -184,6 +184,12 @@ const ( FnKtimeGetCoarseNs FnImaInodeHash FnSockFromFile + FnCheckMtu + FnForEachMapElem + FnSnprintf + FnSysBpf + FnBtfFindByNameKind + FnSysClose ) // Call emits a function call. diff --git a/vendor/github.com/cilium/ebpf/asm/func_string.go b/vendor/github.com/cilium/ebpf/asm/func_string.go index a712c5da8..5a0e33363 100644 --- a/vendor/github.com/cilium/ebpf/asm/func_string.go +++ b/vendor/github.com/cilium/ebpf/asm/func_string.go @@ -171,11 +171,17 @@ func _() { _ = x[FnKtimeGetCoarseNs-160] _ = x[FnImaInodeHash-161] _ = x[FnSockFromFile-162] + _ = x[FnCheckMtu-163] + _ = x[FnForEachMapElem-164] + _ = x[FnSnprintf-165] + _ = x[FnSysBpf-166] + _ = x[FnBtfFindByNameKind-167] + _ = x[FnSysClose-168] } -const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFile" +const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysClose" -var _BuiltinFunc_index = [...]uint16{0, 8, 23, 38, 53, 64, 76, 89, 104, 123, 138, 153, 168, 178, 193, 212, 230, 246, 264, 277, 289, 306, 323, 338, 348, 363, 380, 394, 406, 416, 433, 450, 466, 481, 497, 512, 528, 544, 568, 583, 596, 608, 624, 639, 654, 669, 683, 700, 714, 723, 735, 750, 763, 778, 793, 808, 828, 847, 859, 875, 894, 910, 925, 939, 952, 958, 973, 990, 1000, 1022, 1033, 1049, 1066, 1082, 1096, 1115, 1133, 1148, 1158, 1169, 1182, 1202, 1219, 1238, 1259, 1272, 1285, 1296, 1309, 1321, 1334, 1347, 1359, 1373, 1383, 1395, 1407, 1416, 1429, 1446, 1460, 1479, 1494, 1517, 1536, 1555, 1563, 1572, 1586, 1603, 1615, 1632, 1643, 1658, 1675, 1693, 1713, 1725, 1743, 1754, 1773, 1794, 1805, 1821, 1849, 1859, 1875, 1886, 1896, 1908, 1928, 1943, 1959, 1974, 1990, 2004, 2015, 2030, 2044, 2066, 2087, 2102, 2116, 2128, 2141, 2156, 2173, 2193, 2200, 2214, 2227, 2241, 2259, 2274, 2285, 2297, 2311, 2327, 2346, 2365, 2378, 2396, 2410, 2424} +var _BuiltinFunc_index = [...]uint16{0, 8, 23, 38, 53, 64, 76, 89, 104, 123, 138, 153, 168, 178, 193, 212, 230, 246, 264, 277, 289, 306, 323, 338, 348, 363, 380, 394, 406, 416, 433, 450, 466, 481, 497, 512, 528, 544, 568, 583, 596, 608, 624, 639, 654, 669, 683, 700, 714, 723, 735, 750, 763, 778, 793, 808, 828, 847, 859, 875, 894, 910, 925, 939, 952, 958, 973, 990, 1000, 1022, 1033, 1049, 1066, 1082, 1096, 1115, 1133, 1148, 1158, 1169, 1182, 1202, 1219, 1238, 1259, 1272, 1285, 1296, 1309, 1321, 1334, 1347, 1359, 1373, 1383, 1395, 1407, 1416, 1429, 1446, 1460, 1479, 1494, 1517, 1536, 1555, 1563, 1572, 1586, 1603, 1615, 1632, 1643, 1658, 1675, 1693, 1713, 1725, 1743, 1754, 1773, 1794, 1805, 1821, 1849, 1859, 1875, 1886, 1896, 1908, 1928, 1943, 1959, 1974, 1990, 2004, 2015, 2030, 2044, 2066, 2087, 2102, 2116, 2128, 2141, 2156, 2173, 2193, 2200, 2214, 2227, 2241, 2259, 2274, 2285, 2297, 2311, 2327, 2346, 2365, 2378, 2396, 2410, 2424, 2434, 2450, 2460, 2468, 2487, 2497} func (i BuiltinFunc) String() string { if i < 0 || i >= BuiltinFunc(len(_BuiltinFunc_index)-1) { diff --git a/vendor/github.com/cilium/ebpf/asm/instruction.go b/vendor/github.com/cilium/ebpf/asm/instruction.go index e7ac0109e..64d717d15 100644 --- a/vendor/github.com/cilium/ebpf/asm/instruction.go +++ b/vendor/github.com/cilium/ebpf/asm/instruction.go @@ -181,6 +181,11 @@ func (ins *Instruction) IsFunctionCall() bool { return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall } +// IsBuiltinCall returns true if the instruction is a built-in call, i.e. BPF helper call. +func (ins *Instruction) IsBuiltinCall() bool { + return ins.OpCode.JumpOp() == Call && ins.Src == R0 && ins.Dst == R0 +} + // IsConstantLoad returns true if the instruction loads a constant of the // given size. func (ins *Instruction) IsConstantLoad(size Size) bool { diff --git a/vendor/github.com/cilium/ebpf/attachtype_string.go b/vendor/github.com/cilium/ebpf/attachtype_string.go new file mode 100644 index 000000000..de355ed90 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/attachtype_string.go @@ -0,0 +1,65 @@ +// Code generated by "stringer -type AttachType -trimprefix Attach"; DO NOT EDIT. + +package ebpf + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[AttachNone-0] + _ = x[AttachCGroupInetIngress-0] + _ = x[AttachCGroupInetEgress-1] + _ = x[AttachCGroupInetSockCreate-2] + _ = x[AttachCGroupSockOps-3] + _ = x[AttachSkSKBStreamParser-4] + _ = x[AttachSkSKBStreamVerdict-5] + _ = x[AttachCGroupDevice-6] + _ = x[AttachSkMsgVerdict-7] + _ = x[AttachCGroupInet4Bind-8] + _ = x[AttachCGroupInet6Bind-9] + _ = x[AttachCGroupInet4Connect-10] + _ = x[AttachCGroupInet6Connect-11] + _ = x[AttachCGroupInet4PostBind-12] + _ = x[AttachCGroupInet6PostBind-13] + _ = x[AttachCGroupUDP4Sendmsg-14] + _ = x[AttachCGroupUDP6Sendmsg-15] + _ = x[AttachLircMode2-16] + _ = x[AttachFlowDissector-17] + _ = x[AttachCGroupSysctl-18] + _ = x[AttachCGroupUDP4Recvmsg-19] + _ = x[AttachCGroupUDP6Recvmsg-20] + _ = x[AttachCGroupGetsockopt-21] + _ = x[AttachCGroupSetsockopt-22] + _ = x[AttachTraceRawTp-23] + _ = x[AttachTraceFEntry-24] + _ = x[AttachTraceFExit-25] + _ = x[AttachModifyReturn-26] + _ = x[AttachLSMMac-27] + _ = x[AttachTraceIter-28] + _ = x[AttachCgroupInet4GetPeername-29] + _ = x[AttachCgroupInet6GetPeername-30] + _ = x[AttachCgroupInet4GetSockname-31] + _ = x[AttachCgroupInet6GetSockname-32] + _ = x[AttachXDPDevMap-33] + _ = x[AttachCgroupInetSockRelease-34] + _ = x[AttachXDPCPUMap-35] + _ = x[AttachSkLookup-36] + _ = x[AttachXDP-37] + _ = x[AttachSkSKBVerdict-38] + _ = x[AttachSkReuseportSelect-39] + _ = x[AttachSkReuseportSelectOrMigrate-40] + _ = x[AttachPerfEvent-41] +} + +const _AttachType_name = "NoneCGroupInetEgressCGroupInetSockCreateCGroupSockOpsSkSKBStreamParserSkSKBStreamVerdictCGroupDeviceSkMsgVerdictCGroupInet4BindCGroupInet6BindCGroupInet4ConnectCGroupInet6ConnectCGroupInet4PostBindCGroupInet6PostBindCGroupUDP4SendmsgCGroupUDP6SendmsgLircMode2FlowDissectorCGroupSysctlCGroupUDP4RecvmsgCGroupUDP6RecvmsgCGroupGetsockoptCGroupSetsockoptTraceRawTpTraceFEntryTraceFExitModifyReturnLSMMacTraceIterCgroupInet4GetPeernameCgroupInet6GetPeernameCgroupInet4GetSocknameCgroupInet6GetSocknameXDPDevMapCgroupInetSockReleaseXDPCPUMapSkLookupXDPSkSKBVerdictSkReuseportSelectSkReuseportSelectOrMigratePerfEvent" + +var _AttachType_index = [...]uint16{0, 4, 20, 40, 53, 70, 88, 100, 112, 127, 142, 160, 178, 197, 216, 233, 250, 259, 272, 284, 301, 318, 334, 350, 360, 371, 381, 393, 399, 408, 430, 452, 474, 496, 505, 526, 535, 543, 546, 558, 575, 601, 610} + +func (i AttachType) String() string { + if i >= AttachType(len(_AttachType_index)-1) { + return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _AttachType_name[_AttachType_index[i]:_AttachType_index[i+1]] +} diff --git a/vendor/github.com/cilium/ebpf/collection.go b/vendor/github.com/cilium/ebpf/collection.go index 17cc69492..2ededc87a 100644 --- a/vendor/github.com/cilium/ebpf/collection.go +++ b/vendor/github.com/cilium/ebpf/collection.go @@ -1,6 +1,7 @@ package ebpf import ( + "encoding/binary" "errors" "fmt" "io" @@ -25,6 +26,10 @@ type CollectionOptions struct { type CollectionSpec struct { Maps map[string]*MapSpec Programs map[string]*ProgramSpec + + // ByteOrder specifies whether the ELF was compiled for + // big-endian or little-endian architectures. + ByteOrder binary.ByteOrder } // Copy returns a recursive copy of the spec. @@ -34,8 +39,9 @@ func (cs *CollectionSpec) Copy() *CollectionSpec { } cpy := CollectionSpec{ - Maps: make(map[string]*MapSpec, len(cs.Maps)), - Programs: make(map[string]*ProgramSpec, len(cs.Programs)), + Maps: make(map[string]*MapSpec, len(cs.Maps)), + Programs: make(map[string]*ProgramSpec, len(cs.Programs)), + ByteOrder: cs.ByteOrder, } for name, spec := range cs.Maps { @@ -123,7 +129,7 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error buf := make([]byte, len(value)) copy(buf, value) - err := patchValue(buf, btf.MapValue(rodata.BTF), consts) + err := patchValue(buf, rodata.BTF.Value, consts) if err != nil { return err } @@ -134,15 +140,15 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error // Assign the contents of a CollectionSpec to a struct. // -// This function is a short-cut to manually checking the presence -// of maps and programs in a collection spec. Consider using bpf2go if this -// sounds useful. +// This function is a shortcut to manually checking the presence +// of maps and programs in a CollectionSpec. Consider using bpf2go +// if this sounds useful. // -// The argument to must be a pointer to a struct. A field of the +// 'to' must be a pointer to a struct. A field of the // struct is updated with values from Programs or Maps if it // has an `ebpf` tag and its type is *ProgramSpec or *MapSpec. -// The tag gives the name of the program or map as found in -// the CollectionSpec. +// The tag's value specifies the name of the program or map as +// found in the CollectionSpec. // // struct { // Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"` @@ -150,42 +156,47 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error // Ignored int // } // -// Returns an error if any of the fields can't be found, or -// if the same map or program is assigned multiple times. +// Returns an error if any of the eBPF objects can't be found, or +// if the same MapSpec or ProgramSpec is assigned multiple times. func (cs *CollectionSpec) Assign(to interface{}) error { - valueOf := func(typ reflect.Type, name string) (reflect.Value, error) { + // Assign() only supports assigning ProgramSpecs and MapSpecs, + // so doesn't load any resources into the kernel. + getValue := func(typ reflect.Type, name string) (interface{}, error) { switch typ { + case reflect.TypeOf((*ProgramSpec)(nil)): - p := cs.Programs[name] - if p == nil { - return reflect.Value{}, fmt.Errorf("missing program %q", name) + if p := cs.Programs[name]; p != nil { + return p, nil } - return reflect.ValueOf(p), nil + return nil, fmt.Errorf("missing program %q", name) + case reflect.TypeOf((*MapSpec)(nil)): - m := cs.Maps[name] - if m == nil { - return reflect.Value{}, fmt.Errorf("missing map %q", name) + if m := cs.Maps[name]; m != nil { + return m, nil } - return reflect.ValueOf(m), nil + return nil, fmt.Errorf("missing map %q", name) + default: - return reflect.Value{}, fmt.Errorf("unsupported type %s", typ) + return nil, fmt.Errorf("unsupported type %s", typ) } } - return assignValues(to, valueOf) + return assignValues(to, getValue) } -// LoadAndAssign maps and programs into the kernel and assign them to a struct. +// LoadAndAssign loads Maps and Programs into the kernel and assigns them +// to a struct. // -// This function is a short-cut to manually checking the presence -// of maps and programs in a collection spec. Consider using bpf2go if this -// sounds useful. +// This function is a shortcut to manually checking the presence +// of maps and programs in a CollectionSpec. Consider using bpf2go +// if this sounds useful. // -// The argument to must be a pointer to a struct. A field of the -// struct is updated with values from Programs or Maps if it -// has an `ebpf` tag and its type is *Program or *Map. -// The tag gives the name of the program or map as found in -// the CollectionSpec. +// 'to' must be a pointer to a struct. A field of the struct is updated with +// a Program or Map if it has an `ebpf` tag and its type is *Program or *Map. +// The tag's value specifies the name of the program or map as found in the +// CollectionSpec. Before updating the struct, the requested objects and their +// dependent resources are loaded into the kernel and populated with values if +// specified. // // struct { // Foo *ebpf.Program `ebpf:"xdp_foo"` @@ -196,39 +207,53 @@ func (cs *CollectionSpec) Assign(to interface{}) error { // opts may be nil. // // Returns an error if any of the fields can't be found, or -// if the same map or program is assigned multiple times. +// if the same Map or Program is assigned multiple times. func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error { - if opts == nil { - opts = &CollectionOptions{} - } - - loadMap, loadProgram, done, cleanup := lazyLoadCollection(cs, opts) - defer cleanup() + loader := newCollectionLoader(cs, opts) + defer loader.cleanup() - valueOf := func(typ reflect.Type, name string) (reflect.Value, error) { + // Support assigning Programs and Maps, lazy-loading the required objects. + assignedMaps := make(map[string]bool) + getValue := func(typ reflect.Type, name string) (interface{}, error) { switch typ { + case reflect.TypeOf((*Program)(nil)): - p, err := loadProgram(name) - if err != nil { - return reflect.Value{}, err - } - return reflect.ValueOf(p), nil + return loader.loadProgram(name) + case reflect.TypeOf((*Map)(nil)): - m, err := loadMap(name) - if err != nil { - return reflect.Value{}, err - } - return reflect.ValueOf(m), nil + assignedMaps[name] = true + return loader.loadMap(name) + default: - return reflect.Value{}, fmt.Errorf("unsupported type %s", typ) + return nil, fmt.Errorf("unsupported type %s", typ) } } - if err := assignValues(to, valueOf); err != nil { + // Load the Maps and Programs requested by the annotated struct. + if err := assignValues(to, getValue); err != nil { return err } - done() + // Populate the requested maps. Has a chance of lazy-loading other dependent maps. + if err := loader.populateMaps(); err != nil { + return err + } + + // Evaluate the loader's objects after all (lazy)loading has taken place. + for n, m := range loader.maps { + switch m.typ { + case ProgramArray: + // Require all lazy-loaded ProgramArrays to be assigned to the given object. + // Without any references, they will be closed on the first GC and all tail + // calls into them will miss. + if !assignedMaps[n] { + return fmt.Errorf("ProgramArray %s must be assigned to prevent missed tail calls", n) + } + } + } + + loader.finalize() + return nil } @@ -246,24 +271,32 @@ func NewCollection(spec *CollectionSpec) (*Collection, error) { // NewCollectionWithOptions creates a Collection from a specification. func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) { - loadMap, loadProgram, done, cleanup := lazyLoadCollection(spec, &opts) - defer cleanup() + loader := newCollectionLoader(spec, &opts) + defer loader.cleanup() + // Create maps first, as their fds need to be linked into programs. for mapName := range spec.Maps { - _, err := loadMap(mapName) - if err != nil { + if _, err := loader.loadMap(mapName); err != nil { return nil, err } } for progName := range spec.Programs { - _, err := loadProgram(progName) - if err != nil { + if _, err := loader.loadProgram(progName); err != nil { return nil, err } } - maps, progs := done() + // Maps can contain Program and Map stubs, so populate them after + // all Maps and Programs have been successfully loaded. + if err := loader.populateMaps(); err != nil { + return nil, err + } + + maps, progs := loader.maps, loader.programs + + loader.finalize() + return &Collection{ progs, maps, @@ -314,113 +347,154 @@ func (hc handleCache) close() { for _, handle := range hc.btfHandles { handle.Close() } - hc.btfHandles = nil - hc.btfSpecs = nil } -func lazyLoadCollection(coll *CollectionSpec, opts *CollectionOptions) ( - loadMap func(string) (*Map, error), - loadProgram func(string) (*Program, error), - done func() (map[string]*Map, map[string]*Program), - cleanup func(), -) { - var ( - maps = make(map[string]*Map) - progs = make(map[string]*Program) - handles = newHandleCache() - skipMapsAndProgs = false - ) - - cleanup = func() { - handles.close() - - if skipMapsAndProgs { - return - } +type collectionLoader struct { + coll *CollectionSpec + opts *CollectionOptions + maps map[string]*Map + programs map[string]*Program + handles *handleCache +} - for _, m := range maps { - m.Close() - } +func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) *collectionLoader { + if opts == nil { + opts = &CollectionOptions{} + } - for _, p := range progs { - p.Close() - } + return &collectionLoader{ + coll, + opts, + make(map[string]*Map), + make(map[string]*Program), + newHandleCache(), + } +} + +// finalize should be called when all the collectionLoader's resources +// have been successfully loaded into the kernel and populated with values. +func (cl *collectionLoader) finalize() { + cl.maps, cl.programs = nil, nil +} + +// cleanup cleans up all resources left over in the collectionLoader. +// Call finalize() when Map and Program creation/population is successful +// to prevent them from getting closed. +func (cl *collectionLoader) cleanup() { + cl.handles.close() + for _, m := range cl.maps { + m.Close() + } + for _, p := range cl.programs { + p.Close() + } +} + +func (cl *collectionLoader) loadMap(mapName string) (*Map, error) { + if m := cl.maps[mapName]; m != nil { + return m, nil + } + + mapSpec := cl.coll.Maps[mapName] + if mapSpec == nil { + return nil, fmt.Errorf("missing map %s", mapName) } - done = func() (map[string]*Map, map[string]*Program) { - skipMapsAndProgs = true - return maps, progs + m, err := newMapWithOptions(mapSpec, cl.opts.Maps, cl.handles) + if err != nil { + return nil, fmt.Errorf("map %s: %w", mapName, err) } - loadMap = func(mapName string) (*Map, error) { - if m := maps[mapName]; m != nil { - return m, nil + cl.maps[mapName] = m + return m, nil +} + +func (cl *collectionLoader) loadProgram(progName string) (*Program, error) { + if prog := cl.programs[progName]; prog != nil { + return prog, nil + } + + progSpec := cl.coll.Programs[progName] + if progSpec == nil { + return nil, fmt.Errorf("unknown program %s", progName) + } + + progSpec = progSpec.Copy() + + // Rewrite any reference to a valid map. + for i := range progSpec.Instructions { + ins := &progSpec.Instructions[i] + + if !ins.IsLoadFromMap() || ins.Reference == "" { + continue } - mapSpec := coll.Maps[mapName] - if mapSpec == nil { - return nil, fmt.Errorf("missing map %s", mapName) + if uint32(ins.Constant) != math.MaxUint32 { + // Don't overwrite maps already rewritten, users can + // rewrite programs in the spec themselves + continue } - m, err := newMapWithOptions(mapSpec, opts.Maps, handles) + m, err := cl.loadMap(ins.Reference) if err != nil { - return nil, fmt.Errorf("map %s: %w", mapName, err) + return nil, fmt.Errorf("program %s: %w", progName, err) } - maps[mapName] = m - return m, nil - } - - loadProgram = func(progName string) (*Program, error) { - if prog := progs[progName]; prog != nil { - return prog, nil + fd := m.FD() + if fd < 0 { + return nil, fmt.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd) } - - progSpec := coll.Programs[progName] - if progSpec == nil { - return nil, fmt.Errorf("unknown program %s", progName) + if err := ins.RewriteMapPtr(m.FD()); err != nil { + return nil, fmt.Errorf("program %s: map %s: %w", progName, ins.Reference, err) } + } - progSpec = progSpec.Copy() + prog, err := newProgramWithOptions(progSpec, cl.opts.Programs, cl.handles) + if err != nil { + return nil, fmt.Errorf("program %s: %w", progName, err) + } - // Rewrite any reference to a valid map. - for i := range progSpec.Instructions { - ins := &progSpec.Instructions[i] + cl.programs[progName] = prog + return prog, nil +} - if !ins.IsLoadFromMap() || ins.Reference == "" { - continue - } +func (cl *collectionLoader) populateMaps() error { + for mapName, m := range cl.maps { + mapSpec, ok := cl.coll.Maps[mapName] + if !ok { + return fmt.Errorf("missing map spec %s", mapName) + } - if uint32(ins.Constant) != math.MaxUint32 { - // Don't overwrite maps already rewritten, users can - // rewrite programs in the spec themselves - continue - } + mapSpec = mapSpec.Copy() - m, err := loadMap(ins.Reference) - if err != nil { - return nil, fmt.Errorf("program %s: %w", progName, err) - } + // Replace any object stubs with loaded objects. + for i, kv := range mapSpec.Contents { + switch v := kv.Value.(type) { + case programStub: + // loadProgram is idempotent and could return an existing Program. + prog, err := cl.loadProgram(string(v)) + if err != nil { + return fmt.Errorf("loading program %s, for map %s: %w", v, mapName, err) + } + mapSpec.Contents[i] = MapKV{kv.Key, prog} - fd := m.FD() - if fd < 0 { - return nil, fmt.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd) - } - if err := ins.RewriteMapPtr(m.FD()); err != nil { - return nil, fmt.Errorf("progam %s: map %s: %w", progName, ins.Reference, err) + case mapStub: + // loadMap is idempotent and could return an existing Map. + innerMap, err := cl.loadMap(string(v)) + if err != nil { + return fmt.Errorf("loading inner map %s, for map %s: %w", v, mapName, err) + } + mapSpec.Contents[i] = MapKV{kv.Key, innerMap} } } - prog, err := newProgramWithOptions(progSpec, opts.Programs, handles) - if err != nil { - return nil, fmt.Errorf("program %s: %w", progName, err) + // Populate and freeze the map if specified. + if err := m.finalize(mapSpec); err != nil { + return fmt.Errorf("populating map %s: %w", mapName, err) } - - progs[progName] = prog - return prog, nil } - return + return nil } // LoadCollection parses an object file and converts it to a collection. @@ -466,108 +540,81 @@ func (coll *Collection) DetachProgram(name string) *Program { return p } -// Assign the contents of a collection to a struct. -// -// Deprecated: use CollectionSpec.Assign instead. It provides the same -// functionality but creates only the maps and programs requested. -func (coll *Collection) Assign(to interface{}) error { - assignedMaps := make(map[string]struct{}) - assignedPrograms := make(map[string]struct{}) - valueOf := func(typ reflect.Type, name string) (reflect.Value, error) { - switch typ { - case reflect.TypeOf((*Program)(nil)): - p := coll.Programs[name] - if p == nil { - return reflect.Value{}, fmt.Errorf("missing program %q", name) - } - assignedPrograms[name] = struct{}{} - return reflect.ValueOf(p), nil - case reflect.TypeOf((*Map)(nil)): - m := coll.Maps[name] - if m == nil { - return reflect.Value{}, fmt.Errorf("missing map %q", name) - } - assignedMaps[name] = struct{}{} - return reflect.ValueOf(m), nil - default: - return reflect.Value{}, fmt.Errorf("unsupported type %s", typ) - } - } - - if err := assignValues(to, valueOf); err != nil { - return err - } +// structField represents a struct field containing the ebpf struct tag. +type structField struct { + reflect.StructField + value reflect.Value +} - for name := range assignedPrograms { - coll.DetachProgram(name) +// ebpfFields extracts field names tagged with 'ebpf' from a struct type. +// Keep track of visited types to avoid infinite recursion. +func ebpfFields(structVal reflect.Value, visited map[reflect.Type]bool) ([]structField, error) { + if visited == nil { + visited = make(map[reflect.Type]bool) } - for name := range assignedMaps { - coll.DetachMap(name) + structType := structVal.Type() + if structType.Kind() != reflect.Struct { + return nil, fmt.Errorf("%s is not a struct", structType) } - return nil -} - -func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Value, error)) error { - type structField struct { - reflect.StructField - value reflect.Value + if visited[structType] { + return nil, fmt.Errorf("recursion on type %s", structType) } - var ( - fields []structField - visitedTypes = make(map[reflect.Type]bool) - flattenStruct func(reflect.Value) error - ) + fields := make([]structField, 0, structType.NumField()) + for i := 0; i < structType.NumField(); i++ { + field := structField{structType.Field(i), structVal.Field(i)} - flattenStruct = func(structVal reflect.Value) error { - structType := structVal.Type() - if structType.Kind() != reflect.Struct { - return fmt.Errorf("%s is not a struct", structType) - } - - if visitedTypes[structType] { - return fmt.Errorf("recursion on type %s", structType) + // If the field is tagged, gather it and move on. + name := field.Tag.Get("ebpf") + if name != "" { + fields = append(fields, field) + continue } - for i := 0; i < structType.NumField(); i++ { - field := structField{structType.Field(i), structVal.Field(i)} - - name := field.Tag.Get("ebpf") - if name != "" { - fields = append(fields, field) + // If the field does not have an ebpf tag, but is a struct or a pointer + // to a struct, attempt to gather its fields as well. + var v reflect.Value + switch field.Type.Kind() { + case reflect.Ptr: + if field.Type.Elem().Kind() != reflect.Struct { continue } - var err error - switch field.Type.Kind() { - case reflect.Ptr: - if field.Type.Elem().Kind() != reflect.Struct { - continue - } - - if field.value.IsNil() { - return fmt.Errorf("nil pointer to %s", structType) - } + if field.value.IsNil() { + return nil, fmt.Errorf("nil pointer to %s", structType) + } - err = flattenStruct(field.value.Elem()) + // Obtain the destination type of the pointer. + v = field.value.Elem() - case reflect.Struct: - err = flattenStruct(field.value) + case reflect.Struct: + // Reference the value's type directly. + v = field.value - default: - continue - } + default: + continue + } - if err != nil { - return fmt.Errorf("field %s: %w", field.Name, err) - } + inner, err := ebpfFields(v, visited) + if err != nil { + return nil, fmt.Errorf("field %s: %w", field.Name, err) } - return nil + fields = append(fields, inner...) } + return fields, nil +} + +// assignValues attempts to populate all fields of 'to' tagged with 'ebpf'. +// +// getValue is called for every tagged field of 'to' and must return the value +// to be assigned to the field with the given typ and name. +func assignValues(to interface{}, + getValue func(typ reflect.Type, name string) (interface{}, error)) error { + toValue := reflect.ValueOf(to) if toValue.Type().Kind() != reflect.Ptr { return fmt.Errorf("%T is not a pointer to struct", to) @@ -577,7 +624,8 @@ func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Va return fmt.Errorf("nil pointer to %T", to) } - if err := flattenStruct(toValue.Elem()); err != nil { + fields, err := ebpfFields(toValue.Elem(), nil) + if err != nil { return err } @@ -587,19 +635,23 @@ func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Va name string } - assignedTo := make(map[elem]string) + assigned := make(map[elem]string) for _, field := range fields { - name := field.Tag.Get("ebpf") - if strings.Contains(name, ",") { + // Get string value the field is tagged with. + tag := field.Tag.Get("ebpf") + if strings.Contains(tag, ",") { return fmt.Errorf("field %s: ebpf tag contains a comma", field.Name) } - e := elem{field.Type, name} - if assignedField := assignedTo[e]; assignedField != "" { - return fmt.Errorf("field %s: %q was already assigned to %s", field.Name, name, assignedField) + // Check if the eBPF object with the requested + // type and tag was already assigned elsewhere. + e := elem{field.Type, tag} + if af := assigned[e]; af != "" { + return fmt.Errorf("field %s: object %q was already assigned to %s", field.Name, tag, af) } - value, err := valueOf(field.Type, name) + // Get the eBPF object referred to by the tag. + value, err := getValue(field.Type, tag) if err != nil { return fmt.Errorf("field %s: %w", field.Name, err) } @@ -607,9 +659,9 @@ func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Va if !field.value.CanSet() { return fmt.Errorf("field %s: can't set value", field.Name) } + field.value.Set(reflect.ValueOf(value)) - field.value.Set(value) - assignedTo[e] = field.Name + assigned[e] = field.Name } return nil diff --git a/vendor/github.com/cilium/ebpf/elf_reader.go b/vendor/github.com/cilium/ebpf/elf_reader.go index c2afbc36a..42010f43e 100644 --- a/vendor/github.com/cilium/ebpf/elf_reader.go +++ b/vendor/github.com/cilium/ebpf/elf_reader.go @@ -19,7 +19,7 @@ import ( ) // elfCode is a convenience to reduce the amount of arguments that have to -// be passed around explicitly. You should treat it's contents as immutable. +// be passed around explicitly. You should treat its contents as immutable. type elfCode struct { *internal.SafeELFFile sections map[elf.SectionIndex]*elfSection @@ -188,7 +188,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { return nil, fmt.Errorf("load programs: %w", err) } - return &CollectionSpec{maps, progs}, nil + return &CollectionSpec{maps, progs, ec.ByteOrder}, nil } func loadLicense(sec *elf.Section) (string, error) { @@ -520,8 +520,12 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec) error { return fmt.Errorf("map %s: missing flags", mapName) } - if _, err := io.Copy(internal.DiscardZeroes{}, lr); err != nil { - return fmt.Errorf("map %s: unknown and non-zero fields in definition", mapName) + extra, err := io.ReadAll(lr) + if err != nil { + return fmt.Errorf("map %s: reading map tail: %w", mapName, err) + } + if len(extra) > 0 { + spec.Extra = *bytes.NewReader(extra) } if err := spec.clampPerfEventArraySize(); err != nil { @@ -535,6 +539,9 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec) error { return nil } +// loadBTFMaps iterates over all ELF sections marked as BTF map sections +// (like .maps) and parses them into MapSpecs. Dump the .maps section and +// any relocations with `readelf -x .maps -r <elf_file>`. func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec) error { for _, sec := range ec.sections { if sec.kind != btfMapSection { @@ -545,33 +552,46 @@ func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec) error { return fmt.Errorf("missing BTF") } - _, err := io.Copy(internal.DiscardZeroes{}, bufio.NewReader(sec.Open())) - if err != nil { - return fmt.Errorf("section %v: initializing BTF map definitions: %w", sec.Name, internal.ErrNotSupported) - } - - var ds btf.Datasec + // Each section must appear as a DataSec in the ELF's BTF blob. + var ds *btf.Datasec if err := ec.btf.FindType(sec.Name, &ds); err != nil { return fmt.Errorf("cannot find section '%s' in BTF: %w", sec.Name, err) } + // Open a Reader to the ELF's raw section bytes so we can assert that all + // of them are zero on a per-map (per-Var) basis. For now, the section's + // sole purpose is to receive relocations, so all must be zero. + rs := sec.Open() + for _, vs := range ds.Vars { + // BPF maps are declared as and assigned to global variables, + // so iterate over each Var in the DataSec and validate their types. v, ok := vs.Type.(*btf.Var) if !ok { return fmt.Errorf("section %v: unexpected type %s", sec.Name, vs.Type) } name := string(v.Name) + // The BTF metadata for each Var contains the full length of the map + // declaration, so read the corresponding amount of bytes from the ELF. + // This way, we can pinpoint which map declaration contains unexpected + // (and therefore unsupported) data. + _, err := io.Copy(internal.DiscardZeroes{}, io.LimitReader(rs, int64(vs.Size))) + if err != nil { + return fmt.Errorf("section %v: map %s: initializing BTF map definitions: %w", sec.Name, name, internal.ErrNotSupported) + } + if maps[name] != nil { return fmt.Errorf("section %v: map %s already exists", sec.Name, name) } + // Each Var representing a BTF map definition contains a Struct. mapStruct, ok := v.Type.(*btf.Struct) if !ok { return fmt.Errorf("expected struct, got %s", v.Type) } - mapSpec, err := mapSpecFromBTF(name, mapStruct, false, ec.btf) + mapSpec, err := mapSpecFromBTF(sec, &vs, mapStruct, ec.btf, name, false) if err != nil { return fmt.Errorf("map %v: %w", name, err) } @@ -582,32 +602,52 @@ func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec) error { maps[name] = mapSpec } + + // Drain the ELF section reader to make sure all bytes are accounted for + // with BTF metadata. + i, err := io.Copy(io.Discard, rs) + if err != nil { + return fmt.Errorf("section %v: unexpected error reading remainder of ELF section: %w", sec.Name, err) + } + if i > 0 { + return fmt.Errorf("section %v: %d unexpected remaining bytes in ELF section, invalid BTF?", sec.Name, i) + } } return nil } +// A programStub is a placeholder for a Program to be inserted at a certain map key. +// It needs to be resolved into a Program later on in the loader process. +type programStub string + +// A mapStub is a placeholder for a Map to be inserted at a certain map key. +// It needs to be resolved into a Map later on in the loader process. +type mapStub string + // mapSpecFromBTF produces a MapSpec based on a btf.Struct def representing // a BTF map definition. The name and spec arguments will be copied to the // resulting MapSpec, and inner must be true on any resursive invocations. -func mapSpecFromBTF(name string, def *btf.Struct, inner bool, spec *btf.Spec) (*MapSpec, error) { - +func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *btf.Spec, name string, inner bool) (*MapSpec, error) { var ( - key, value btf.Type - keySize, valueSize uint32 - mapType, flags, maxEntries uint32 - pinType PinType - innerMapSpec *MapSpec - err error + key, value btf.Type + keySize, valueSize uint32 + mapType MapType + flags, maxEntries uint32 + pinType PinType + innerMapSpec *MapSpec + contents []MapKV + err error ) for i, member := range def.Members { switch member.Name { case "type": - mapType, err = uintFromBTF(member.Type) + mt, err := uintFromBTF(member.Type) if err != nil { return nil, fmt.Errorf("can't get type: %w", err) } + mapType = MapType(mt) case "map_flags": flags, err = uintFromBTF(member.Type) @@ -717,7 +757,7 @@ func mapSpecFromBTF(name string, def *btf.Struct, inner bool, spec *btf.Spec) (* case *btf.Struct: // The values member pointing to an array of structs means we're expecting // a map-in-map declaration. - if MapType(mapType) != ArrayOfMaps && MapType(mapType) != HashOfMaps { + if mapType != ArrayOfMaps && mapType != HashOfMaps { return nil, errors.New("outer map needs to be an array or a hash of maps") } if inner { @@ -731,21 +771,38 @@ func mapSpecFromBTF(name string, def *btf.Struct, inner bool, spec *btf.Spec) (* // on kernels 5.2 and up) // Pass the BTF spec from the parent object, since both parent and // child must be created from the same BTF blob (on kernels that support BTF). - innerMapSpec, err = mapSpecFromBTF(name+"_inner", t, true, spec) + innerMapSpec, err = mapSpecFromBTF(es, vs, t, spec, name+"_inner", true) if err != nil { return nil, fmt.Errorf("can't parse BTF map definition of inner map: %w", err) } + case *btf.FuncProto: + // The values member contains an array of function pointers, meaning an + // autopopulated PROG_ARRAY. + if mapType != ProgramArray { + return nil, errors.New("map needs to be a program array") + } + default: return nil, fmt.Errorf("unsupported value type %q in 'values' field", t) } + contents, err = resolveBTFValuesContents(es, vs, member) + if err != nil { + return nil, fmt.Errorf("resolving values contents: %w", err) + } + default: return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name) } } - bm := btf.NewMap(spec, key, value) + if key == nil { + key = &btf.Void{} + } + if value == nil { + value = &btf.Void{} + } return &MapSpec{ Name: SanitizeName(name, -1), @@ -754,9 +811,10 @@ func mapSpecFromBTF(name string, def *btf.Struct, inner bool, spec *btf.Spec) (* ValueSize: valueSize, MaxEntries: maxEntries, Flags: flags, - BTF: &bm, + BTF: &btf.Map{Spec: spec, Key: key, Value: value}, Pinning: pinType, InnerMap: innerMapSpec, + Contents: contents, }, nil } @@ -793,6 +851,64 @@ func resolveBTFArrayMacro(typ btf.Type) (btf.Type, error) { return ptr.Target, nil } +// resolveBTFValuesContents resolves relocations into ELF sections belonging +// to btf.VarSecinfo's. This can be used on the 'values' member in BTF map +// definitions to extract static declarations of map contents. +func resolveBTFValuesContents(es *elfSection, vs *btf.VarSecinfo, member btf.Member) ([]MapKV, error) { + // The elements of a .values pointer array are not encoded in BTF. + // Instead, relocations are generated into each array index. + // However, it's possible to leave certain array indices empty, so all + // indices' offsets need to be checked for emitted relocations. + + // The offset of the 'values' member within the _struct_ (in bits) + // is the starting point of the array. Convert to bytes. Add VarSecinfo + // offset to get the absolute position in the ELF blob. + start := (member.OffsetBits / 8) + vs.Offset + // 'values' is encoded in BTF as a zero (variable) length struct + // member, and its contents run until the end of the VarSecinfo. + // Add VarSecinfo offset to get the absolute position in the ELF blob. + end := vs.Size + vs.Offset + // The size of an address in this section. This determines the width of + // an index in the array. + align := uint32(es.SectionHeader.Addralign) + + // Check if variable-length section is aligned. + if (end-start)%align != 0 { + return nil, errors.New("unaligned static values section") + } + elems := (end - start) / align + + if elems == 0 { + return nil, nil + } + + contents := make([]MapKV, 0, elems) + + // k is the array index, off is its corresponding ELF section offset. + for k, off := uint32(0), start; k < elems; k, off = k+1, off+align { + r, ok := es.relocations[uint64(off)] + if !ok { + continue + } + + // Relocation exists for the current offset in the ELF section. + // Emit a value stub based on the type of relocation to be replaced by + // a real fd later in the pipeline before populating the map. + // Map keys are encoded in MapKV entries, so empty array indices are + // skipped here. + switch t := elf.ST_TYPE(r.Info); t { + case elf.STT_FUNC: + contents = append(contents, MapKV{uint32(k), programStub(r.Name)}) + case elf.STT_OBJECT: + contents = append(contents, MapKV{uint32(k), mapStub(r.Name)}) + default: + return nil, fmt.Errorf("unknown relocation type %v", t) + } + } + + return contents, nil +} + func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error { for _, sec := range ec.sections { if sec.kind != dataSection { @@ -809,9 +925,9 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error { return errors.New("data sections require BTF, make sure all consts are marked as static") } - btfMap, err := ec.btf.Datasec(sec.Name) - if err != nil { - return err + var datasec *btf.Datasec + if err := ec.btf.FindType(sec.Name, &datasec); err != nil { + return fmt.Errorf("data section %s: can't get BTF: %w", sec.Name, err) } data, err := sec.Data() @@ -830,7 +946,7 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error { ValueSize: uint32(len(data)), MaxEntries: 1, Contents: []MapKV{{uint32(0), data}}, - BTF: btfMap, + BTF: &btf.Map{Spec: ec.btf, Key: &btf.Void{}, Value: datasec}, } switch sec.Name { @@ -855,6 +971,8 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) { }{ // From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c "socket": {SocketFilter, AttachNone, 0}, + "sk_reuseport/migrate": {SkReuseport, AttachSkReuseportSelectOrMigrate, 0}, + "sk_reuseport": {SkReuseport, AttachSkReuseportSelect, 0}, "seccomp": {SocketFilter, AttachNone, 0}, "kprobe/": {Kprobe, AttachNone, 0}, "uprobe/": {Kprobe, AttachNone, 0}, @@ -884,6 +1002,7 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) { "fmod_ret.s/": {Tracing, AttachModifyReturn, unix.BPF_F_SLEEPABLE}, "fexit.s/": {Tracing, AttachTraceFExit, unix.BPF_F_SLEEPABLE}, "sk_lookup/": {SkLookup, AttachSkLookup, 0}, + "freplace/": {Extension, AttachNone, 0}, "lsm/": {LSM, AttachLSMMac, 0}, "lsm.s/": {LSM, AttachLSMMac, unix.BPF_F_SLEEPABLE}, @@ -907,6 +1026,11 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) { "cgroup/setsockopt": {CGroupSockopt, AttachCGroupSetsockopt, 0}, "classifier": {SchedCLS, AttachNone, 0}, "action": {SchedACT, AttachNone, 0}, + + "cgroup/getsockname4": {CGroupSockAddr, AttachCgroupInet4GetSockname, 0}, + "cgroup/getsockname6": {CGroupSockAddr, AttachCgroupInet6GetSockname, 0}, + "cgroup/getpeername4": {CGroupSockAddr, AttachCgroupInet4GetPeername, 0}, + "cgroup/getpeername6": {CGroupSockAddr, AttachCgroupInet6GetPeername, 0}, } for prefix, t := range types { diff --git a/vendor/github.com/cilium/ebpf/elf_reader_fuzz.go b/vendor/github.com/cilium/ebpf/elf_reader_fuzz.go index d46d135f2..5f4e0a0ad 100644 --- a/vendor/github.com/cilium/ebpf/elf_reader_fuzz.go +++ b/vendor/github.com/cilium/ebpf/elf_reader_fuzz.go @@ -1,3 +1,4 @@ +//go:build gofuzz // +build gofuzz // Use with https://github.com/dvyukov/go-fuzz diff --git a/vendor/github.com/cilium/ebpf/info.go b/vendor/github.com/cilium/ebpf/info.go index b95131ef5..65fa4d7d8 100644 --- a/vendor/github.com/cilium/ebpf/info.go +++ b/vendor/github.com/cilium/ebpf/info.go @@ -12,6 +12,7 @@ import ( "time" "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" ) // MapInfo describes a map. @@ -87,12 +88,16 @@ type ProgramInfo struct { Tag string // Name as supplied by user space at load time. Name string + // BTF for the program. + btf btf.ID + // IDS map ids related to program. + ids []MapID stats *programStats } func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) { - info, err := bpfGetProgInfoByFD(fd) + info, err := bpfGetProgInfoByFD(fd, nil) if errors.Is(err, syscall.EINVAL) { return newProgramInfoFromProc(fd) } @@ -100,6 +105,15 @@ func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) { return nil, err } + var mapIDs []MapID + if info.nr_map_ids > 0 { + mapIDs = make([]MapID, info.nr_map_ids) + info, err = bpfGetProgInfoByFD(fd, mapIDs) + if err != nil { + return nil, err + } + } + return &ProgramInfo{ Type: ProgramType(info.prog_type), id: ProgramID(info.id), @@ -107,6 +121,8 @@ func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) { Tag: hex.EncodeToString(info.tag[:]), // name is available from 4.15. Name: internal.CString(info.name[:]), + btf: btf.ID(info.btf_id), + ids: mapIDs, stats: &programStats{ runtime: time.Duration(info.run_time_ns), runCount: info.run_cnt, @@ -142,6 +158,17 @@ func (pi *ProgramInfo) ID() (ProgramID, bool) { return pi.id, pi.id > 0 } +// BTFID returns the BTF ID associated with the program. +// +// Available from 5.0. +// +// The bool return value indicates whether this optional field is available and +// populated. (The field may be available but not populated if the kernel +// supports the field but the program was loaded without BTF information.) +func (pi *ProgramInfo) BTFID() (btf.ID, bool) { + return pi.btf, pi.btf > 0 +} + // RunCount returns the total number of times the program was called. // // Can return 0 if the collection of statistics is not enabled. See EnableStats(). @@ -164,6 +191,13 @@ func (pi *ProgramInfo) Runtime() (time.Duration, bool) { return time.Duration(0), false } +// MapIDs returns the maps related to the program. +// +// The bool return value indicates whether this optional field is available. +func (pi *ProgramInfo) MapIDs() ([]MapID, bool) { + return pi.ids, pi.ids != nil +} + func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error { raw, err := fd.Value() if err != nil { diff --git a/vendor/github.com/cilium/ebpf/internal/BUILD.bazel b/vendor/github.com/cilium/ebpf/internal/BUILD.bazel index 4568f4c93..4079687fb 100644 --- a/vendor/github.com/cilium/ebpf/internal/BUILD.bazel +++ b/vendor/github.com/cilium/ebpf/internal/BUILD.bazel @@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "go_default_library", srcs = [ + "align.go", "cpu.go", "elf.go", "endian.go", diff --git a/vendor/github.com/cilium/ebpf/internal/align.go b/vendor/github.com/cilium/ebpf/internal/align.go new file mode 100644 index 000000000..8b4f2658e --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/align.go @@ -0,0 +1,6 @@ +package internal + +// Align returns 'n' updated to 'alignment' boundary. +func Align(n, alignment int) int { + return (int(n) + alignment - 1) / alignment * alignment +} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/BUILD.bazel b/vendor/github.com/cilium/ebpf/internal/btf/BUILD.bazel index f2e218e6a..0d7915fc4 100644 --- a/vendor/github.com/cilium/ebpf/internal/btf/BUILD.bazel +++ b/vendor/github.com/cilium/ebpf/internal/btf/BUILD.bazel @@ -9,7 +9,9 @@ go_library( "core.go", "doc.go", "ext_info.go", + "info.go", "strings.go", + "syscalls.go", "types.go", ], importmap = "kubevirt.io/kubevirt/vendor/github.com/cilium/ebpf/internal/btf", diff --git a/vendor/github.com/cilium/ebpf/internal/btf/btf.go b/vendor/github.com/cilium/ebpf/internal/btf/btf.go index 5da9e1192..2b5f6d226 100644 --- a/vendor/github.com/cilium/ebpf/internal/btf/btf.go +++ b/vendor/github.com/cilium/ebpf/internal/btf/btf.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "math" "os" "reflect" @@ -27,12 +26,15 @@ var ( ErrNoExtendedInfo = errors.New("no extended info") ) +// ID represents the unique ID of a BTF object. +type ID uint32 + // Spec represents decoded BTF. type Spec struct { rawTypes []rawType strings stringTable types []Type - namedTypes map[string][]namedType + namedTypes map[string][]NamedType funcInfos map[string]extInfo lineInfos map[string]extInfo coreRelos map[string]coreRelos @@ -61,15 +63,6 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { } defer file.Close() - btfSection, btfExtSection, sectionSizes, err := findBtfSections(file) - if err != nil { - return nil, err - } - - if btfSection == nil { - return nil, fmt.Errorf("btf: %w", ErrNotFound) - } - symbols, err := file.Symbols() if err != nil { return nil, fmt.Errorf("can't read symbols: %v", err) @@ -87,10 +80,6 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { } secName := file.Sections[symbol.Section].Name - if _, ok := sectionSizes[secName]; !ok { - continue - } - if symbol.Value > math.MaxUint32 { return nil, fmt.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name) } @@ -98,24 +87,10 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value) } - spec, err := loadNakedSpec(btfSection.Open(), file.ByteOrder, sectionSizes, variableOffsets) - if err != nil { - return nil, err - } - - if btfExtSection == nil { - return spec, nil - } - - spec.funcInfos, spec.lineInfos, spec.coreRelos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings) - if err != nil { - return nil, fmt.Errorf("can't read ext info: %w", err) - } - - return spec, nil + return loadSpecFromELF(file, variableOffsets) } -func findBtfSections(file *internal.SafeELFFile) (*elf.Section, *elf.Section, map[string]uint32, error) { +func loadSpecFromELF(file *internal.SafeELFFile, variableOffsets map[variable]uint32) (*Spec, error) { var ( btfSection *elf.Section btfExtSection *elf.Section @@ -134,33 +109,45 @@ func findBtfSections(file *internal.SafeELFFile) (*elf.Section, *elf.Section, ma } if sec.Size > math.MaxUint32 { - return nil, nil, nil, fmt.Errorf("section %s exceeds maximum size", sec.Name) + return nil, fmt.Errorf("section %s exceeds maximum size", sec.Name) } sectionSizes[sec.Name] = uint32(sec.Size) } } - return btfSection, btfExtSection, sectionSizes, nil -} -func loadSpecFromVmlinux(rd io.ReaderAt) (*Spec, error) { - file, err := internal.NewSafeELFFile(rd) + if btfSection == nil { + return nil, fmt.Errorf("btf: %w", ErrNotFound) + } + + spec, err := loadRawSpec(btfSection.Open(), file.ByteOrder, sectionSizes, variableOffsets) if err != nil { return nil, err } - defer file.Close() - btfSection, _, _, err := findBtfSections(file) - if err != nil { - return nil, fmt.Errorf(".BTF ELF section: %s", err) + if btfExtSection == nil { + return spec, nil } - if btfSection == nil { - return nil, fmt.Errorf("unable to find .BTF ELF section") + + spec.funcInfos, spec.lineInfos, spec.coreRelos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings) + if err != nil { + return nil, fmt.Errorf("can't read ext info: %w", err) } - return loadNakedSpec(btfSection.Open(), file.ByteOrder, nil, nil) + + return spec, nil +} + +// LoadRawSpec reads a blob of BTF data that isn't wrapped in an ELF file. +// +// Prefer using LoadSpecFromReader, since this function only supports a subset +// of BTF. +func LoadRawSpec(btf io.Reader, bo binary.ByteOrder) (*Spec, error) { + // This will return an error if we encounter a Datasec, since we can't fix + // it up. + return loadRawSpec(btf, bo, nil, nil) } -func loadNakedSpec(btf io.ReadSeeker, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) { +func loadRawSpec(btf io.Reader, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) { rawTypes, rawStrings, err := parseBTF(btf, bo) if err != nil { return nil, err @@ -217,7 +204,7 @@ func loadKernelSpec() (*Spec, error) { if err == nil { defer fh.Close() - return loadNakedSpec(fh, internal.NativeEndian, nil, nil) + return LoadRawSpec(fh, internal.NativeEndian) } // use same list of locations as libbpf @@ -241,14 +228,20 @@ func loadKernelSpec() (*Spec, error) { } defer fh.Close() - return loadSpecFromVmlinux(fh) + file, err := internal.NewSafeELFFile(fh) + if err != nil { + return nil, err + } + defer file.Close() + + return loadSpecFromELF(file, nil) } return nil, fmt.Errorf("no BTF for kernel version %s: %w", release, internal.ErrNotSupported) } -func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) ([]rawType, stringTable, error) { - rawBTF, err := ioutil.ReadAll(btf) +func parseBTF(btf io.Reader, bo binary.ByteOrder) ([]rawType, stringTable, error) { + rawBTF, err := io.ReadAll(btf) if err != nil { return nil, nil, fmt.Errorf("can't read BTF: %v", err) } @@ -357,6 +350,30 @@ func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[s return nil } +// Copy creates a copy of Spec. +func (s *Spec) Copy() *Spec { + types, _ := copyTypes(s.types, nil) + namedTypes := make(map[string][]NamedType) + for _, typ := range types { + if named, ok := typ.(NamedType); ok { + name := essentialName(named.TypeName()) + namedTypes[name] = append(namedTypes[name], named) + } + } + + // NB: Other parts of spec are not copied since they are immutable. + return &Spec{ + s.rawTypes, + s.strings, + types, + namedTypes, + s.funcInfos, + s.lineInfos, + s.coreRelos, + s.byteOrder, + } +} + type marshalOpts struct { ByteOrder binary.ByteOrder StripFuncLinkage bool @@ -447,36 +464,37 @@ func (s *Spec) Program(name string, length uint64) (*Program, error) { return &Program{s, length, funcInfos, lineInfos, relos}, nil } -// Datasec returns the BTF required to create maps which represent data sections. -func (s *Spec) Datasec(name string) (*Map, error) { - var datasec Datasec - if err := s.FindType(name, &datasec); err != nil { - return nil, fmt.Errorf("data section %s: can't get BTF: %w", name, err) - } - - m := NewMap(s, &Void{}, &datasec) - return &m, nil -} - // FindType searches for a type with a specific name. // -// hint determines the type of the returned Type. +// Called T a type that satisfies Type, typ must be a non-nil **T. +// On success, the address of the found type will be copied in typ. // // Returns an error wrapping ErrNotFound if no matching // type exists in spec. -func (s *Spec) FindType(name string, typ Type) error { - var ( - wanted = reflect.TypeOf(typ) - candidate Type - ) +func (s *Spec) FindType(name string, typ interface{}) error { + typValue := reflect.ValueOf(typ) + if typValue.Kind() != reflect.Ptr { + return fmt.Errorf("%T is not a pointer", typ) + } + + typPtr := typValue.Elem() + if !typPtr.CanSet() { + return fmt.Errorf("%T cannot be set", typ) + } + wanted := typPtr.Type() + if !wanted.AssignableTo(reflect.TypeOf((*Type)(nil)).Elem()) { + return fmt.Errorf("%T does not satisfy Type interface", typ) + } + + var candidate Type for _, typ := range s.namedTypes[essentialName(name)] { if reflect.TypeOf(typ) != wanted { continue } // Match against the full name, not just the essential one. - if typ.name() != name { + if typ.TypeName() != name { continue } @@ -491,15 +509,15 @@ func (s *Spec) FindType(name string, typ Type) error { return fmt.Errorf("type %s: %w", name, ErrNotFound) } - cpy, _ := copyType(candidate, nil) - value := reflect.Indirect(reflect.ValueOf(cpy)) - reflect.Indirect(reflect.ValueOf(typ)).Set(value) + typPtr.Set(reflect.ValueOf(candidate)) + return nil } // Handle is a reference to BTF loaded into the kernel. type Handle struct { - fd *internal.FD + spec *Spec + fd *internal.FD } // NewHandle loads BTF into the kernel. @@ -541,7 +559,32 @@ func NewHandle(spec *Spec) (*Handle, error) { return nil, internal.ErrorWithLog(err, logBuf, logErr) } - return &Handle{fd}, nil + return &Handle{spec.Copy(), fd}, nil +} + +// NewHandleFromID returns the BTF handle for a given id. +// +// Returns ErrNotExist, if there is no BTF with the given id. +// +// Requires CAP_SYS_ADMIN. +func NewHandleFromID(id ID) (*Handle, error) { + fd, err := internal.BPFObjGetFDByID(internal.BPF_BTF_GET_FD_BY_ID, uint32(id)) + if err != nil { + return nil, fmt.Errorf("get BTF by id: %w", err) + } + + info, err := newInfoFromFd(fd) + if err != nil { + _ = fd.Close() + return nil, fmt.Errorf("get BTF spec for handle: %w", err) + } + + return &Handle{info.BTF, fd}, nil +} + +// Spec returns the Spec that defined the BTF loaded into the kernel. +func (h *Handle) Spec() *Spec { + return h.spec } // Close destroys the handle. @@ -563,43 +606,8 @@ func (h *Handle) FD() int { // Map is the BTF for a map. type Map struct { - spec *Spec - key, value Type -} - -// NewMap returns a new Map containing the given values. -// The key and value arguments are initialized to Void if nil values are given. -func NewMap(spec *Spec, key Type, value Type) Map { - if key == nil { - key = &Void{} - } - if value == nil { - value = &Void{} - } - - return Map{ - spec: spec, - key: key, - value: value, - } -} - -// MapSpec should be a method on Map, but is a free function -// to hide it from users of the ebpf package. -func MapSpec(m *Map) *Spec { - return m.spec -} - -// MapKey should be a method on Map, but is a free function -// to hide it from users of the ebpf package. -func MapKey(m *Map) Type { - return m.key -} - -// MapValue should be a method on Map, but is a free function -// to hide it from users of the ebpf package. -func MapValue(m *Map) Type { - return m.value + Spec *Spec + Key, Value Type } // Program is the BTF information for a stream of instructions. @@ -610,68 +618,59 @@ type Program struct { coreRelos coreRelos } -// ProgramSpec returns the Spec needed for loading function and line infos into the kernel. -// -// This is a free function instead of a method to hide it from users -// of package ebpf. -func ProgramSpec(s *Program) *Spec { - return s.spec +// Spec returns the BTF spec of this program. +func (p *Program) Spec() *Spec { + return p.spec } -// ProgramAppend the information from other to the Program. -// -// This is a free function instead of a method to hide it from users -// of package ebpf. -func ProgramAppend(s, other *Program) error { - funcInfos, err := s.funcInfos.append(other.funcInfos, s.length) +// Append the information from other to the Program. +func (p *Program) Append(other *Program) error { + if other.spec != p.spec { + return fmt.Errorf("can't append program with different BTF specs") + } + + funcInfos, err := p.funcInfos.append(other.funcInfos, p.length) if err != nil { return fmt.Errorf("func infos: %w", err) } - lineInfos, err := s.lineInfos.append(other.lineInfos, s.length) + lineInfos, err := p.lineInfos.append(other.lineInfos, p.length) if err != nil { return fmt.Errorf("line infos: %w", err) } - s.funcInfos = funcInfos - s.lineInfos = lineInfos - s.coreRelos = s.coreRelos.append(other.coreRelos, s.length) - s.length += other.length + p.funcInfos = funcInfos + p.lineInfos = lineInfos + p.coreRelos = p.coreRelos.append(other.coreRelos, p.length) + p.length += other.length return nil } -// ProgramFuncInfos returns the binary form of BTF function infos. -// -// This is a free function instead of a method to hide it from users -// of package ebpf. -func ProgramFuncInfos(s *Program) (recordSize uint32, bytes []byte, err error) { - bytes, err = s.funcInfos.MarshalBinary() +// FuncInfos returns the binary form of BTF function infos. +func (p *Program) FuncInfos() (recordSize uint32, bytes []byte, err error) { + bytes, err = p.funcInfos.MarshalBinary() if err != nil { - return 0, nil, err + return 0, nil, fmt.Errorf("func infos: %w", err) } - return s.funcInfos.recordSize, bytes, nil + return p.funcInfos.recordSize, bytes, nil } -// ProgramLineInfos returns the binary form of BTF line infos. -// -// This is a free function instead of a method to hide it from users -// of package ebpf. -func ProgramLineInfos(s *Program) (recordSize uint32, bytes []byte, err error) { - bytes, err = s.lineInfos.MarshalBinary() +// LineInfos returns the binary form of BTF line infos. +func (p *Program) LineInfos() (recordSize uint32, bytes []byte, err error) { + bytes, err = p.lineInfos.MarshalBinary() if err != nil { - return 0, nil, err + return 0, nil, fmt.Errorf("line infos: %w", err) } - return s.lineInfos.recordSize, bytes, nil + return p.lineInfos.recordSize, bytes, nil } -// ProgramFixups returns the changes required to adjust the program to the target. +// Fixups returns the changes required to adjust the program to the target. // -// This is a free function instead of a method to hide it from users -// of package ebpf. -func ProgramFixups(s *Program, target *Spec) (COREFixups, error) { - if len(s.coreRelos) == 0 { +// Passing a nil target will relocate against the running kernel. +func (p *Program) Fixups(target *Spec) (COREFixups, error) { + if len(p.coreRelos) == 0 { return nil, nil } @@ -683,7 +682,7 @@ func ProgramFixups(s *Program, target *Spec) (COREFixups, error) { } } - return coreRelocate(s.spec, target, s.coreRelos) + return coreRelocate(p.spec, target, p.coreRelos) } type bpfLoadBTFAttr struct { diff --git a/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go b/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go index a5ef94512..d98c73ca5 100644 --- a/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go +++ b/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go @@ -31,6 +31,8 @@ const ( // Added ~5.1 kindVar kindDatasec + // Added ~5.13 + kindFloat ) // FuncLinkage describes BTF function linkage metadata. @@ -54,7 +56,7 @@ const ( const ( btfTypeKindShift = 24 - btfTypeKindLen = 4 + btfTypeKindLen = 5 btfTypeVlenShift = 0 btfTypeVlenMask = 16 btfTypeKindFlagShift = 31 @@ -67,8 +69,8 @@ type btfType struct { /* "info" bits arrangement * bits 0-15: vlen (e.g. # of struct's members), linkage * bits 16-23: unused - * bits 24-27: kind (e.g. int, ptr, array...etc) - * bits 28-30: unused + * bits 24-28: kind (e.g. int, ptr, array...etc) + * bits 29-30: unused * bit 31: kind_flag, currently used by * struct, union and fwd */ @@ -117,6 +119,8 @@ func (k btfKind) String() string { return "Variable" case kindDatasec: return "Section" + case kindFloat: + return "Float" default: return fmt.Sprintf("Unknown (%d)", k) } @@ -260,6 +264,7 @@ func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) { data = new(btfVariable) case kindDatasec: data = make([]btfVarSecinfo, header.Vlen()) + case kindFloat: default: return nil, fmt.Errorf("type id %v: unknown kind: %v", id, header.Kind()) } diff --git a/vendor/github.com/cilium/ebpf/internal/btf/core.go b/vendor/github.com/cilium/ebpf/internal/btf/core.go index 7c888f602..d02df9d50 100644 --- a/vendor/github.com/cilium/ebpf/internal/btf/core.go +++ b/vendor/github.com/cilium/ebpf/internal/btf/core.go @@ -234,13 +234,13 @@ func coreRelocate(local, target *Spec, relos coreRelos) (COREFixups, error) { } localType := local.types[id] - named, ok := localType.(namedType) - if !ok || named.name() == "" { + named, ok := localType.(NamedType) + if !ok || named.TypeName() == "" { return nil, fmt.Errorf("relocate unnamed or anonymous type %s: %w", localType, ErrNotSupported) } relos := relosByID[id] - targets := target.namedTypes[named.essentialName()] + targets := target.namedTypes[essentialName(named.TypeName())] fixups, err := coreCalculateFixups(localType, targets, relos) if err != nil { return nil, fmt.Errorf("relocate %s: %w", localType, err) @@ -262,7 +262,7 @@ var errImpossibleRelocation = errors.New("impossible relocation") // // The best target is determined by scoring: the less poisoning we have to do // the better the target is. -func coreCalculateFixups(local Type, targets []namedType, relos coreRelos) ([]COREFixup, error) { +func coreCalculateFixups(local Type, targets []NamedType, relos coreRelos) ([]COREFixup, error) { localID := local.ID() local, err := copyType(local, skipQualifierAndTypedef) if err != nil { @@ -467,8 +467,8 @@ func parseCoreAccessor(accessor string) (coreAccessor, error) { return nil, fmt.Errorf("empty accessor") } - var result coreAccessor parts := strings.Split(accessor, ":") + result := make(coreAccessor, 0, len(parts)) for _, part := range parts { // 31 bits to avoid overflowing int on 32 bit platforms. index, err := strconv.ParseUint(part, 10, 31) @@ -564,7 +564,7 @@ func coreFindField(local Type, localAcc coreAccessor, target Type) (_, _ coreFie // This is an anonymous struct or union, ignore it. local = localMember.Type - localOffset += localMember.Offset + localOffset += localMember.OffsetBits localMaybeFlex = false continue } @@ -585,10 +585,10 @@ func coreFindField(local Type, localAcc coreAccessor, target Type) (_, _ coreFie local = localMember.Type localMaybeFlex = acc == len(localMembers)-1 - localOffset += localMember.Offset + localOffset += localMember.OffsetBits target = targetMember.Type targetMaybeFlex = last - targetOffset += targetMember.Offset + targetOffset += targetMember.OffsetBits case *Array: // For arrays, acc is the index in the target. @@ -639,7 +639,7 @@ func coreFindField(local Type, localAcc coreAccessor, target Type) (_, _ coreFie // coreFindMember finds a member in a composite type while handling anonymous // structs and unions. -func coreFindMember(typ composite, name Name) (Member, bool, error) { +func coreFindMember(typ composite, name string) (Member, bool, error) { if name == "" { return Member{}, false, errors.New("can't search for anonymous member") } @@ -670,7 +670,7 @@ func coreFindMember(typ composite, name Name) (Member, bool, error) { for j, member := range members { if member.Name == name { // NB: This is safe because member is a copy. - member.Offset += target.offset + member.OffsetBits += target.offset return member, j == len(members)-1, nil } @@ -685,7 +685,7 @@ func coreFindMember(typ composite, name Name) (Member, bool, error) { return Member{}, false, fmt.Errorf("anonymous non-composite type %T not allowed", member.Type) } - targets = append(targets, offsetTarget{comp, target.offset + member.Offset}) + targets = append(targets, offsetTarget{comp, target.offset + member.OffsetBits}) } } @@ -704,9 +704,9 @@ func coreFindEnumValue(local Type, localAcc coreAccessor, target Type) (localVal return nil, nil, errImpossibleRelocation } - localName := localValue.Name.essentialName() + localName := essentialName(localValue.Name) for i, targetValue := range targetEnum.Values { - if targetValue.Name.essentialName() != localName { + if essentialName(targetValue.Name) != localName { continue } @@ -813,6 +813,7 @@ func coreAreTypesCompatible(localType Type, targetType Type) error { * least one of enums should be anonymous; * - for ENUMs, check sizes, names are ignored; * - for INT, size and signedness are ignored; + * - any two FLOATs are always compatible; * - for ARRAY, dimensionality is ignored, element types are checked for * compatibility recursively; * [ NB: coreAreMembersCompatible doesn't recurse, this check is done @@ -848,16 +849,16 @@ func coreAreMembersCompatible(localType Type, targetType Type) error { } switch lv := localType.(type) { - case *Array, *Pointer: + case *Array, *Pointer, *Float: return nil case *Enum: tv := targetType.(*Enum) - return doNamesMatch(lv.name(), tv.name()) + return doNamesMatch(lv.Name, tv.Name) case *Fwd: tv := targetType.(*Fwd) - return doNamesMatch(lv.name(), tv.name()) + return doNamesMatch(lv.Name, tv.Name) case *Int: tv := targetType.(*Int) diff --git a/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go b/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go index beba1bce6..cdae2ec40 100644 --- a/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go +++ b/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" @@ -64,7 +63,7 @@ func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (f // Of course, the .BTF.ext header has different semantics than the // .BTF ext header. We need to ignore non-null values. - _, err = io.CopyN(ioutil.Discard, r, remainder) + _, err = io.CopyN(io.Discard, r, remainder) if err != nil { return nil, nil, nil, fmt.Errorf("header padding: %v", err) } @@ -114,11 +113,16 @@ type extInfoRecord struct { } type extInfo struct { + byteOrder binary.ByteOrder recordSize uint32 records []extInfoRecord } func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) { + if other.byteOrder != ei.byteOrder { + return extInfo{}, fmt.Errorf("ext_info byte order mismatch, want %v (got %v)", ei.byteOrder, other.byteOrder) + } + if other.recordSize != ei.recordSize { return extInfo{}, fmt.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize) } @@ -131,10 +135,14 @@ func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) { Opaque: info.Opaque, }) } - return extInfo{ei.recordSize, records}, nil + return extInfo{ei.byteOrder, ei.recordSize, records}, nil } func (ei extInfo) MarshalBinary() ([]byte, error) { + if ei.byteOrder != internal.NativeEndian { + return nil, fmt.Errorf("%s is not the native byte order", ei.byteOrder) + } + if len(ei.records) == 0 { return nil, nil } @@ -197,6 +205,7 @@ func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[st } result[secName] = extInfo{ + bo, recordSize, records, } diff --git a/vendor/github.com/cilium/ebpf/internal/btf/fuzz.go b/vendor/github.com/cilium/ebpf/internal/btf/fuzz.go index 37e043fd3..220b285af 100644 --- a/vendor/github.com/cilium/ebpf/internal/btf/fuzz.go +++ b/vendor/github.com/cilium/ebpf/internal/btf/fuzz.go @@ -1,3 +1,4 @@ +//go:build gofuzz // +build gofuzz // Use with https://github.com/dvyukov/go-fuzz diff --git a/vendor/github.com/cilium/ebpf/internal/btf/info.go b/vendor/github.com/cilium/ebpf/internal/btf/info.go new file mode 100644 index 000000000..6a9b5d2e0 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/info.go @@ -0,0 +1,48 @@ +package btf + +import ( + "bytes" + + "github.com/cilium/ebpf/internal" +) + +// info describes a BTF object. +type info struct { + BTF *Spec + ID ID + // Name is an identifying name for the BTF, currently only used by the + // kernel. + Name string + // KernelBTF is true if the BTf originated with the kernel and not + // userspace. + KernelBTF bool +} + +func newInfoFromFd(fd *internal.FD) (*info, error) { + // We invoke the syscall once with a empty BTF and name buffers to get size + // information to allocate buffers. Then we invoke it a second time with + // buffers to receive the data. + bpfInfo, err := bpfGetBTFInfoByFD(fd, nil, nil) + if err != nil { + return nil, err + } + + btfBuffer := make([]byte, bpfInfo.btfSize) + nameBuffer := make([]byte, bpfInfo.nameLen) + bpfInfo, err = bpfGetBTFInfoByFD(fd, btfBuffer, nameBuffer) + if err != nil { + return nil, err + } + + spec, err := loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, nil, nil) + if err != nil { + return nil, err + } + + return &info{ + BTF: spec, + ID: ID(bpfInfo.id), + Name: internal.CString(nameBuffer), + KernelBTF: bpfInfo.kernelBTF != 0, + }, nil +} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/strings.go b/vendor/github.com/cilium/ebpf/internal/btf/strings.go index 8782643a0..9876aa227 100644 --- a/vendor/github.com/cilium/ebpf/internal/btf/strings.go +++ b/vendor/github.com/cilium/ebpf/internal/btf/strings.go @@ -5,13 +5,12 @@ import ( "errors" "fmt" "io" - "io/ioutil" ) type stringTable []byte func readStringTable(r io.Reader) (stringTable, error) { - contents, err := ioutil.ReadAll(r) + contents, err := io.ReadAll(r) if err != nil { return nil, fmt.Errorf("can't read string table: %v", err) } @@ -53,8 +52,3 @@ func (st stringTable) Lookup(offset uint32) (string, error) { return string(str[:end]), nil } - -func (st stringTable) LookupName(offset uint32) (Name, error) { - str, err := st.Lookup(offset) - return Name(str), err -} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/syscalls.go b/vendor/github.com/cilium/ebpf/internal/btf/syscalls.go new file mode 100644 index 000000000..a4f80abd0 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/syscalls.go @@ -0,0 +1,31 @@ +package btf + +import ( + "fmt" + "unsafe" + + "github.com/cilium/ebpf/internal" +) + +type bpfBTFInfo struct { + btf internal.Pointer + btfSize uint32 + id uint32 + name internal.Pointer + nameLen uint32 + kernelBTF uint32 +} + +func bpfGetBTFInfoByFD(fd *internal.FD, btf, name []byte) (*bpfBTFInfo, error) { + info := bpfBTFInfo{ + btf: internal.NewSlicePointer(btf), + btfSize: uint32(len(btf)), + name: internal.NewSlicePointer(name), + nameLen: uint32(len(name)), + } + if err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil { + return nil, fmt.Errorf("can't get program info: %w", err) + } + + return &info, nil +} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/types.go b/vendor/github.com/cilium/ebpf/internal/btf/types.go index 62aa31bcd..5c8e7c6e5 100644 --- a/vendor/github.com/cilium/ebpf/internal/btf/types.go +++ b/vendor/github.com/cilium/ebpf/internal/btf/types.go @@ -30,27 +30,26 @@ type Type interface { walk(*typeDeque) } -// namedType is a type with a name. -// -// Most named types simply embed Name. -type namedType interface { +// NamedType is a type with a name. +type NamedType interface { Type - name() string - essentialName() string -} - -// Name identifies a type. -// -// Anonymous types have an empty name. -type Name string -func (n Name) name() string { - return string(n) + // Name of the type, empty for anonymous types. + TypeName() string } -func (n Name) essentialName() string { - return essentialName(string(n)) -} +var ( + _ NamedType = (*Int)(nil) + _ NamedType = (*Struct)(nil) + _ NamedType = (*Union)(nil) + _ NamedType = (*Enum)(nil) + _ NamedType = (*Fwd)(nil) + _ NamedType = (*Func)(nil) + _ NamedType = (*Typedef)(nil) + _ NamedType = (*Var)(nil) + _ NamedType = (*Datasec)(nil) + _ NamedType = (*Float)(nil) +) // Void is the unit type of BTF. type Void struct{} @@ -72,19 +71,17 @@ const ( // Int is an integer of a given length. type Int struct { TypeID - Name + Name string // The size of the integer in bytes. Size uint32 Encoding IntEncoding - // Offset is the starting bit offset. Currently always 0. + // OffsetBits is the starting bit offset. Currently always 0. // See https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-int - Offset uint32 - Bits byte + OffsetBits uint32 + Bits byte } -var _ namedType = (*Int)(nil) - func (i *Int) String() string { var s strings.Builder @@ -110,15 +107,16 @@ func (i *Int) String() string { return s.String() } -func (i *Int) size() uint32 { return i.Size } -func (i *Int) walk(*typeDeque) {} +func (i *Int) TypeName() string { return i.Name } +func (i *Int) size() uint32 { return i.Size } +func (i *Int) walk(*typeDeque) {} func (i *Int) copy() Type { cpy := *i return &cpy } func (i *Int) isBitfield() bool { - return i.Offset > 0 + return i.OffsetBits > 0 } // Pointer is a pointer to another type. @@ -158,7 +156,7 @@ func (arr *Array) copy() Type { // Struct is a compound type of consecutive members. type Struct struct { TypeID - Name + Name string // The size of the struct including padding, in bytes Size uint32 Members []Member @@ -168,6 +166,8 @@ func (s *Struct) String() string { return fmt.Sprintf("struct#%d[%q]", s.TypeID, s.Name) } +func (s *Struct) TypeName() string { return s.Name } + func (s *Struct) size() uint32 { return s.Size } func (s *Struct) walk(tdq *typeDeque) { @@ -189,7 +189,7 @@ func (s *Struct) members() []Member { // Union is a compound type where members occupy the same memory. type Union struct { TypeID - Name + Name string // The size of the union including padding, in bytes. Size uint32 Members []Member @@ -199,6 +199,8 @@ func (u *Union) String() string { return fmt.Sprintf("union#%d[%q]", u.TypeID, u.Name) } +func (u *Union) TypeName() string { return u.Name } + func (u *Union) size() uint32 { return u.Size } func (u *Union) walk(tdq *typeDeque) { @@ -236,17 +238,17 @@ var ( // // It is not a valid Type. type Member struct { - Name + Name string Type Type - // Offset is the bit offset of this member - Offset uint32 + // OffsetBits is the bit offset of this member. + OffsetBits uint32 BitfieldSize uint32 } // Enum lists possible values. type Enum struct { TypeID - Name + Name string Values []EnumValue } @@ -254,11 +256,13 @@ func (e *Enum) String() string { return fmt.Sprintf("enum#%d[%q]", e.TypeID, e.Name) } +func (e *Enum) TypeName() string { return e.Name } + // EnumValue is part of an Enum // // Is is not a valid Type type EnumValue struct { - Name + Name string Value int32 } @@ -294,7 +298,7 @@ func (fk FwdKind) String() string { // Fwd is a forward declaration of a Type. type Fwd struct { TypeID - Name + Name string Kind FwdKind } @@ -302,6 +306,8 @@ func (f *Fwd) String() string { return fmt.Sprintf("fwd#%d[%s %q]", f.TypeID, f.Kind, f.Name) } +func (f *Fwd) TypeName() string { return f.Name } + func (f *Fwd) walk(*typeDeque) {} func (f *Fwd) copy() Type { cpy := *f @@ -311,7 +317,7 @@ func (f *Fwd) copy() Type { // Typedef is an alias of a Type. type Typedef struct { TypeID - Name + Name string Type Type } @@ -319,6 +325,8 @@ func (td *Typedef) String() string { return fmt.Sprintf("typedef#%d[%q #%d]", td.TypeID, td.Name, td.Type.ID()) } +func (td *Typedef) TypeName() string { return td.Name } + func (td *Typedef) walk(tdq *typeDeque) { tdq.push(&td.Type) } func (td *Typedef) copy() Type { cpy := *td @@ -379,7 +387,7 @@ func (r *Restrict) copy() Type { // Func is a function definition. type Func struct { TypeID - Name + Name string Type Type Linkage FuncLinkage } @@ -388,6 +396,8 @@ func (f *Func) String() string { return fmt.Sprintf("func#%d[%s %q proto=#%d]", f.TypeID, f.Linkage, f.Name, f.Type.ID()) } +func (f *Func) TypeName() string { return f.Name } + func (f *Func) walk(tdq *typeDeque) { tdq.push(&f.Type) } func (f *Func) copy() Type { cpy := *f @@ -426,14 +436,14 @@ func (fp *FuncProto) copy() Type { } type FuncParam struct { - Name + Name string Type Type } // Var is a global variable. type Var struct { TypeID - Name + Name string Type Type Linkage VarLinkage } @@ -442,6 +452,8 @@ func (v *Var) String() string { return fmt.Sprintf("var#%d[%s %q]", v.TypeID, v.Linkage, v.Name) } +func (v *Var) TypeName() string { return v.Name } + func (v *Var) walk(tdq *typeDeque) { tdq.push(&v.Type) } func (v *Var) copy() Type { cpy := *v @@ -451,7 +463,7 @@ func (v *Var) copy() Type { // Datasec is a global program section containing data. type Datasec struct { TypeID - Name + Name string Size uint32 Vars []VarSecinfo } @@ -460,6 +472,8 @@ func (ds *Datasec) String() string { return fmt.Sprintf("section#%d[%q]", ds.TypeID, ds.Name) } +func (ds *Datasec) TypeName() string { return ds.Name } + func (ds *Datasec) size() uint32 { return ds.Size } func (ds *Datasec) walk(tdq *typeDeque) { @@ -475,7 +489,7 @@ func (ds *Datasec) copy() Type { return &cpy } -// VarSecinfo describes variable in a Datasec +// VarSecinfo describes variable in a Datasec. // // It is not a valid Type. type VarSecinfo struct { @@ -484,6 +498,27 @@ type VarSecinfo struct { Size uint32 } +// Float is a float of a given length. +type Float struct { + TypeID + Name string + + // The size of the float in bytes. + Size uint32 +} + +func (f *Float) String() string { + return fmt.Sprintf("float%d#%d[%q]", f.Size*8, f.TypeID, f.Name) +} + +func (f *Float) TypeName() string { return f.Name } +func (f *Float) size() uint32 { return f.Size } +func (f *Float) walk(*typeDeque) {} +func (f *Float) copy() Type { + cpy := *f + return &cpy +} + type sizer interface { size() uint32 } @@ -565,14 +600,36 @@ func Sizeof(typ Type) (int, error) { // // Returns any errors from transform verbatim. func copyType(typ Type, transform func(Type) (Type, error)) (Type, error) { - var ( - copies = make(map[Type]Type) - work typeDeque - ) + copies := make(copier) + return typ, copies.copy(&typ, transform) +} - for t := &typ; t != nil; t = work.pop() { +// copy a slice of Types recursively. +// +// Types may form a cycle. +// +// Returns any errors from transform verbatim. +func copyTypes(types []Type, transform func(Type) (Type, error)) ([]Type, error) { + result := make([]Type, len(types)) + copy(result, types) + + copies := make(copier) + for i := range result { + if err := copies.copy(&result[i], transform); err != nil { + return nil, err + } + } + + return result, nil +} + +type copier map[Type]Type + +func (c copier) copy(typ *Type, transform func(Type) (Type, error)) error { + var work typeDeque + for t := typ; t != nil; t = work.pop() { // *t is the identity of the type. - if cpy := copies[*t]; cpy != nil { + if cpy := c[*t]; cpy != nil { *t = cpy continue } @@ -581,21 +638,21 @@ func copyType(typ Type, transform func(Type) (Type, error)) (Type, error) { if transform != nil { tf, err := transform(*t) if err != nil { - return nil, fmt.Errorf("copy %s: %w", typ, err) + return fmt.Errorf("copy %s: %w", *t, err) } cpy = tf.copy() } else { cpy = (*t).copy() } - copies[*t] = cpy + c[*t] = cpy *t = cpy // Mark any nested types for copying. cpy.walk(&work) } - return typ, nil + return nil } // typeDeque keeps track of pointers to types which still @@ -606,6 +663,10 @@ type typeDeque struct { mask uint64 } +func (dq *typeDeque) empty() bool { + return dq.read == dq.write +} + // push adds a type to the stack. func (dq *typeDeque) push(t *Type) { if dq.write-dq.read < uint64(len(dq.types)) { @@ -632,7 +693,7 @@ func (dq *typeDeque) push(t *Type) { // shift returns the first element or null. func (dq *typeDeque) shift() *Type { - if dq.read == dq.write { + if dq.empty() { return nil } @@ -645,7 +706,7 @@ func (dq *typeDeque) shift() *Type { // pop returns the last element or null. func (dq *typeDeque) pop() *Type { - if dq.read == dq.write { + if dq.empty() { return nil } @@ -674,7 +735,7 @@ func (dq *typeDeque) all() []*Type { // Returns a map of named types (so, where NameOff is non-zero) and a slice of types // indexed by TypeID. Since BTF ignores compilation units, multiple types may share // the same name. A Type may form a cyclic graph by pointing at itself. -func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type, namedTypes map[string][]namedType, err error) { +func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type, namedTypes map[string][]NamedType, err error) { type fixupDef struct { id TypeID expectedKind btfKind @@ -691,17 +752,17 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type, // work, since otherwise append might re-allocate members. members := make([]Member, 0, len(raw)) for i, btfMember := range raw { - name, err := rawStrings.LookupName(btfMember.NameOff) + name, err := rawStrings.Lookup(btfMember.NameOff) if err != nil { return nil, fmt.Errorf("can't get name for member %d: %w", i, err) } m := Member{ - Name: name, - Offset: btfMember.Offset, + Name: name, + OffsetBits: btfMember.Offset, } if kindFlag { m.BitfieldSize = btfMember.Offset >> 24 - m.Offset &= 0xffffff + m.OffsetBits &= 0xffffff } members = append(members, m) } @@ -713,7 +774,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type, types = make([]Type, 0, len(rawTypes)) types = append(types, (*Void)(nil)) - namedTypes = make(map[string][]namedType) + namedTypes = make(map[string][]NamedType) for i, raw := range rawTypes { var ( @@ -723,7 +784,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type, typ Type ) - name, err := rawStrings.LookupName(raw.NameOff) + name, err := rawStrings.Lookup(raw.NameOff) if err != nil { return nil, nil, fmt.Errorf("get name for type id %d: %w", id, err) } @@ -765,7 +826,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type, rawvals := raw.data.([]btfEnum) vals := make([]EnumValue, 0, len(rawvals)) for i, btfVal := range rawvals { - name, err := rawStrings.LookupName(btfVal.NameOff) + name, err := rawStrings.Lookup(btfVal.NameOff) if err != nil { return nil, nil, fmt.Errorf("get name for enum value %d: %s", i, err) } @@ -812,7 +873,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type, rawparams := raw.data.([]btfParam) params := make([]FuncParam, 0, len(rawparams)) for i, param := range rawparams { - name, err := rawStrings.LookupName(param.NameOff) + name, err := rawStrings.Lookup(param.NameOff) if err != nil { return nil, nil, fmt.Errorf("get name for func proto parameter %d: %s", i, err) } @@ -848,14 +909,17 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type, } typ = &Datasec{id, name, raw.SizeType, vars} + case kindFloat: + typ = &Float{id, name, raw.Size()} + default: return nil, nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind()) } types = append(types, typ) - if named, ok := typ.(namedType); ok { - if name := essentialName(named.name()); name != "" { + if named, ok := typ.(NamedType); ok { + if name := essentialName(named.TypeName()); name != "" { namedTypes[name] = append(namedTypes[name], named) } } diff --git a/vendor/github.com/cilium/ebpf/internal/cpu.go b/vendor/github.com/cilium/ebpf/internal/cpu.go index d3424ba43..3affa1efb 100644 --- a/vendor/github.com/cilium/ebpf/internal/cpu.go +++ b/vendor/github.com/cilium/ebpf/internal/cpu.go @@ -2,7 +2,7 @@ package internal import ( "fmt" - "io/ioutil" + "os" "strings" "sync" ) @@ -24,7 +24,7 @@ func PossibleCPUs() (int, error) { } func parseCPUsFromFile(path string) (int, error) { - spec, err := ioutil.ReadFile(path) + spec, err := os.ReadFile(path) if err != nil { return 0, err } diff --git a/vendor/github.com/cilium/ebpf/internal/ptr_32_be.go b/vendor/github.com/cilium/ebpf/internal/ptr_32_be.go index a56fbcc8e..8c114ddf4 100644 --- a/vendor/github.com/cilium/ebpf/internal/ptr_32_be.go +++ b/vendor/github.com/cilium/ebpf/internal/ptr_32_be.go @@ -1,3 +1,4 @@ +//go:build armbe || mips || mips64p32 // +build armbe mips mips64p32 package internal diff --git a/vendor/github.com/cilium/ebpf/internal/ptr_32_le.go b/vendor/github.com/cilium/ebpf/internal/ptr_32_le.go index be2ecfca7..e65a61e45 100644 --- a/vendor/github.com/cilium/ebpf/internal/ptr_32_le.go +++ b/vendor/github.com/cilium/ebpf/internal/ptr_32_le.go @@ -1,3 +1,4 @@ +//go:build 386 || amd64p32 || arm || mipsle || mips64p32le // +build 386 amd64p32 arm mipsle mips64p32le package internal diff --git a/vendor/github.com/cilium/ebpf/internal/ptr_64.go b/vendor/github.com/cilium/ebpf/internal/ptr_64.go index 69452dceb..71a3afe30 100644 --- a/vendor/github.com/cilium/ebpf/internal/ptr_64.go +++ b/vendor/github.com/cilium/ebpf/internal/ptr_64.go @@ -1,5 +1,5 @@ -// +build !386,!amd64p32,!arm,!mipsle,!mips64p32le -// +build !armbe,!mips,!mips64p32 +//go:build !386 && !amd64p32 && !arm && !mipsle && !mips64p32le && !armbe && !mips && !mips64p32 +// +build !386,!amd64p32,!arm,!mipsle,!mips64p32le,!armbe,!mips,!mips64p32 package internal diff --git a/vendor/github.com/cilium/ebpf/internal/syscall.go b/vendor/github.com/cilium/ebpf/internal/syscall.go index b766e643e..b75037bb9 100644 --- a/vendor/github.com/cilium/ebpf/internal/syscall.go +++ b/vendor/github.com/cilium/ebpf/internal/syscall.go @@ -1,6 +1,7 @@ package internal import ( + "errors" "fmt" "path/filepath" "runtime" @@ -68,6 +69,48 @@ func BPF(cmd BPFCmd, attr unsafe.Pointer, size uintptr) (uintptr, error) { return r1, err } +type BPFProgLoadAttr struct { + ProgType uint32 + InsCount uint32 + Instructions Pointer + License Pointer + LogLevel uint32 + LogSize uint32 + LogBuf Pointer + KernelVersion uint32 // since 4.1 2541517c32be + ProgFlags uint32 // since 4.11 e07b98d9bffe + ProgName BPFObjName // since 4.15 067cae47771c + ProgIfIndex uint32 // since 4.15 1f6f4cb7ba21 + ExpectedAttachType uint32 // since 4.17 5e43f899b03a + ProgBTFFd uint32 + FuncInfoRecSize uint32 + FuncInfo Pointer + FuncInfoCnt uint32 + LineInfoRecSize uint32 + LineInfo Pointer + LineInfoCnt uint32 + AttachBTFID uint32 + AttachProgFd uint32 +} + +// BPFProgLoad wraps BPF_PROG_LOAD. +func BPFProgLoad(attr *BPFProgLoadAttr) (*FD, error) { + for { + fd, err := BPF(BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + // As of ~4.20 the verifier can be interrupted by a signal, + // and returns EAGAIN in that case. + if errors.Is(err, unix.EAGAIN) { + continue + } + + if err != nil { + return nil, err + } + + return NewFD(uint32(fd)), nil + } +} + type BPFProgAttachAttr struct { TargetFd uint32 AttachBpfFd uint32 @@ -180,6 +223,22 @@ func BPFObjGetInfoByFD(fd *FD, info unsafe.Pointer, size uintptr) error { return nil } +type bpfGetFDByIDAttr struct { + id uint32 + next uint32 +} + +// BPFObjGetInfoByFD wraps BPF_*_GET_FD_BY_ID. +// +// Available from 4.13. +func BPFObjGetFDByID(cmd BPFCmd, id uint32) (*FD, error) { + attr := bpfGetFDByIDAttr{ + id: id, + } + ptr, err := BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return NewFD(uint32(ptr)), err +} + // BPFObjName is a null-terminated string made up of // 'A-Za-z0-9_' characters. type BPFObjName [unix.BPF_OBJ_NAME_LEN]byte diff --git a/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go index 0a18eaf0c..9aa70fa78 100644 --- a/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package unix @@ -20,10 +21,11 @@ const ( EPERM = linux.EPERM ESRCH = linux.ESRCH ENODEV = linux.ENODEV + EBADF = linux.EBADF + E2BIG = linux.E2BIG // ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP ENOTSUPP = syscall.Errno(0x20c) - EBADF = linux.EBADF BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE BPF_F_RDONLY = linux.BPF_F_RDONLY @@ -35,6 +37,9 @@ const ( BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN BPF_TAG_SIZE = linux.BPF_TAG_SIZE + BPF_RINGBUF_BUSY_BIT = linux.BPF_RINGBUF_BUSY_BIT + BPF_RINGBUF_DISCARD_BIT = linux.BPF_RINGBUF_DISCARD_BIT + BPF_RINGBUF_HDR_SZ = linux.BPF_RINGBUF_HDR_SZ SYS_BPF = linux.SYS_BPF F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD @@ -69,11 +74,6 @@ type Statfs_t = linux.Statfs_t // Rlimit is a wrapper type Rlimit = linux.Rlimit -// Setrlimit is a wrapper -func Setrlimit(resource int, rlim *Rlimit) (err error) { - return linux.Setrlimit(resource, rlim) -} - // Syscall is a wrapper func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return linux.Syscall(trap, a1, a2, a3) @@ -202,3 +202,7 @@ func KernelRelease() (string, error) { release := string(uname.Release[:end]) return release, nil } + +func Prlimit(pid, resource int, new, old *Rlimit) error { + return linux.Prlimit(pid, resource, new, old) +} diff --git a/vendor/github.com/cilium/ebpf/internal/unix/types_other.go b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go index 1b06defc0..4f50d896e 100644 --- a/vendor/github.com/cilium/ebpf/internal/unix/types_other.go +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package unix @@ -21,6 +22,7 @@ const ( ESRCH = syscall.ESRCH ENODEV = syscall.ENODEV EBADF = syscall.Errno(0) + E2BIG = syscall.Errno(0) // ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP ENOTSUPP = syscall.Errno(0x20c) @@ -35,6 +37,9 @@ const ( BPF_F_INNER_MAP = 0 BPF_OBJ_NAME_LEN = 0x10 BPF_TAG_SIZE = 0x8 + BPF_RINGBUF_BUSY_BIT = 0 + BPF_RINGBUF_DISCARD_BIT = 0 + BPF_RINGBUF_HDR_SZ = 0 SYS_BPF = 321 F_DUPFD_CLOEXEC = 0x406 EPOLLIN = 0x1 @@ -86,11 +91,6 @@ type Rlimit struct { Max uint64 } -// Setrlimit is a wrapper -func Setrlimit(resource int, rlim *Rlimit) (err error) { - return errNonLinux -} - // Syscall is a wrapper func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { return 0, 0, syscall.Errno(1) @@ -261,3 +261,7 @@ func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags func KernelRelease() (string, error) { return "", errNonLinux } + +func Prlimit(pid, resource int, new, old *Rlimit) error { + return errNonLinux +} diff --git a/vendor/github.com/cilium/ebpf/internal/version.go b/vendor/github.com/cilium/ebpf/internal/version.go index 1a678bfe6..4915e5837 100644 --- a/vendor/github.com/cilium/ebpf/internal/version.go +++ b/vendor/github.com/cilium/ebpf/internal/version.go @@ -2,7 +2,7 @@ package internal import ( "fmt" - "io/ioutil" + "os" "regexp" "sync" @@ -109,7 +109,7 @@ func detectKernelVersion() (Version, error) { // Example format: Ubuntu 4.15.0-91.92-generic 4.15.18 // This method exists in the kernel itself, see d18acd15c // ("perf tools: Fix kernel version error in ubuntu"). - if pvs, err := ioutil.ReadFile("/proc/version_signature"); err == nil { + if pvs, err := os.ReadFile("/proc/version_signature"); err == nil { // If /proc/version_signature exists, failing to parse it is an error. // It only exists on Ubuntu, where the real patch level is not obtainable // through any other method. diff --git a/vendor/github.com/cilium/ebpf/link/BUILD.bazel b/vendor/github.com/cilium/ebpf/link/BUILD.bazel index b50621bbc..02492f319 100644 --- a/vendor/github.com/cilium/ebpf/link/BUILD.bazel +++ b/vendor/github.com/cilium/ebpf/link/BUILD.bazel @@ -5,6 +5,7 @@ go_library( srcs = [ "cgroup.go", "doc.go", + "freplace.go", "iter.go", "kprobe.go", "link.go", @@ -24,6 +25,7 @@ go_library( "//vendor/github.com/cilium/ebpf:go_default_library", "//vendor/github.com/cilium/ebpf/asm:go_default_library", "//vendor/github.com/cilium/ebpf/internal:go_default_library", + "//vendor/github.com/cilium/ebpf/internal/btf:go_default_library", "//vendor/github.com/cilium/ebpf/internal/unix:go_default_library", ], ) diff --git a/vendor/github.com/cilium/ebpf/link/freplace.go b/vendor/github.com/cilium/ebpf/link/freplace.go new file mode 100644 index 000000000..a698e1a9d --- /dev/null +++ b/vendor/github.com/cilium/ebpf/link/freplace.go @@ -0,0 +1,88 @@ +package link + +import ( + "fmt" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/internal/btf" +) + +type FreplaceLink struct { + RawLink +} + +// AttachFreplace attaches the given eBPF program to the function it replaces. +// +// The program and name can either be provided at link time, or can be provided +// at program load time. If they were provided at load time, they should be nil +// and empty respectively here, as they will be ignored by the kernel. +// Examples: +// +// AttachFreplace(dispatcher, "function", replacement) +// AttachFreplace(nil, "", replacement) +func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (*FreplaceLink, error) { + if (name == "") != (targetProg == nil) { + return nil, fmt.Errorf("must provide both or neither of name and targetProg: %w", errInvalidInput) + } + if prog == nil { + return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput) + } + if prog.Type() != ebpf.Extension { + return nil, fmt.Errorf("eBPF program type %s is not an Extension: %w", prog.Type(), errInvalidInput) + } + + var ( + target int + typeID btf.TypeID + ) + if targetProg != nil { + info, err := targetProg.Info() + if err != nil { + return nil, err + } + btfID, ok := info.BTFID() + if !ok { + return nil, fmt.Errorf("could not get BTF ID for program %s: %w", info.Name, errInvalidInput) + } + btfHandle, err := btf.NewHandleFromID(btfID) + if err != nil { + return nil, err + } + defer btfHandle.Close() + + var function *btf.Func + if err := btfHandle.Spec().FindType(name, &function); err != nil { + return nil, err + } + + target = targetProg.FD() + typeID = function.ID() + } + + link, err := AttachRawLink(RawLinkOptions{ + Target: target, + Program: prog, + Attach: ebpf.AttachNone, + BTF: typeID, + }) + if err != nil { + return nil, err + } + + return &FreplaceLink{*link}, nil +} + +// Update implements the Link interface. +func (f *FreplaceLink) Update(new *ebpf.Program) error { + return fmt.Errorf("freplace update: %w", ErrNotSupported) +} + +// LoadPinnedFreplace loads a pinned iterator from a bpffs. +func LoadPinnedFreplace(fileName string, opts *ebpf.LoadPinOptions) (*FreplaceLink, error) { + link, err := LoadPinnedRawLink(fileName, TracingType, opts) + if err != nil { + return nil, err + } + + return &FreplaceLink{*link}, err +} diff --git a/vendor/github.com/cilium/ebpf/link/kprobe.go b/vendor/github.com/cilium/ebpf/link/kprobe.go index ea71d6d60..b6577b5a9 100644 --- a/vendor/github.com/cilium/ebpf/link/kprobe.go +++ b/vendor/github.com/cilium/ebpf/link/kprobe.go @@ -5,7 +5,6 @@ import ( "crypto/rand" "errors" "fmt" - "io/ioutil" "os" "path/filepath" "runtime" @@ -72,10 +71,11 @@ func (pt probeType) RetprobeBit() (uint64, error) { // given kernel symbol starts executing. See /proc/kallsyms for available // symbols. For example, printk(): // -// Kprobe("printk", prog) +// kp, err := Kprobe("printk", prog) // -// The resulting Link must be Closed during program shutdown to avoid leaking -// system resources. +// Losing the reference to the resulting Link (kp) will close the Kprobe +// and prevent further execution of prog. The Link must be Closed during +// program shutdown to avoid leaking system resources. func Kprobe(symbol string, prog *ebpf.Program) (Link, error) { k, err := kprobe(symbol, prog, false) if err != nil { @@ -95,10 +95,11 @@ func Kprobe(symbol string, prog *ebpf.Program) (Link, error) { // before the given kernel symbol exits, with the function stack left intact. // See /proc/kallsyms for available symbols. For example, printk(): // -// Kretprobe("printk", prog) +// kp, err := Kretprobe("printk", prog) // -// The resulting Link must be Closed during program shutdown to avoid leaking -// system resources. +// Losing the reference to the resulting Link (kp) will close the Kretprobe +// and prevent further execution of prog. The Link must be Closed during +// program shutdown to avoid leaking system resources. func Kretprobe(symbol string, prog *ebpf.Program) (Link, error) { k, err := kprobe(symbol, prog, true) if err != nil { @@ -157,7 +158,7 @@ func kprobe(symbol string, prog *ebpf.Program, ret bool) (*perfEvent, error) { // pmuKprobe opens a perf event based on the kprobe PMU. // Returns os.ErrNotExist if the given symbol does not exist in the kernel. func pmuKprobe(symbol string, ret bool) (*perfEvent, error) { - return pmuProbe(kprobeType, symbol, "", 0, ret) + return pmuProbe(kprobeType, symbol, "", 0, perfAllThreads, ret) } // pmuProbe opens a perf event based on a Performance Monitoring Unit. @@ -167,7 +168,7 @@ func pmuKprobe(symbol string, ret bool) (*perfEvent, error) { // 33ea4b24277b "perf/core: Implement the 'perf_uprobe' PMU" // // Returns ErrNotSupported if the kernel doesn't support perf_[k,u]probe PMU -func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*perfEvent, error) { +func pmuProbe(typ probeType, symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) { // Getting the PMU type will fail if the kernel doesn't support // the perf_[k,u]probe PMU. et, err := getPMUEventType(typ) @@ -191,7 +192,7 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per switch typ { case kprobeType: // Create a pointer to a NUL-terminated string for the kernel. - sp, err := unsafeStringPtr(symbol) + sp, err = unsafeStringPtr(symbol) if err != nil { return nil, err } @@ -202,7 +203,7 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per Config: config, // Retprobe flag } case uprobeType: - sp, err := unsafeStringPtr(path) + sp, err = unsafeStringPtr(path) if err != nil { return nil, err } @@ -220,7 +221,7 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per } } - fd, err := unix.PerfEventOpen(&attr, perfAllThreads, 0, -1, unix.PERF_FLAG_FD_CLOEXEC) + fd, err := unix.PerfEventOpen(&attr, pid, 0, -1, unix.PERF_FLAG_FD_CLOEXEC) // Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL // when trying to create a kretprobe for a missing symbol. Make sure ENOENT @@ -228,6 +229,11 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) { return nil, fmt.Errorf("symbol '%s' not found: %w", symbol, os.ErrNotExist) } + // Since at least commit cb9a19fe4aa51, ENOTSUPP is returned + // when attempting to set a uprobe on a trap instruction. + if errors.Is(err, unix.ENOTSUPP) { + return nil, fmt.Errorf("failed setting uprobe on offset %#x (possible trap insn): %w", offset, err) + } if err != nil { return nil, fmt.Errorf("opening perf event: %w", err) } @@ -246,7 +252,7 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per // tracefsKprobe creates a Kprobe tracefs entry. func tracefsKprobe(symbol string, ret bool) (*perfEvent, error) { - return tracefsProbe(kprobeType, symbol, "", 0, ret) + return tracefsProbe(kprobeType, symbol, "", 0, perfAllThreads, ret) } // tracefsProbe creates a trace event by writing an entry to <tracefs>/[k,u]probe_events. @@ -255,7 +261,7 @@ func tracefsKprobe(symbol string, ret bool) (*perfEvent, error) { // Path and offset are only set in the case of uprobe(s) and are used to set // the executable/library path on the filesystem and the offset where the probe is inserted. // A perf event is then opened on the newly-created trace event and returned to the caller. -func tracefsProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*perfEvent, error) { +func tracefsProbe(typ probeType, symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) { // Generate a random string for each trace event we attempt to create. // This value is used as the 'group' token in tracefs to allow creating // multiple kprobe trace events with the same name. @@ -288,7 +294,7 @@ func tracefsProbe(typ probeType, symbol, path string, offset uint64, ret bool) ( } // Kprobes are ephemeral tracepoints and share the same perf event type. - fd, err := openTracepointPerfEvent(tid) + fd, err := openTracepointPerfEvent(tid, pid) if err != nil { return nil, err } @@ -413,7 +419,7 @@ func probePrefix(ret bool) string { func determineRetprobeBit(typ probeType) (uint64, error) { p := filepath.Join("/sys/bus/event_source/devices/", typ.String(), "/format/retprobe") - data, err := ioutil.ReadFile(p) + data, err := os.ReadFile(p) if err != nil { return 0, err } diff --git a/vendor/github.com/cilium/ebpf/link/link.go b/vendor/github.com/cilium/ebpf/link/link.go index 16cfff415..492658469 100644 --- a/vendor/github.com/cilium/ebpf/link/link.go +++ b/vendor/github.com/cilium/ebpf/link/link.go @@ -6,6 +6,7 @@ import ( "github.com/cilium/ebpf" "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" ) var ErrNotSupported = internal.ErrNotSupported @@ -29,8 +30,8 @@ type Link interface { // Close frees resources. // - // The link will be broken unless it has been pinned. A link - // may continue past the lifetime of the process if Close is + // The link will be broken unless it has been successfully pinned. + // A link may continue past the lifetime of the process if Close is // not called. Close() error @@ -49,6 +50,8 @@ type RawLinkOptions struct { Program *ebpf.Program // Attach must match the attach type of Program. Attach ebpf.AttachType + // BTF is the BTF of the attachment target. + BTF btf.TypeID } // RawLinkInfo contains metadata on a link. @@ -83,9 +86,10 @@ func AttachRawLink(opts RawLinkOptions) (*RawLink, error) { } attr := bpfLinkCreateAttr{ - targetFd: uint32(opts.Target), - progFd: uint32(progFd), - attachType: opts.Attach, + targetFd: uint32(opts.Target), + progFd: uint32(progFd), + attachType: opts.Attach, + targetBTFID: uint32(opts.BTF), } fd, err := bpfLinkCreate(&attr) if err != nil { diff --git a/vendor/github.com/cilium/ebpf/link/perf_event.go b/vendor/github.com/cilium/ebpf/link/perf_event.go index 5267a47ec..7e0443a75 100644 --- a/vendor/github.com/cilium/ebpf/link/perf_event.go +++ b/vendor/github.com/cilium/ebpf/link/perf_event.go @@ -4,7 +4,6 @@ import ( "bytes" "errors" "fmt" - "io/ioutil" "os" "path/filepath" "regexp" @@ -236,7 +235,7 @@ func getPMUEventType(typ probeType) (uint64, error) { // openTracepointPerfEvent opens a tracepoint-type perf event. System-wide // [k,u]probes created by writing to <tracefs>/[k,u]probe_events are tracepoints // behind the scenes, and can be attached to using these perf events. -func openTracepointPerfEvent(tid uint64) (*internal.FD, error) { +func openTracepointPerfEvent(tid uint64, pid int) (*internal.FD, error) { attr := unix.PerfEventAttr{ Type: unix.PERF_TYPE_TRACEPOINT, Config: tid, @@ -245,7 +244,7 @@ func openTracepointPerfEvent(tid uint64) (*internal.FD, error) { Wakeup: 1, } - fd, err := unix.PerfEventOpen(&attr, perfAllThreads, 0, -1, unix.PERF_FLAG_FD_CLOEXEC) + fd, err := unix.PerfEventOpen(&attr, pid, 0, -1, unix.PERF_FLAG_FD_CLOEXEC) if err != nil { return nil, fmt.Errorf("opening tracepoint perf event: %w", err) } @@ -263,7 +262,7 @@ func uint64FromFile(base string, path ...string) (uint64, error) { return 0, fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, errInvalidInput) } - data, err := ioutil.ReadFile(p) + data, err := os.ReadFile(p) if err != nil { return 0, fmt.Errorf("reading file %s: %w", p, err) } diff --git a/vendor/github.com/cilium/ebpf/link/syscalls.go b/vendor/github.com/cilium/ebpf/link/syscalls.go index 30e8a8805..a61499438 100644 --- a/vendor/github.com/cilium/ebpf/link/syscalls.go +++ b/vendor/github.com/cilium/ebpf/link/syscalls.go @@ -88,10 +88,11 @@ var haveProgAttachReplace = internal.FeatureTest("BPF_PROG_ATTACH atomic replace }) type bpfLinkCreateAttr struct { - progFd uint32 - targetFd uint32 - attachType ebpf.AttachType - flags uint32 + progFd uint32 + targetFd uint32 + attachType ebpf.AttachType + flags uint32 + targetBTFID uint32 } func bpfLinkCreate(attr *bpfLinkCreateAttr) (*internal.FD, error) { diff --git a/vendor/github.com/cilium/ebpf/link/tracepoint.go b/vendor/github.com/cilium/ebpf/link/tracepoint.go index b8ae04bf0..7423df86b 100644 --- a/vendor/github.com/cilium/ebpf/link/tracepoint.go +++ b/vendor/github.com/cilium/ebpf/link/tracepoint.go @@ -11,7 +11,11 @@ import ( // tracepoints. The top-level directory is the group, the event's subdirectory // is the name. Example: // -// Tracepoint("syscalls", "sys_enter_fork", prog) +// tp, err := Tracepoint("syscalls", "sys_enter_fork", prog) +// +// Losing the reference to the resulting Link (tp) will close the Tracepoint +// and prevent further execution of prog. The Link must be Closed during +// program shutdown to avoid leaking system resources. // // Note that attaching eBPF programs to syscalls (sys_enter_*/sys_exit_*) is // only possible as of kernel 4.14 (commit cf5f5ce). @@ -34,7 +38,7 @@ func Tracepoint(group, name string, prog *ebpf.Program) (Link, error) { return nil, err } - fd, err := openTracepointPerfEvent(tid) + fd, err := openTracepointPerfEvent(tid, perfAllThreads) if err != nil { return nil, err } diff --git a/vendor/github.com/cilium/ebpf/link/uprobe.go b/vendor/github.com/cilium/ebpf/link/uprobe.go index 2bc395ee3..59170ce04 100644 --- a/vendor/github.com/cilium/ebpf/link/uprobe.go +++ b/vendor/github.com/cilium/ebpf/link/uprobe.go @@ -25,14 +25,18 @@ var ( value uint64 err error }{} + + // ErrNoSymbol indicates that the given symbol was not found + // in the ELF symbols table. + ErrNoSymbol = errors.New("not found") ) // Executable defines an executable program on the filesystem. type Executable struct { // Path of the executable on the filesystem. path string - // Parsed ELF symbols and dynamic symbols. - symbols map[string]elf.Symbol + // Parsed ELF symbols and dynamic symbols offsets. + offsets map[string]uint64 } // UprobeOptions defines additional parameters that will be used @@ -41,6 +45,9 @@ type UprobeOptions struct { // Symbol offset. Must be provided in case of external symbols (shared libs). // If set, overrides the offset eventually parsed from the executable. Offset uint64 + // Only set the uprobe on the given process ID. Useful when tracing + // shared library calls or programs that have many running instances. + PID int } // To open a new Executable, use: @@ -64,42 +71,84 @@ func OpenExecutable(path string) (*Executable, error) { return nil, fmt.Errorf("parse ELF file: %w", err) } - var ex = Executable{ - path: path, - symbols: make(map[string]elf.Symbol), + if se.Type != elf.ET_EXEC && se.Type != elf.ET_DYN { + // ELF is not an executable or a shared object. + return nil, errors.New("the given file is not an executable or a shared object") } - if err := ex.addSymbols(se.Symbols); err != nil { - return nil, err + + ex := Executable{ + path: path, + offsets: make(map[string]uint64), } - if err := ex.addSymbols(se.DynamicSymbols); err != nil { + if err := ex.load(se); err != nil { return nil, err } return &ex, nil } -func (ex *Executable) addSymbols(f func() ([]elf.Symbol, error)) error { - // elf.Symbols and elf.DynamicSymbols return ErrNoSymbols if the section is not found. - syms, err := f() +func (ex *Executable) load(f *internal.SafeELFFile) error { + syms, err := f.Symbols() if err != nil && !errors.Is(err, elf.ErrNoSymbols) { return err } + + dynsyms, err := f.DynamicSymbols() + if err != nil && !errors.Is(err, elf.ErrNoSymbols) { + return err + } + + syms = append(syms, dynsyms...) + for _, s := range syms { if elf.ST_TYPE(s.Info) != elf.STT_FUNC { // Symbol not associated with a function or other executable code. continue } - ex.symbols[s.Name] = s + + off := s.Value + + // Loop over ELF segments. + for _, prog := range f.Progs { + // Skip uninteresting segments. + if prog.Type != elf.PT_LOAD || (prog.Flags&elf.PF_X) == 0 { + continue + } + + if prog.Vaddr <= s.Value && s.Value < (prog.Vaddr+prog.Memsz) { + // If the symbol value is contained in the segment, calculate + // the symbol offset. + // + // fn symbol offset = fn symbol VA - .text VA + .text offset + // + // stackoverflow.com/a/40249502 + off = s.Value - prog.Vaddr + prog.Off + break + } + } + + ex.offsets[s.Name] = off } + return nil } -func (ex *Executable) symbol(symbol string) (*elf.Symbol, error) { - if s, ok := ex.symbols[symbol]; ok { - return &s, nil +func (ex *Executable) offset(symbol string) (uint64, error) { + if off, ok := ex.offsets[symbol]; ok { + // Symbols with location 0 from section undef are shared library calls and + // are relocated before the binary is executed. Dynamic linking is not + // implemented by the library, so mark this as unsupported for now. + // + // Since only offset values are stored and not elf.Symbol, if the value is 0, + // assume it's an external symbol. + if off == 0 { + return 0, fmt.Errorf("cannot resolve %s library call '%s', "+ + "consider providing the offset via options: %w", ex.path, symbol, ErrNotSupported) + } + return off, nil } - return nil, fmt.Errorf("symbol %s not found", symbol) + return 0, fmt.Errorf("symbol %s: %w", symbol, ErrNoSymbol) } // Uprobe attaches the given eBPF program to a perf event that fires when the @@ -112,11 +161,14 @@ func (ex *Executable) symbol(symbol string) (*elf.Symbol, error) { // When using symbols which belongs to shared libraries, // an offset must be provided via options: // -// ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123}) +// up, err := ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123}) +// +// Losing the reference to the resulting Link (up) will close the Uprobe +// and prevent further execution of prog. The Link must be Closed during +// program shutdown to avoid leaking system resources. // -// The resulting Link must be Closed during program shutdown to avoid leaking -// system resources. Functions provided by shared libraries can currently not -// be traced and will result in an ErrNotSupported. +// Functions provided by shared libraries can currently not be traced and +// will result in an ErrNotSupported. func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) { u, err := ex.uprobe(symbol, prog, opts, false) if err != nil { @@ -141,11 +193,14 @@ func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti // When using symbols which belongs to shared libraries, // an offset must be provided via options: // -// ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123}) +// up, err := ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123}) +// +// Losing the reference to the resulting Link (up) will close the Uprobe +// and prevent further execution of prog. The Link must be Closed during +// program shutdown to avoid leaking system resources. // -// The resulting Link must be Closed during program shutdown to avoid leaking -// system resources. Functions provided by shared libraries can currently not -// be traced and will result in an ErrNotSupported. +// Functions provided by shared libraries can currently not be traced and +// will result in an ErrNotSupported. func (ex *Executable) Uretprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) { u, err := ex.uprobe(symbol, prog, opts, true) if err != nil { @@ -175,24 +230,20 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti if opts != nil && opts.Offset != 0 { offset = opts.Offset } else { - sym, err := ex.symbol(symbol) + off, err := ex.offset(symbol) if err != nil { - return nil, fmt.Errorf("symbol '%s' not found: %w", symbol, err) - } - - // Symbols with location 0 from section undef are shared library calls and - // are relocated before the binary is executed. Dynamic linking is not - // implemented by the library, so mark this as unsupported for now. - if sym.Section == elf.SHN_UNDEF && sym.Value == 0 { - return nil, fmt.Errorf("cannot resolve %s library call '%s', "+ - "consider providing the offset via options: %w", ex.path, symbol, ErrNotSupported) + return nil, err } + offset = off + } - offset = sym.Value + pid := perfAllThreads + if opts != nil && opts.PID != 0 { + pid = opts.PID } // Use uprobe PMU if the kernel has it available. - tp, err := pmuUprobe(symbol, ex.path, offset, ret) + tp, err := pmuUprobe(symbol, ex.path, offset, pid, ret) if err == nil { return tp, nil } @@ -201,7 +252,7 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti } // Use tracefs if uprobe PMU is missing. - tp, err = tracefsUprobe(uprobeSanitizedSymbol(symbol), ex.path, offset, ret) + tp, err = tracefsUprobe(uprobeSanitizedSymbol(symbol), ex.path, offset, pid, ret) if err != nil { return nil, fmt.Errorf("creating trace event '%s:%s' in tracefs: %w", ex.path, symbol, err) } @@ -210,13 +261,13 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti } // pmuUprobe opens a perf event based on the uprobe PMU. -func pmuUprobe(symbol, path string, offset uint64, ret bool) (*perfEvent, error) { - return pmuProbe(uprobeType, symbol, path, offset, ret) +func pmuUprobe(symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) { + return pmuProbe(uprobeType, symbol, path, offset, pid, ret) } // tracefsUprobe creates a Uprobe tracefs entry. -func tracefsUprobe(symbol, path string, offset uint64, ret bool) (*perfEvent, error) { - return tracefsProbe(uprobeType, symbol, path, offset, ret) +func tracefsUprobe(symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) { + return tracefsProbe(uprobeType, symbol, path, offset, pid, ret) } // uprobeSanitizedSymbol replaces every invalid characted for the tracefs api with an underscore. diff --git a/vendor/github.com/cilium/ebpf/linker.go b/vendor/github.com/cilium/ebpf/linker.go index 6c2efef9e..f3b1629e7 100644 --- a/vendor/github.com/cilium/ebpf/linker.go +++ b/vendor/github.com/cilium/ebpf/linker.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/cilium/ebpf/asm" - "github.com/cilium/ebpf/internal/btf" ) // link resolves bpf-to-bpf calls. @@ -40,7 +39,7 @@ func link(prog *ProgramSpec, libs []*ProgramSpec) error { pending = append(pending, lib.Instructions) if prog.BTF != nil && lib.BTF != nil { - if err := btf.ProgramAppend(prog.BTF, lib.BTF); err != nil { + if err := prog.BTF.Append(lib.BTF); err != nil { return fmt.Errorf("linking BTF of %s: %w", lib.Name, err) } } @@ -136,5 +135,25 @@ func fixupJumpsAndCalls(insns asm.Instructions) error { } } + // fixupBPFCalls replaces bpf_probe_read_{kernel,user}[_str] with bpf_probe_read[_str] on older kernels + // https://github.com/libbpf/libbpf/blob/master/src/libbpf.c#L6009 + iter = insns.Iterate() + for iter.Next() { + ins := iter.Ins + if !ins.IsBuiltinCall() { + continue + } + switch asm.BuiltinFunc(ins.Constant) { + case asm.FnProbeReadKernel, asm.FnProbeReadUser: + if err := haveProbeReadKernel(); err != nil { + ins.Constant = int64(asm.FnProbeRead) + } + case asm.FnProbeReadKernelStr, asm.FnProbeReadUserStr: + if err := haveProbeReadKernel(); err != nil { + ins.Constant = int64(asm.FnProbeReadStr) + } + } + } + return nil } diff --git a/vendor/github.com/cilium/ebpf/map.go b/vendor/github.com/cilium/ebpf/map.go index f257d88c0..cca387ead 100644 --- a/vendor/github.com/cilium/ebpf/map.go +++ b/vendor/github.com/cilium/ebpf/map.go @@ -1,6 +1,7 @@ package ebpf import ( + "bytes" "errors" "fmt" "io" @@ -65,6 +66,11 @@ type MapSpec struct { // InnerMap is used as a template for ArrayOfMaps and HashOfMaps InnerMap *MapSpec + // Extra trailing bytes found in the ELF map definition when using structs + // larger than libbpf's bpf_map_def. Must be empty before instantiating + // the MapSpec into a Map. + Extra bytes.Reader + // The BTF associated with this map. BTF *btf.Map } @@ -82,9 +88,12 @@ func (ms *MapSpec) Copy() *MapSpec { } cpy := *ms + cpy.Contents = make([]MapKV, len(ms.Contents)) copy(cpy.Contents, ms.Contents) + cpy.InnerMap = ms.InnerMap.Copy() + return &cpy } @@ -188,14 +197,24 @@ func NewMap(spec *MapSpec) (*Map, error) { // // The caller is responsible for ensuring the process' rlimit is set // sufficiently high for locking memory during map creation. This can be done -// by calling unix.Setrlimit with unix.RLIMIT_MEMLOCK prior to calling NewMapWithOptions. +// by calling rlimit.RemoveMemlock() prior to calling NewMapWithOptions. // // May return an error wrapping ErrMapIncompatible. func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) { handles := newHandleCache() defer handles.close() - return newMapWithOptions(spec, opts, handles) + m, err := newMapWithOptions(spec, opts, handles) + if err != nil { + return nil, fmt.Errorf("creating map: %w", err) + } + + err = m.finalize(spec) + if err != nil { + return nil, fmt.Errorf("populating map: %w", err) + } + + return m, nil } func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_ *Map, err error) { @@ -207,8 +226,12 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_ switch spec.Pinning { case PinByName: - if spec.Name == "" || opts.PinPath == "" { - return nil, fmt.Errorf("pin by name: missing Name or PinPath") + if spec.Name == "" { + return nil, fmt.Errorf("pin by name: missing Name") + } + + if opts.PinPath == "" { + return nil, fmt.Errorf("pin by name: missing MapOptions.PinPath") } path := filepath.Join(opts.PinPath, spec.Name) @@ -244,16 +267,19 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_ return nil, errors.New("inner maps cannot be pinned") } - template, err := createMap(spec.InnerMap, nil, opts, handles) + template, err := spec.InnerMap.createMap(nil, opts, handles) if err != nil { - return nil, err + return nil, fmt.Errorf("inner map: %w", err) } defer template.Close() + // Intentionally skip populating and freezing (finalizing) + // the inner map template since it will be removed shortly. + innerFd = template.fd } - m, err := createMap(spec, innerFd, opts, handles) + m, err := spec.createMap(innerFd, opts, handles) if err != nil { return nil, err } @@ -269,7 +295,9 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_ return m, nil } -func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *handleCache) (_ *Map, err error) { +// createMap validates the spec's properties and creates the map in the kernel +// using the given opts. It does not populate or freeze the map. +func (spec *MapSpec) createMap(inner *internal.FD, opts MapOptions, handles *handleCache) (_ *Map, err error) { closeOnError := func(closer io.Closer) { if err != nil { closer.Close() @@ -278,10 +306,16 @@ func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *hand spec = spec.Copy() + // Kernels 4.13 through 5.4 used a struct bpf_map_def that contained + // additional 'inner_map_idx' and later 'numa_node' fields. + // In order to support loading these definitions, tolerate the presence of + // extra bytes, but require them to be zeroes. + if _, err := io.Copy(internal.DiscardZeroes{}, &spec.Extra); err != nil { + return nil, errors.New("extra contains unhandled non-zero bytes, drain before creating map") + } + switch spec.Type { - case ArrayOfMaps: - fallthrough - case HashOfMaps: + case ArrayOfMaps, HashOfMaps: if err := haveNestedMaps(); err != nil { return nil, err } @@ -350,7 +384,7 @@ func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *hand var btfDisabled bool if spec.BTF != nil { - handle, err := handles.btfHandle(btf.MapSpec(spec.BTF)) + handle, err := handles.btfHandle(spec.BTF.Spec) btfDisabled = errors.Is(err, btf.ErrNotSupported) if err != nil && !btfDisabled { return nil, fmt.Errorf("load BTF: %w", err) @@ -358,15 +392,15 @@ func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *hand if handle != nil { attr.BTFFd = uint32(handle.FD()) - attr.BTFKeyTypeID = uint32(btf.MapKey(spec.BTF).ID()) - attr.BTFValueTypeID = uint32(btf.MapValue(spec.BTF).ID()) + attr.BTFKeyTypeID = uint32(spec.BTF.Key.ID()) + attr.BTFValueTypeID = uint32(spec.BTF.Value.ID()) } } fd, err := internal.BPFMapCreate(&attr) if err != nil { if errors.Is(err, unix.EPERM) { - return nil, fmt.Errorf("map create: RLIMIT_MEMLOCK may be too low: %w", err) + return nil, fmt.Errorf("map create: %w (MEMLOCK bay be too low, consider rlimit.RemoveMemlock)", err) } if btfDisabled { return nil, fmt.Errorf("map create without BTF: %w", err) @@ -380,19 +414,11 @@ func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *hand return nil, fmt.Errorf("map create: %w", err) } - if err := m.populate(spec.Contents); err != nil { - return nil, fmt.Errorf("map create: can't set initial contents: %w", err) - } - - if spec.Freeze { - if err := m.Freeze(); err != nil { - return nil, fmt.Errorf("can't freeze map: %w", err) - } - } - return m, nil } +// newMap allocates and returns a new Map structure. +// Sets the fullValueSize on per-CPU maps. func newMap(fd *internal.FD, name string, typ MapType, keySize, valueSize, maxEntries, flags uint32) (*Map, error) { m := &Map{ name, @@ -415,7 +441,7 @@ func newMap(fd *internal.FD, name string, typ MapType, keySize, valueSize, maxEn return nil, err } - m.fullValueSize = align(int(valueSize), 8) * possibleCPUs + m.fullValueSize = internal.Align(int(valueSize), 8) * possibleCPUs return m, nil } @@ -892,12 +918,21 @@ func (m *Map) Freeze() error { return nil } -func (m *Map) populate(contents []MapKV) error { - for _, kv := range contents { +// finalize populates the Map according to the Contents specified +// in spec and freezes the Map if requested by spec. +func (m *Map) finalize(spec *MapSpec) error { + for _, kv := range spec.Contents { if err := m.Put(kv.Key, kv.Value); err != nil { - return fmt.Errorf("key %v: %w", kv.Key, err) + return fmt.Errorf("putting value: key %v: %w", kv.Key, err) } } + + if spec.Freeze { + if err := m.Freeze(); err != nil { + return fmt.Errorf("freezing map: %w", err) + } + } + return nil } @@ -1212,7 +1247,7 @@ func MapGetNextID(startID MapID) (MapID, error) { // // Returns ErrNotExist, if there is no eBPF map with the given id. func NewMapFromID(id MapID) (*Map, error) { - fd, err := bpfObjGetFDByID(internal.BPF_MAP_GET_FD_BY_ID, uint32(id)) + fd, err := internal.BPFObjGetFDByID(internal.BPF_MAP_GET_FD_BY_ID, uint32(id)) if err != nil { return nil, err } diff --git a/vendor/github.com/cilium/ebpf/marshalers.go b/vendor/github.com/cilium/ebpf/marshalers.go index f2610eff9..e461d673d 100644 --- a/vendor/github.com/cilium/ebpf/marshalers.go +++ b/vendor/github.com/cilium/ebpf/marshalers.go @@ -8,6 +8,7 @@ import ( "fmt" "reflect" "runtime" + "sync" "unsafe" "github.com/cilium/ebpf/internal" @@ -39,6 +40,10 @@ func marshalPtr(data interface{}, length int) (internal.Pointer, error) { // Returns an error if the given value isn't representable in exactly // length bytes. func marshalBytes(data interface{}, length int) (buf []byte, err error) { + if data == nil { + return nil, errors.New("can't marshal a nil value") + } + switch value := data.(type) { case encoding.BinaryMarshaler: buf, err = value.MarshalBinary() @@ -77,22 +82,30 @@ func makeBuffer(dst interface{}, length int) (internal.Pointer, []byte) { return internal.NewSlicePointer(buf), buf } +var bytesReaderPool = sync.Pool{ + New: func() interface{} { + return new(bytes.Reader) + }, +} + // unmarshalBytes converts a byte buffer into an arbitrary value. // // Prefer using Map.unmarshalKey and Map.unmarshalValue if possible, since // those have special cases that allow more types to be encoded. +// +// The common int32 and int64 types are directly handled to avoid +// unnecessary heap allocations as happening in the default case. func unmarshalBytes(data interface{}, buf []byte) error { switch value := data.(type) { case unsafe.Pointer: - // This could be solved in Go 1.17 by unsafe.Slice instead. (https://github.com/golang/go/issues/19367) - // We could opt for removing unsafe.Pointer support in the lib as well. - sh := &reflect.SliceHeader{ //nolint:govet - Data: uintptr(value), - Len: len(buf), - Cap: len(buf), - } + var dst []byte + // Use unsafe.Slice when we drop support for pre1.17 (https://github.com/golang/go/issues/19367) + // We could opt for removing unsafe.Pointer support in the lib as well + sh := (*reflect.SliceHeader)(unsafe.Pointer(&dst)) + sh.Data = uintptr(value) + sh.Len = len(buf) + sh.Cap = len(buf) - dst := *(*[]byte)(unsafe.Pointer(sh)) copy(dst, buf) runtime.KeepAlive(value) return nil @@ -106,12 +119,38 @@ func unmarshalBytes(data interface{}, buf []byte) error { case *[]byte: *value = buf return nil + case *int32: + if len(buf) < 4 { + return errors.New("int32 requires 4 bytes") + } + *value = int32(internal.NativeEndian.Uint32(buf)) + return nil + case *uint32: + if len(buf) < 4 { + return errors.New("uint32 requires 4 bytes") + } + *value = internal.NativeEndian.Uint32(buf) + return nil + case *int64: + if len(buf) < 8 { + return errors.New("int64 requires 8 bytes") + } + *value = int64(internal.NativeEndian.Uint64(buf)) + return nil + case *uint64: + if len(buf) < 8 { + return errors.New("uint64 requires 8 bytes") + } + *value = internal.NativeEndian.Uint64(buf) + return nil case string: return errors.New("require pointer to string") case []byte: return errors.New("require pointer to []byte") default: - rd := bytes.NewReader(buf) + rd := bytesReaderPool.Get().(*bytes.Reader) + rd.Reset(buf) + defer bytesReaderPool.Put(rd) if err := binary.Read(rd, internal.NativeEndian, value); err != nil { return fmt.Errorf("decoding %T: %v", value, err) } @@ -142,7 +181,7 @@ func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, er return internal.Pointer{}, fmt.Errorf("per-CPU value exceeds number of CPUs") } - alignedElemLength := align(elemLength, 8) + alignedElemLength := internal.Align(elemLength, 8) buf := make([]byte, alignedElemLength*possibleCPUs) for i := 0; i < sliceLen; i++ { @@ -212,7 +251,3 @@ func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) erro reflect.ValueOf(slicePtr).Elem().Set(slice) return nil } - -func align(n, alignment int) int { - return (int(n) + alignment - 1) / alignment * alignment -} diff --git a/vendor/github.com/cilium/ebpf/prog.go b/vendor/github.com/cilium/ebpf/prog.go index 13bdb6dda..3549a3fe3 100644 --- a/vendor/github.com/cilium/ebpf/prog.go +++ b/vendor/github.com/cilium/ebpf/prog.go @@ -57,16 +57,21 @@ type ProgramSpec struct { // Name is passed to the kernel as a debug aid. Must only contain // alpha numeric and '_' characters. Name string + // Type determines at which hook in the kernel a program will run. Type ProgramType AttachType AttachType - // Name of a kernel data structure to attach to. It's interpretation - // depends on Type and AttachType. - AttachTo string + // Name of a kernel data structure or function to attach to. Its + // interpretation depends on Type and AttachType. + AttachTo string + // The program to attach to. Must be provided manually. + AttachTarget *Program Instructions asm.Instructions + // Flags is passed to the kernel and specifies additional program // load attributes. Flags uint32 + // License of the program. Some helpers are only available if // the license is deemed compatible with the GPL. // @@ -146,7 +151,7 @@ func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *handleCache) (*Program, error) { if len(spec.Instructions) == 0 { - return nil, errors.New("Instructions cannot be empty") + return nil, errors.New("instructions cannot be empty") } if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian { @@ -166,16 +171,16 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand kv = v.Kernel() } - attr := &bpfProgLoadAttr{ - progType: spec.Type, - progFlags: spec.Flags, - expectedAttachType: spec.AttachType, - license: internal.NewStringPointer(spec.License), - kernelVersion: kv, + attr := &internal.BPFProgLoadAttr{ + ProgType: uint32(spec.Type), + ProgFlags: spec.Flags, + ExpectedAttachType: uint32(spec.AttachType), + License: internal.NewStringPointer(spec.License), + KernelVersion: kv, } if haveObjName() == nil { - attr.progName = internal.NewBPFObjName(spec.Name) + attr.ProgName = internal.NewBPFObjName(spec.Name) } var err error @@ -190,35 +195,35 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand var btfDisabled bool var core btf.COREFixups if spec.BTF != nil { - core, err = btf.ProgramFixups(spec.BTF, targetBTF) + core, err = spec.BTF.Fixups(targetBTF) if err != nil { return nil, fmt.Errorf("CO-RE relocations: %w", err) } - handle, err := handles.btfHandle(btf.ProgramSpec(spec.BTF)) + handle, err := handles.btfHandle(spec.BTF.Spec()) btfDisabled = errors.Is(err, btf.ErrNotSupported) if err != nil && !btfDisabled { return nil, fmt.Errorf("load BTF: %w", err) } if handle != nil { - attr.progBTFFd = uint32(handle.FD()) + attr.ProgBTFFd = uint32(handle.FD()) - recSize, bytes, err := btf.ProgramLineInfos(spec.BTF) + recSize, bytes, err := spec.BTF.LineInfos() if err != nil { return nil, fmt.Errorf("get BTF line infos: %w", err) } - attr.lineInfoRecSize = recSize - attr.lineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) - attr.lineInfo = internal.NewSlicePointer(bytes) + attr.LineInfoRecSize = recSize + attr.LineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) + attr.LineInfo = internal.NewSlicePointer(bytes) - recSize, bytes, err = btf.ProgramFuncInfos(spec.BTF) + recSize, bytes, err = spec.BTF.FuncInfos() if err != nil { return nil, fmt.Errorf("get BTF function infos: %w", err) } - attr.funcInfoRecSize = recSize - attr.funcInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) - attr.funcInfo = internal.NewSlicePointer(bytes) + attr.FuncInfoRecSize = recSize + attr.FuncInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) + attr.FuncInfo = internal.NewSlicePointer(bytes) } } @@ -238,16 +243,41 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand } bytecode := buf.Bytes() - attr.instructions = internal.NewSlicePointer(bytecode) - attr.insCount = uint32(len(bytecode) / asm.InstructionSize) + attr.Instructions = internal.NewSlicePointer(bytecode) + attr.InsCount = uint32(len(bytecode) / asm.InstructionSize) if spec.AttachTo != "" { + if spec.AttachTarget != nil { + info, err := spec.AttachTarget.Info() + if err != nil { + return nil, fmt.Errorf("load target BTF: %w", err) + } + + btfID, ok := info.BTFID() + if !ok { + return nil, fmt.Errorf("load target BTF: no BTF info available") + } + btfHandle, err := btf.NewHandleFromID(btfID) + if err != nil { + return nil, fmt.Errorf("load target BTF: %w", err) + } + defer btfHandle.Close() + + targetBTF = btfHandle.Spec() + if err != nil { + return nil, fmt.Errorf("load target BTF: %w", err) + } + } + target, err := resolveBTFType(targetBTF, spec.AttachTo, spec.Type, spec.AttachType) if err != nil { return nil, err } if target != nil { - attr.attachBTFID = target.ID() + attr.AttachBTFID = uint32(target.ID()) + } + if spec.AttachTarget != nil { + attr.AttachProgFd = uint32(spec.AttachTarget.FD()) } } @@ -259,12 +289,12 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand var logBuf []byte if opts.LogLevel > 0 { logBuf = make([]byte, logSize) - attr.logLevel = opts.LogLevel - attr.logSize = uint32(len(logBuf)) - attr.logBuf = internal.NewSlicePointer(logBuf) + attr.LogLevel = opts.LogLevel + attr.LogSize = uint32(len(logBuf)) + attr.LogBuf = internal.NewSlicePointer(logBuf) } - fd, err := bpfProgLoad(attr) + fd, err := internal.BPFProgLoad(attr) if err == nil { return &Program{internal.CString(logBuf), fd, spec.Name, "", spec.Type}, nil } @@ -273,17 +303,20 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand if opts.LogLevel == 0 && opts.LogSize >= 0 { // Re-run with the verifier enabled to get better error messages. logBuf = make([]byte, logSize) - attr.logLevel = 1 - attr.logSize = uint32(len(logBuf)) - attr.logBuf = internal.NewSlicePointer(logBuf) + attr.LogLevel = 1 + attr.LogSize = uint32(len(logBuf)) + attr.LogBuf = internal.NewSlicePointer(logBuf) - _, logErr = bpfProgLoad(attr) + fd, logErr = internal.BPFProgLoad(attr) + if logErr == nil { + fd.Close() + } } if errors.Is(logErr, unix.EPERM) && logBuf[0] == 0 { // EPERM due to RLIMIT_MEMLOCK happens before the verifier, so we can // check that the log is empty to reduce false positives. - return nil, fmt.Errorf("load program: RLIMIT_MEMLOCK may be too low: %w", logErr) + return nil, fmt.Errorf("load program: %w (MEMLOCK bay be too low, consider rlimit.RemoveMemlock)", logErr) } err = internal.ErrorWithLog(err, logBuf, logErr) @@ -310,7 +343,7 @@ func NewProgramFromFD(fd int) (*Program, error) { // // Returns ErrNotExist, if there is no eBPF program with the given id. func NewProgramFromID(id ProgramID) (*Program, error) { - fd, err := bpfObjGetFDByID(internal.BPF_PROG_GET_FD_BY_ID, uint32(id)) + fd, err := internal.BPFObjGetFDByID(internal.BPF_PROG_GET_FD_BY_ID, uint32(id)) if err != nil { return nil, fmt.Errorf("get program by id: %w", err) } @@ -677,45 +710,44 @@ func ProgramGetNextID(startID ProgramID) (ProgramID, error) { // // Deprecated: use ProgramInfo.ID() instead. func (p *Program) ID() (ProgramID, error) { - info, err := bpfGetProgInfoByFD(p.fd) + info, err := bpfGetProgInfoByFD(p.fd, nil) if err != nil { return ProgramID(0), err } return ProgramID(info.id), nil } -func resolveBTFType(kernel *btf.Spec, name string, progType ProgramType, attachType AttachType) (btf.Type, error) { +func resolveBTFType(spec *btf.Spec, name string, progType ProgramType, attachType AttachType) (btf.Type, error) { type match struct { p ProgramType a AttachType } - var target btf.Type var typeName, featureName string switch (match{progType, attachType}) { case match{LSM, AttachLSMMac}: - target = new(btf.Func) typeName = "bpf_lsm_" + name featureName = name + " LSM hook" - case match{Tracing, AttachTraceIter}: - target = new(btf.Func) typeName = "bpf_iter_" + name featureName = name + " iterator" - + case match{Extension, AttachNone}: + typeName = name + featureName = fmt.Sprintf("freplace %s", name) default: return nil, nil } - if kernel == nil { + if spec == nil { var err error - kernel, err = btf.LoadKernelSpec() + spec, err = btf.LoadKernelSpec() if err != nil { return nil, fmt.Errorf("load kernel spec: %w", err) } } - err := kernel.FindType(typeName, target) + var target *btf.Func + err := spec.FindType(typeName, &target) if errors.Is(err, btf.ErrNotFound) { return nil, &internal.UnsupportedFeatureError{ Name: featureName, @@ -724,5 +756,6 @@ func resolveBTFType(kernel *btf.Spec, name string, progType ProgramType, attachT if err != nil { return nil, fmt.Errorf("resolve BTF for %s: %w", featureName, err) } + return target, nil } diff --git a/vendor/github.com/cilium/ebpf/run-tests.sh b/vendor/github.com/cilium/ebpf/run-tests.sh index e2437beed..a079edc7e 100644 --- a/vendor/github.com/cilium/ebpf/run-tests.sh +++ b/vendor/github.com/cilium/ebpf/run-tests.sh @@ -5,7 +5,7 @@ # Run all tests on a 5.4 kernel # $ ./run-tests.sh 5.4 # Run a subset of tests: -# $ ./run-tests.sh 5.4 go test ./link +# $ ./run-tests.sh 5.4 ./link set -euo pipefail @@ -48,15 +48,17 @@ if [[ "${1:-}" = "--exec-vm" ]]; then rm "${output}/fake-stdin" fi - $sudo virtme-run --kimg "${input}/bzImage" --memory 768M --pwd \ - --rwdir="${testdir}=${testdir}" \ - --rodir=/run/input="${input}" \ - --rwdir=/run/output="${output}" \ - --script-sh "PATH=\"$PATH\" \"$script\" --exec-test $cmd" \ - --qemu-opts -smp 2 # need at least two CPUs for some tests + if ! $sudo virtme-run --kimg "${input}/bzImage" --memory 768M --pwd \ + --rwdir="${testdir}=${testdir}" \ + --rodir=/run/input="${input}" \ + --rwdir=/run/output="${output}" \ + --script-sh "PATH=\"$PATH\" \"$script\" --exec-test $cmd" \ + --kopt possible_cpus=2; then # need at least two CPUs for some tests + exit 23 + fi if [[ ! -e "${output}/success" ]]; then - exit 1 + exit 42 fi $sudo rm -r "$output" @@ -74,7 +76,7 @@ elif [[ "${1:-}" = "--exec-test" ]]; then dmesg -C if ! "$@"; then dmesg - exit 1 + exit 1 # this return code is "swallowed" by qemu fi touch "/run/output/success" exit 0 @@ -108,7 +110,7 @@ else echo "No selftests found, disabling" fi -args=(-v -short -coverpkg=./... -coverprofile=coverage.out -count 1 ./...) +args=(-short -coverpkg=./... -coverprofile=coverage.out -count 1 ./...) if (( $# > 0 )); then args=("$@") fi diff --git a/vendor/github.com/cilium/ebpf/syscalls.go b/vendor/github.com/cilium/ebpf/syscalls.go index f5a38549b..f8cb5f0e0 100644 --- a/vendor/github.com/cilium/ebpf/syscalls.go +++ b/vendor/github.com/cilium/ebpf/syscalls.go @@ -1,13 +1,14 @@ package ebpf import ( + "bytes" "errors" "fmt" "os" "unsafe" + "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" - "github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/unix" ) @@ -73,30 +74,6 @@ type bpfMapInfo struct { btf_value_type_id uint32 } -type bpfProgLoadAttr struct { - progType ProgramType - insCount uint32 - instructions internal.Pointer - license internal.Pointer - logLevel uint32 - logSize uint32 - logBuf internal.Pointer - kernelVersion uint32 // since 4.1 2541517c32be - progFlags uint32 // since 4.11 e07b98d9bffe - progName internal.BPFObjName // since 4.15 067cae47771c - progIfIndex uint32 // since 4.15 1f6f4cb7ba21 - expectedAttachType AttachType // since 4.17 5e43f899b03a - progBTFFd uint32 - funcInfoRecSize uint32 - funcInfo internal.Pointer - funcInfoCnt uint32 - lineInfoRecSize uint32 - lineInfo internal.Pointer - lineInfoCnt uint32 - attachBTFID btf.TypeID - attachProgFd uint32 -} - type bpfProgInfo struct { prog_type uint32 id uint32 @@ -107,7 +84,7 @@ type bpfProgInfo struct { xlated_prog_insns internal.Pointer load_time uint64 // since 4.15 cb4d2b3f03d8 created_by_uid uint32 - nr_map_ids uint32 + nr_map_ids uint32 // since 4.15 cb4d2b3f03d8 map_ids internal.Pointer name internal.BPFObjName // since 4.15 067cae47771c ifindex uint32 @@ -145,11 +122,6 @@ type bpfProgTestRunAttr struct { duration uint32 } -type bpfGetFDByIDAttr struct { - id uint32 - next uint32 -} - type bpfMapFreezeAttr struct { mapFd uint32 } @@ -160,23 +132,6 @@ type bpfObjGetNextIDAttr struct { openFlags uint32 } -func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) { - for { - fd, err := internal.BPF(internal.BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) - // As of ~4.20 the verifier can be interrupted by a signal, - // and returns EAGAIN in that case. - if errors.Is(err, unix.EAGAIN) { - continue - } - - if err != nil { - return nil, err - } - - return internal.NewFD(uint32(fd)), nil - } -} - func bpfProgTestRun(attr *bpfProgTestRunAttr) error { _, err := internal.BPF(internal.BPF_PROG_TEST_RUN, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err @@ -372,6 +327,10 @@ func wrapMapError(err error) error { return internal.SyscallError(ErrNotSupported, unix.ENOTSUPP) } + if errors.Is(err, unix.E2BIG) { + return fmt.Errorf("key too big for map: %w", err) + } + return err } @@ -388,8 +347,13 @@ func bpfMapFreeze(m *internal.FD) error { return err } -func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) { +func bpfGetProgInfoByFD(fd *internal.FD, ids []MapID) (*bpfProgInfo, error) { var info bpfProgInfo + if len(ids) > 0 { + info.nr_map_ids = uint32(len(ids)) + info.map_ids = internal.NewPointer(unsafe.Pointer(&ids[0])) + } + if err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil { return nil, fmt.Errorf("can't get program info: %w", err) } @@ -471,10 +435,30 @@ var haveBatchAPI = internal.FeatureTest("map batch api", "5.6", func() error { return nil }) -func bpfObjGetFDByID(cmd internal.BPFCmd, id uint32) (*internal.FD, error) { - attr := bpfGetFDByIDAttr{ - id: id, +var haveProbeReadKernel = internal.FeatureTest("bpf_probe_read_kernel", "5.5", func() error { + insns := asm.Instructions{ + asm.Mov.Reg(asm.R1, asm.R10), + asm.Add.Imm(asm.R1, -8), + asm.Mov.Imm(asm.R2, 8), + asm.Mov.Imm(asm.R3, 0), + asm.FnProbeReadKernel.Call(), + asm.Return(), + } + buf := bytes.NewBuffer(make([]byte, 0, len(insns)*asm.InstructionSize)) + if err := insns.Marshal(buf, internal.NativeEndian); err != nil { + return err } - ptr, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) - return internal.NewFD(uint32(ptr)), err -} + bytecode := buf.Bytes() + + fd, err := internal.BPFProgLoad(&internal.BPFProgLoadAttr{ + ProgType: uint32(Kprobe), + License: internal.NewStringPointer("GPL"), + Instructions: internal.NewSlicePointer(bytecode), + InsCount: uint32(len(bytecode) / asm.InstructionSize), + }) + if err != nil { + return internal.ErrNotSupported + } + _ = fd.Close() + return nil +}) diff --git a/vendor/github.com/cilium/ebpf/types.go b/vendor/github.com/cilium/ebpf/types.go index 441a82fe4..84b83f9f9 100644 --- a/vendor/github.com/cilium/ebpf/types.go +++ b/vendor/github.com/cilium/ebpf/types.go @@ -4,12 +4,17 @@ import ( "github.com/cilium/ebpf/internal/unix" ) -//go:generate stringer -output types_string.go -type=MapType,ProgramType,AttachType,PinType +//go:generate stringer -output types_string.go -type=MapType,ProgramType,PinType // MapType indicates the type map structure // that will be initialized in the kernel. type MapType uint32 +// Max returns the latest supported MapType. +func (_ MapType) Max() MapType { + return maxMapType - 1 +} + // All the various map types that can be created const ( UnspecifiedMap MapType = iota @@ -85,15 +90,28 @@ const ( SkStorage // DevMapHash - Hash-based indexing scheme for references to network devices. DevMapHash - StructOpts + // StructOpsMap - This map holds a kernel struct with its function pointer implemented in a BPF + // program. + StructOpsMap + // RingBuf - Similar to PerfEventArray, but shared across all CPUs. RingBuf + // InodeStorage - Specialized local storage map for inodes. InodeStorage + // TaskStorage - Specialized local storage map for task_struct. TaskStorage + // maxMapType - Bound enum of MapTypes, has to be last in enum. + maxMapType ) +// Deprecated: StructOpts was a typo, use StructOpsMap instead. +// +// Declared as a variable to prevent stringer from picking it up +// as an enum value. +var StructOpts MapType = StructOpsMap + // hasPerCPUValue returns true if the Map stores a value per CPU. func (mt MapType) hasPerCPUValue() bool { - return mt == PerCPUHash || mt == PerCPUArray || mt == LRUCPUHash + return mt == PerCPUHash || mt == PerCPUArray || mt == LRUCPUHash || mt == PerCPUCGroupStorage } // canStoreMap returns true if the map type accepts a map fd @@ -111,6 +129,11 @@ func (mt MapType) canStoreProgram() bool { // ProgramType of the eBPF program type ProgramType uint32 +// Max return the latest supported ProgramType. +func (_ ProgramType) Max() ProgramType { + return maxProgramType - 1 +} + // eBPF program types const ( UnspecifiedProgram ProgramType = iota @@ -144,6 +167,7 @@ const ( Extension LSM SkLookup + maxProgramType ) // AttachType of the eBPF program, needed to differentiate allowed context accesses in @@ -151,6 +175,8 @@ const ( // Will cause invalid argument (EINVAL) at program load time if set incorrectly. type AttachType uint32 +//go:generate stringer -type AttachType -trimprefix Attach + // AttachNone is an alias for AttachCGroupInetIngress for readability reasons. const AttachNone AttachType = 0 @@ -193,6 +219,10 @@ const ( AttachXDPCPUMap AttachSkLookup AttachXDP + AttachSkSKBVerdict + AttachSkReuseportSelect + AttachSkReuseportSelectOrMigrate + AttachPerfEvent ) // AttachFlags of the eBPF program used in BPF_PROG_ATTACH command diff --git a/vendor/github.com/cilium/ebpf/types_string.go b/vendor/github.com/cilium/ebpf/types_string.go index c25f76564..81cbc9efd 100644 --- a/vendor/github.com/cilium/ebpf/types_string.go +++ b/vendor/github.com/cilium/ebpf/types_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -output types_string.go -type=MapType,ProgramType,AttachType,PinType"; DO NOT EDIT. +// Code generated by "stringer -output types_string.go -type=MapType,ProgramType,PinType"; DO NOT EDIT. package ebpf @@ -34,15 +34,16 @@ func _() { _ = x[Stack-23] _ = x[SkStorage-24] _ = x[DevMapHash-25] - _ = x[StructOpts-26] + _ = x[StructOpsMap-26] _ = x[RingBuf-27] _ = x[InodeStorage-28] _ = x[TaskStorage-29] + _ = x[maxMapType-30] } -const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOptsRingBufInodeStorageTaskStorage" +const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOpsMapRingBufInodeStorageTaskStoragemaxMapType" -var _MapType_index = [...]uint16{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248, 258, 265, 277, 288} +var _MapType_index = [...]uint16{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248, 260, 267, 279, 290, 300} func (i MapType) String() string { if i >= MapType(len(_MapType_index)-1) { @@ -85,11 +86,12 @@ func _() { _ = x[Extension-28] _ = x[LSM-29] _ = x[SkLookup-30] + _ = x[maxProgramType-31] } -const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracingStructOpsExtensionLSMSkLookup" +const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracingStructOpsExtensionLSMSkLookupmaxProgramType" -var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258, 265, 274, 283, 286, 294} +var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258, 265, 274, 283, 286, 294, 308} func (i ProgramType) String() string { if i >= ProgramType(len(_ProgramType_index)-1) { @@ -97,61 +99,6 @@ func (i ProgramType) String() string { } return _ProgramType_name[_ProgramType_index[i]:_ProgramType_index[i+1]] } -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[AttachNone-0] - _ = x[AttachCGroupInetIngress-0] - _ = x[AttachCGroupInetEgress-1] - _ = x[AttachCGroupInetSockCreate-2] - _ = x[AttachCGroupSockOps-3] - _ = x[AttachSkSKBStreamParser-4] - _ = x[AttachSkSKBStreamVerdict-5] - _ = x[AttachCGroupDevice-6] - _ = x[AttachSkMsgVerdict-7] - _ = x[AttachCGroupInet4Bind-8] - _ = x[AttachCGroupInet6Bind-9] - _ = x[AttachCGroupInet4Connect-10] - _ = x[AttachCGroupInet6Connect-11] - _ = x[AttachCGroupInet4PostBind-12] - _ = x[AttachCGroupInet6PostBind-13] - _ = x[AttachCGroupUDP4Sendmsg-14] - _ = x[AttachCGroupUDP6Sendmsg-15] - _ = x[AttachLircMode2-16] - _ = x[AttachFlowDissector-17] - _ = x[AttachCGroupSysctl-18] - _ = x[AttachCGroupUDP4Recvmsg-19] - _ = x[AttachCGroupUDP6Recvmsg-20] - _ = x[AttachCGroupGetsockopt-21] - _ = x[AttachCGroupSetsockopt-22] - _ = x[AttachTraceRawTp-23] - _ = x[AttachTraceFEntry-24] - _ = x[AttachTraceFExit-25] - _ = x[AttachModifyReturn-26] - _ = x[AttachLSMMac-27] - _ = x[AttachTraceIter-28] - _ = x[AttachCgroupInet4GetPeername-29] - _ = x[AttachCgroupInet6GetPeername-30] - _ = x[AttachCgroupInet4GetSockname-31] - _ = x[AttachCgroupInet6GetSockname-32] - _ = x[AttachXDPDevMap-33] - _ = x[AttachCgroupInetSockRelease-34] - _ = x[AttachXDPCPUMap-35] - _ = x[AttachSkLookup-36] - _ = x[AttachXDP-37] -} - -const _AttachType_name = "AttachNoneAttachCGroupInetEgressAttachCGroupInetSockCreateAttachCGroupSockOpsAttachSkSKBStreamParserAttachSkSKBStreamVerdictAttachCGroupDeviceAttachSkMsgVerdictAttachCGroupInet4BindAttachCGroupInet6BindAttachCGroupInet4ConnectAttachCGroupInet6ConnectAttachCGroupInet4PostBindAttachCGroupInet6PostBindAttachCGroupUDP4SendmsgAttachCGroupUDP6SendmsgAttachLircMode2AttachFlowDissectorAttachCGroupSysctlAttachCGroupUDP4RecvmsgAttachCGroupUDP6RecvmsgAttachCGroupGetsockoptAttachCGroupSetsockoptAttachTraceRawTpAttachTraceFEntryAttachTraceFExitAttachModifyReturnAttachLSMMacAttachTraceIterAttachCgroupInet4GetPeernameAttachCgroupInet6GetPeernameAttachCgroupInet4GetSocknameAttachCgroupInet6GetSocknameAttachXDPDevMapAttachCgroupInetSockReleaseAttachXDPCPUMapAttachSkLookupAttachXDP" - -var _AttachType_index = [...]uint16{0, 10, 32, 58, 77, 100, 124, 142, 160, 181, 202, 226, 250, 275, 300, 323, 346, 361, 380, 398, 421, 444, 466, 488, 504, 521, 537, 555, 567, 582, 610, 638, 666, 694, 709, 736, 751, 765, 774} - -func (i AttachType) String() string { - if i >= AttachType(len(_AttachType_index)-1) { - return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _AttachType_name[_AttachType_index[i]:_AttachType_index[i+1]] -} func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. diff --git a/vendor/github.com/cyphar/filepath-securejoin/.travis.yml b/vendor/github.com/cyphar/filepath-securejoin/.travis.yml index 3938f3834..b94ff8cf9 100644 --- a/vendor/github.com/cyphar/filepath-securejoin/.travis.yml +++ b/vendor/github.com/cyphar/filepath-securejoin/.travis.yml @@ -4,10 +4,12 @@ language: go go: - - 1.7.x - - 1.8.x + - 1.13.x + - 1.16.x - tip - +arch: + - AMD64 + - ppc64le os: - linux - osx diff --git a/vendor/github.com/cyphar/filepath-securejoin/BUILD.bazel b/vendor/github.com/cyphar/filepath-securejoin/BUILD.bazel index 4dcb35975..8d1d42328 100644 --- a/vendor/github.com/cyphar/filepath-securejoin/BUILD.bazel +++ b/vendor/github.com/cyphar/filepath-securejoin/BUILD.bazel @@ -9,5 +9,4 @@ go_library( importmap = "kubevirt.io/kubevirt/vendor/github.com/cyphar/filepath-securejoin", importpath = "github.com/cyphar/filepath-securejoin", visibility = ["//visibility:public"], - deps = ["//vendor/github.com/pkg/errors:go_default_library"], ) diff --git a/vendor/github.com/cyphar/filepath-securejoin/README.md b/vendor/github.com/cyphar/filepath-securejoin/README.md index 49b2baa9f..3624617c8 100644 --- a/vendor/github.com/cyphar/filepath-securejoin/README.md +++ b/vendor/github.com/cyphar/filepath-securejoin/README.md @@ -7,6 +7,19 @@ standard library][go#20126]. The purpose of this function is to be a "secure" alternative to `filepath.Join`, and in particular it provides certain guarantees that are not provided by `filepath.Join`. +> **NOTE**: This code is *only* safe if you are not at risk of other processes +> modifying path components after you've used `SecureJoin`. If it is possible +> for a malicious process to modify path components of the resolved path, then +> you will be vulnerable to some fairly trivial TOCTOU race conditions. [There +> are some Linux kernel patches I'm working on which might allow for a better +> solution.][lwn-obeneath] +> +> In addition, with a slightly modified API it might be possible to use +> `O_PATH` and verify that the opened path is actually the resolved one -- but +> I have not done that yet. I might add it in the future as a helper function +> to help users verify the path (we can't just return `/proc/self/fd/<foo>` +> because that doesn't always work transparently for all users). + This is the function prototype: ```go @@ -16,8 +29,8 @@ func SecureJoin(root, unsafePath string) (string, error) This library **guarantees** the following: * If no error is set, the resulting string **must** be a child path of - `SecureJoin` and will not contain any symlink path components (they will all - be expanded). + `root` and will not contain any symlink path components (they will all be + expanded). * When expanding symlinks, all symlink path components **must** be resolved relative to the provided root. In particular, this can be considered a @@ -25,7 +38,7 @@ This library **guarantees** the following: these symlinks will **not** be expanded lexically (`filepath.Clean` is not called on the input before processing). -* Non-existant path components are unaffected by `SecureJoin` (similar to +* Non-existent path components are unaffected by `SecureJoin` (similar to `filepath.EvalSymlinks`'s semantics). * The returned path will always be `filepath.Clean`ed and thus not contain any @@ -57,6 +70,7 @@ func SecureJoin(root, unsafePath string) (string, error) { } ``` +[lwn-obeneath]: https://lwn.net/Articles/767547/ [go#20126]: https://github.com/golang/go/issues/20126 ### License ### diff --git a/vendor/github.com/cyphar/filepath-securejoin/VERSION b/vendor/github.com/cyphar/filepath-securejoin/VERSION index ee1372d33..717903969 100644 --- a/vendor/github.com/cyphar/filepath-securejoin/VERSION +++ b/vendor/github.com/cyphar/filepath-securejoin/VERSION @@ -1 +1 @@ -0.2.2 +0.2.3 diff --git a/vendor/github.com/cyphar/filepath-securejoin/join.go b/vendor/github.com/cyphar/filepath-securejoin/join.go index c4ca3d713..7dd08dbbd 100644 --- a/vendor/github.com/cyphar/filepath-securejoin/join.go +++ b/vendor/github.com/cyphar/filepath-securejoin/join.go @@ -12,39 +12,20 @@ package securejoin import ( "bytes" + "errors" "os" "path/filepath" "strings" "syscall" - - "github.com/pkg/errors" ) -// ErrSymlinkLoop is returned by SecureJoinVFS when too many symlinks have been -// evaluated in attempting to securely join the two given paths. -var ErrSymlinkLoop = errors.Wrap(syscall.ELOOP, "secure join") - // IsNotExist tells you if err is an error that implies that either the path // accessed does not exist (or path components don't exist). This is // effectively a more broad version of os.IsNotExist. func IsNotExist(err error) bool { - // If it's a bone-fide ENOENT just bail. - if os.IsNotExist(errors.Cause(err)) { - return true - } - // Check that it's not actually an ENOTDIR, which in some cases is a more // convoluted case of ENOENT (usually involving weird paths). - var errno error - switch err := errors.Cause(err).(type) { - case *os.PathError: - errno = err.Err - case *os.LinkError: - errno = err.Err - case *os.SyscallError: - errno = err.Err - } - return errno == syscall.ENOTDIR || errno == syscall.ENOENT + return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) || errors.Is(err, syscall.ENOENT) } // SecureJoinVFS joins the two given path components (similar to Join) except @@ -68,7 +49,7 @@ func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) { n := 0 for unsafePath != "" { if n > 255 { - return "", ErrSymlinkLoop + return "", &os.PathError{Op: "SecureJoin", Path: root + "/" + unsafePath, Err: syscall.ELOOP} } // Next path component, p. diff --git a/vendor/github.com/cyphar/filepath-securejoin/vendor.conf b/vendor/github.com/cyphar/filepath-securejoin/vendor.conf deleted file mode 100644 index 66bb574b9..000000000 --- a/vendor/github.com/cyphar/filepath-securejoin/vendor.conf +++ /dev/null @@ -1 +0,0 @@ -github.com/pkg/errors v0.8.0 diff --git a/vendor/github.com/godbus/dbus/v5/BUILD.bazel b/vendor/github.com/godbus/dbus/v5/BUILD.bazel index 742a02801..97a6b49f1 100644 --- a/vendor/github.com/godbus/dbus/v5/BUILD.bazel +++ b/vendor/github.com/godbus/dbus/v5/BUILD.bazel @@ -36,6 +36,7 @@ go_library( "transport_unixcred_dragonfly.go", "transport_unixcred_freebsd.go", "transport_unixcred_linux.go", + "transport_unixcred_netbsd.go", "transport_unixcred_openbsd.go", "variant.go", "variant_lexer.go", diff --git a/vendor/github.com/godbus/dbus/v5/README.markdown b/vendor/github.com/godbus/dbus/v5/README.md similarity index 81% rename from vendor/github.com/godbus/dbus/v5/README.markdown rename to vendor/github.com/godbus/dbus/v5/README.md index 1fb2eacaa..5c2412583 100644 --- a/vendor/github.com/godbus/dbus/v5/README.markdown +++ b/vendor/github.com/godbus/dbus/v5/README.md @@ -14,14 +14,12 @@ D-Bus message bus system. ### Installation -This packages requires Go 1.7. If you installed it and set up your GOPATH, just run: +This packages requires Go 1.12 or later. It can be installed by running the command below: ``` -go get github.com/godbus/dbus +go get github.com/godbus/dbus/v5 ``` -If you want to use the subpackages, you can install them the same way. - ### Usage The complete package documentation and some simple examples are available at @@ -30,10 +28,12 @@ The complete package documentation and some simple examples are available at gives a short overview over the basic usage. #### Projects using godbus -- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library. +- [fyne](https://github.com/fyne-io/fyne) a cross platform GUI in Go inspired by Material Design. +- [fynedesk](https://github.com/fyne-io/fynedesk) a full desktop environment for Linux/Unix using Fyne. - [go-bluetooth](https://github.com/muka/go-bluetooth) provides a bluetooth client over bluez dbus API. -- [playerbm](https://github.com/altdesktop/playerbm) a bookmark utility for media players. - [iwd](https://github.com/shibumi/iwd) go bindings for the internet wireless daemon "iwd". +- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library. +- [playerbm](https://github.com/altdesktop/playerbm) a bookmark utility for media players. Please note that the API is considered unstable for now and may change without further notice. diff --git a/vendor/github.com/godbus/dbus/v5/auth.go b/vendor/github.com/godbus/dbus/v5/auth.go index 283487a0e..a59b4c0eb 100644 --- a/vendor/github.com/godbus/dbus/v5/auth.go +++ b/vendor/github.com/godbus/dbus/v5/auth.go @@ -53,7 +53,7 @@ type Auth interface { // bus. Auth must not be called on shared connections. func (conn *Conn) Auth(methods []Auth) error { if methods == nil { - uid := strconv.Itoa(os.Getuid()) + uid := strconv.Itoa(os.Geteuid()) methods = []Auth{AuthExternal(uid), AuthCookieSha1(uid, getHomeDir())} } in := bufio.NewReader(conn.transport) @@ -75,9 +75,9 @@ func (conn *Conn) Auth(methods []Auth) error { s = s[1:] for _, v := range s { for _, m := range methods { - if name, data, status := m.FirstData(); bytes.Equal(v, name) { + if name, _, status := m.FirstData(); bytes.Equal(v, name) { var ok bool - err = authWriteLine(conn.transport, []byte("AUTH"), v, data) + err = authWriteLine(conn.transport, []byte("AUTH"), v) if err != nil { return err } @@ -194,11 +194,14 @@ func (conn *Conn) tryAuth(m Auth, state authState, in *bufio.Reader) (error, boo } conn.uuid = string(s[1]) return nil, true + case state == waitingForOk && string(s[0]) == "DATA": + err = authWriteLine(conn.transport, []byte("DATA")) + if err != nil { + return err, false + } case state == waitingForOk && string(s[0]) == "REJECTED": return nil, false - case state == waitingForOk && (string(s[0]) == "DATA" || - string(s[0]) == "ERROR"): - + case state == waitingForOk && string(s[0]) == "ERROR": err = authWriteLine(conn.transport, []byte("CANCEL")) if err != nil { return err, false diff --git a/vendor/github.com/godbus/dbus/v5/conn.go b/vendor/github.com/godbus/dbus/v5/conn.go index 29fe018ad..76fc5cde3 100644 --- a/vendor/github.com/godbus/dbus/v5/conn.go +++ b/vendor/github.com/godbus/dbus/v5/conn.go @@ -73,7 +73,7 @@ func SessionBus() (conn *Conn, err error) { return } -func getSessionBusAddress() (string, error) { +func getSessionBusAddress(autolaunch bool) (string, error) { if address := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); address != "" && address != "autolaunch:" { return address, nil @@ -81,12 +81,26 @@ func getSessionBusAddress() (string, error) { os.Setenv("DBUS_SESSION_BUS_ADDRESS", address) return address, nil } + if !autolaunch { + return "", errors.New("dbus: couldn't determine address of session bus") + } return getSessionBusPlatformAddress() } // SessionBusPrivate returns a new private connection to the session bus. func SessionBusPrivate(opts ...ConnOption) (*Conn, error) { - address, err := getSessionBusAddress() + address, err := getSessionBusAddress(true) + if err != nil { + return nil, err + } + + return Dial(address, opts...) +} + +// SessionBusPrivate returns a new private connection to the session bus. If +// the session bus is not already open, do not attempt to launch it. +func SessionBusPrivateNoAutoStartup(opts ...ConnOption) (*Conn, error) { + address, err := getSessionBusAddress(false) if err != nil { return nil, err } @@ -121,7 +135,7 @@ func SystemBus() (conn *Conn, err error) { // ConnectSessionBus connects to the session bus. func ConnectSessionBus(opts ...ConnOption) (*Conn, error) { - address, err := getSessionBusAddress() + address, err := getSessionBusAddress(true) if err != nil { return nil, err } @@ -180,7 +194,7 @@ func Dial(address string, opts ...ConnOption) (*Conn, error) { // // Deprecated: use Dial with options instead. func DialHandler(address string, handler Handler, signalHandler SignalHandler) (*Conn, error) { - return Dial(address, WithSignalHandler(signalHandler)) + return Dial(address, WithHandler(handler), WithSignalHandler(signalHandler)) } // ConnOption is a connection option. @@ -478,14 +492,24 @@ func (conn *Conn) sendMessageAndIfClosed(msg *Message, ifClosed func()) { conn.outInt(msg) } err := conn.outHandler.sendAndIfClosed(msg, ifClosed) - conn.calls.handleSendError(msg, err) if err != nil { - conn.serialGen.RetireSerial(msg.serial) + conn.handleSendError(msg, err) } else if msg.Type != TypeMethodCall { conn.serialGen.RetireSerial(msg.serial) } } +func (conn *Conn) handleSendError(msg *Message, err error) { + if msg.Type == TypeMethodCall { + conn.calls.handleSendError(msg, err) + } else if msg.Type == TypeMethodReply { + if _, ok := err.(FormatError); ok { + conn.sendError(err, msg.Headers[FieldDestination].value.(string), msg.Headers[FieldReplySerial].value.(uint32)) + } + } + conn.serialGen.RetireSerial(msg.serial) +} + // Send sends the given message to the message bus. You usually don't need to // use this; use the higher-level equivalents (Call / Go, Emit and Export) // instead. If msg is a method call and NoReplyExpected is not set, a non-nil diff --git a/vendor/github.com/godbus/dbus/v5/decoder.go b/vendor/github.com/godbus/dbus/v5/decoder.go index ede91575b..89bfed9d1 100644 --- a/vendor/github.com/godbus/dbus/v5/decoder.go +++ b/vendor/github.com/godbus/dbus/v5/decoder.go @@ -10,14 +10,16 @@ type decoder struct { in io.Reader order binary.ByteOrder pos int + fds []int } // newDecoder returns a new decoder that reads values from in. The input is // expected to be in the given byte order. -func newDecoder(in io.Reader, order binary.ByteOrder) *decoder { +func newDecoder(in io.Reader, order binary.ByteOrder, fds []int) *decoder { dec := new(decoder) dec.in = in dec.order = order + dec.fds = fds return dec } @@ -53,7 +55,7 @@ func (dec *decoder) Decode(sig Signature) (vs []interface{}, err error) { vs = make([]interface{}, 0) s := sig.str for s != "" { - err, rem := validSingle(s, 0) + err, rem := validSingle(s, &depthCounter{}) if err != nil { return nil, err } @@ -150,7 +152,7 @@ func (dec *decoder) decode(s string, depth int) interface{} { if len(sig.str) == 0 { panic(FormatError("variant signature is empty")) } - err, rem := validSingle(sig.str, 0) + err, rem := validSingle(sig.str, &depthCounter{}) if err != nil { panic(err) } @@ -161,7 +163,11 @@ func (dec *decoder) decode(s string, depth int) interface{} { variant.value = dec.decode(sig.str, depth+1) return variant case 'h': - return UnixFDIndex(dec.decode("u", depth).(uint32)) + idx := dec.decode("u", depth).(uint32) + if int(idx) < len(dec.fds) { + return UnixFD(dec.fds[idx]) + } + return UnixFDIndex(idx) case 'a': if len(s) > 1 && s[1] == '{' { ksig := s[2:3] @@ -219,7 +225,7 @@ func (dec *decoder) decode(s string, depth int) interface{} { v := make([]interface{}, 0) s = s[1 : len(s)-1] for s != "" { - err, rem := validSingle(s, 0) + err, rem := validSingle(s, &depthCounter{}) if err != nil { panic(err) } diff --git a/vendor/github.com/godbus/dbus/v5/encoder.go b/vendor/github.com/godbus/dbus/v5/encoder.go index adfbb75c5..015b26cd5 100644 --- a/vendor/github.com/godbus/dbus/v5/encoder.go +++ b/vendor/github.com/godbus/dbus/v5/encoder.go @@ -5,28 +5,33 @@ import ( "encoding/binary" "io" "reflect" + "strings" + "unicode/utf8" ) // An encoder encodes values to the D-Bus wire format. type encoder struct { out io.Writer + fds []int order binary.ByteOrder pos int } // NewEncoder returns a new encoder that writes to out in the given byte order. -func newEncoder(out io.Writer, order binary.ByteOrder) *encoder { - return newEncoderAtOffset(out, 0, order) +func newEncoder(out io.Writer, order binary.ByteOrder, fds []int) *encoder { + enc := newEncoderAtOffset(out, 0, order, fds) + return enc } // newEncoderAtOffset returns a new encoder that writes to out in the given // byte order. Specify the offset to initialize pos for proper alignment // computation. -func newEncoderAtOffset(out io.Writer, offset int, order binary.ByteOrder) *encoder { +func newEncoderAtOffset(out io.Writer, offset int, order binary.ByteOrder, fds []int) *encoder { enc := new(encoder) enc.out = out enc.order = order enc.pos = offset + enc.fds = fds return enc } @@ -75,6 +80,9 @@ func (enc *encoder) Encode(vs ...interface{}) (err error) { // encode encodes the given value to the writer and panics on error. depth holds // the depth of the container nesting. func (enc *encoder) encode(v reflect.Value, depth int) { + if depth > 64 { + panic(FormatError("input exceeds depth limitation")) + } enc.align(alignment(v.Type())) switch v.Kind() { case reflect.Uint8: @@ -97,7 +105,14 @@ func (enc *encoder) encode(v reflect.Value, depth int) { enc.binwrite(uint16(v.Uint())) enc.pos += 2 case reflect.Int, reflect.Int32: - enc.binwrite(int32(v.Int())) + if v.Type() == unixFDType { + fd := v.Int() + idx := len(enc.fds) + enc.fds = append(enc.fds, int(fd)) + enc.binwrite(uint32(idx)) + } else { + enc.binwrite(int32(v.Int())) + } enc.pos += 4 case reflect.Uint, reflect.Uint32: enc.binwrite(uint32(v.Uint())) @@ -112,9 +127,21 @@ func (enc *encoder) encode(v reflect.Value, depth int) { enc.binwrite(v.Float()) enc.pos += 8 case reflect.String: - enc.encode(reflect.ValueOf(uint32(len(v.String()))), depth) + str := v.String() + if !utf8.ValidString(str) { + panic(FormatError("input has a not-utf8 char in string")) + } + if strings.IndexByte(str, byte(0)) != -1 { + panic(FormatError("input has a null char('\\000') in string")) + } + if v.Type() == objectPathType { + if !ObjectPath(str).IsValid() { + panic(FormatError("invalid object path")) + } + } + enc.encode(reflect.ValueOf(uint32(len(str))), depth) b := make([]byte, v.Len()+1) - copy(b, v.String()) + copy(b, str) b[len(b)-1] = 0 n, err := enc.out.Write(b) if err != nil { @@ -124,20 +151,23 @@ func (enc *encoder) encode(v reflect.Value, depth int) { case reflect.Ptr: enc.encode(v.Elem(), depth) case reflect.Slice, reflect.Array: - if depth >= 64 { - panic(FormatError("input exceeds container depth limit")) - } // Lookahead offset: 4 bytes for uint32 length (with alignment), // plus alignment for elements. n := enc.padding(0, 4) + 4 offset := enc.pos + n + enc.padding(n, alignment(v.Type().Elem())) var buf bytes.Buffer - bufenc := newEncoderAtOffset(&buf, offset, enc.order) + bufenc := newEncoderAtOffset(&buf, offset, enc.order, enc.fds) for i := 0; i < v.Len(); i++ { bufenc.encode(v.Index(i), depth+1) } + + if buf.Len() > 1<<26 { + panic(FormatError("input exceeds array size limitation")) + } + + enc.fds = bufenc.fds enc.encode(reflect.ValueOf(uint32(buf.Len())), depth) length := buf.Len() enc.align(alignment(v.Type().Elem())) @@ -146,13 +176,10 @@ func (enc *encoder) encode(v reflect.Value, depth int) { } enc.pos += length case reflect.Struct: - if depth >= 64 && v.Type() != signatureType { - panic(FormatError("input exceeds container depth limit")) - } switch t := v.Type(); t { case signatureType: str := v.Field(0) - enc.encode(reflect.ValueOf(byte(str.Len())), depth+1) + enc.encode(reflect.ValueOf(byte(str.Len())), depth) b := make([]byte, str.Len()+1) copy(b, str.String()) b[len(b)-1] = 0 @@ -176,9 +203,6 @@ func (enc *encoder) encode(v reflect.Value, depth int) { case reflect.Map: // Maps are arrays of structures, so they actually increase the depth by // 2. - if depth >= 63 { - panic(FormatError("input exceeds container depth limit")) - } if !isKeyType(v.Type().Key()) { panic(InvalidTypeError{v.Type()}) } @@ -189,12 +213,13 @@ func (enc *encoder) encode(v reflect.Value, depth int) { offset := enc.pos + n + enc.padding(n, 8) var buf bytes.Buffer - bufenc := newEncoderAtOffset(&buf, offset, enc.order) + bufenc := newEncoderAtOffset(&buf, offset, enc.order, enc.fds) for _, k := range keys { bufenc.align(8) bufenc.encode(k, depth+2) bufenc.encode(v.MapIndex(k), depth+2) } + enc.fds = bufenc.fds enc.encode(reflect.ValueOf(uint32(buf.Len())), depth) length := buf.Len() enc.align(8) diff --git a/vendor/github.com/godbus/dbus/v5/export.go b/vendor/github.com/godbus/dbus/v5/export.go index 2447b51d4..522334715 100644 --- a/vendor/github.com/godbus/dbus/v5/export.go +++ b/vendor/github.com/godbus/dbus/v5/export.go @@ -26,6 +26,27 @@ var ( } ) +func MakeNoObjectError(path ObjectPath) Error { + return Error{ + "org.freedesktop.DBus.Error.NoSuchObject", + []interface{}{fmt.Sprintf("No such object '%s'", string(path))}, + } +} + +func MakeUnknownMethodError(methodName string) Error { + return Error{ + "org.freedesktop.DBus.Error.UnknownMethod", + []interface{}{fmt.Sprintf("Unknown / invalid method '%s'", methodName)}, + } +} + +func MakeUnknownInterfaceError(ifaceName string) Error { + return Error{ + "org.freedesktop.DBus.Error.UnknownInterface", + []interface{}{fmt.Sprintf("Object does not implement the interface '%s'", ifaceName)}, + } +} + func MakeFailedError(err error) *Error { return &Error{ "org.freedesktop.DBus.Error.Failed", @@ -128,6 +149,11 @@ func (conn *Conn) handleCall(msg *Message) { ifaceName, _ := msg.Headers[FieldInterface].value.(string) sender, hasSender := msg.Headers[FieldSender].value.(string) serial := msg.serial + + if len(name) == 0 { + conn.sendError(ErrMsgUnknownMethod, sender, serial) + } + if ifaceName == "org.freedesktop.DBus.Peer" { switch name { case "Ping": @@ -135,29 +161,26 @@ func (conn *Conn) handleCall(msg *Message) { case "GetMachineId": conn.sendReply(sender, serial, conn.uuid) default: - conn.sendError(ErrMsgUnknownMethod, sender, serial) + conn.sendError(MakeUnknownMethodError(name), sender, serial) } return } - if len(name) == 0 { - conn.sendError(ErrMsgUnknownMethod, sender, serial) - } object, ok := conn.handler.LookupObject(path) if !ok { - conn.sendError(ErrMsgNoObject, sender, serial) + conn.sendError(MakeNoObjectError(path), sender, serial) return } iface, exists := object.LookupInterface(ifaceName) if !exists { - conn.sendError(ErrMsgUnknownInterface, sender, serial) + conn.sendError(MakeUnknownInterfaceError(ifaceName), sender, serial) return } m, exists := iface.LookupMethod(name) if !exists { - conn.sendError(ErrMsgUnknownMethod, sender, serial) + conn.sendError(MakeUnknownMethodError(name), sender, serial) return } args, err := conn.decodeArguments(m, sender, msg) diff --git a/vendor/github.com/godbus/dbus/v5/message.go b/vendor/github.com/godbus/dbus/v5/message.go index 6a925367e..16693eb30 100644 --- a/vendor/github.com/godbus/dbus/v5/message.go +++ b/vendor/github.com/godbus/dbus/v5/message.go @@ -118,11 +118,7 @@ type header struct { Variant } -// DecodeMessage tries to decode a single message in the D-Bus wire format -// from the given reader. The byte order is figured out from the first byte. -// The possibly returned error can be an error of the underlying reader, an -// InvalidMessageError or a FormatError. -func DecodeMessage(rd io.Reader) (msg *Message, err error) { +func DecodeMessageWithFDs(rd io.Reader, fds []int) (msg *Message, err error) { var order binary.ByteOrder var hlength, length uint32 var typ, flags, proto byte @@ -142,7 +138,7 @@ func DecodeMessage(rd io.Reader) (msg *Message, err error) { return nil, InvalidMessageError("invalid byte order") } - dec := newDecoder(rd, order) + dec := newDecoder(rd, order, fds) dec.pos = 1 msg = new(Message) @@ -166,7 +162,7 @@ func DecodeMessage(rd io.Reader) (msg *Message, err error) { if hlength+length+16 > 1<<27 { return nil, InvalidMessageError("message is too long") } - dec = newDecoder(io.MultiReader(bytes.NewBuffer(b), rd), order) + dec = newDecoder(io.MultiReader(bytes.NewBuffer(b), rd), order, fds) dec.pos = 12 vs, err = dec.Decode(Signature{"a(yv)"}) if err != nil { @@ -196,7 +192,7 @@ func DecodeMessage(rd io.Reader) (msg *Message, err error) { sig, _ := msg.Headers[FieldSignature].value.(Signature) if sig.str != "" { buf := bytes.NewBuffer(body) - dec = newDecoder(buf, order) + dec = newDecoder(buf, order, fds) vs, err := dec.Decode(sig) if err != nil { return nil, err @@ -207,12 +203,32 @@ func DecodeMessage(rd io.Reader) (msg *Message, err error) { return } -// EncodeTo encodes and sends a message to the given writer. The byte order must -// be either binary.LittleEndian or binary.BigEndian. If the message is not -// valid or an error occurs when writing, an error is returned. -func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) error { +// DecodeMessage tries to decode a single message in the D-Bus wire format +// from the given reader. The byte order is figured out from the first byte. +// The possibly returned error can be an error of the underlying reader, an +// InvalidMessageError or a FormatError. +func DecodeMessage(rd io.Reader) (msg *Message, err error) { + return DecodeMessageWithFDs(rd, make([]int, 0)); +} + +type nullwriter struct{} + +func (nullwriter) Write(p []byte) (cnt int, err error) { + return len(p), nil +} + +func (msg *Message) CountFds() (int, error) { + if len(msg.Body) == 0 { + return 0, nil + } + enc := newEncoder(nullwriter{}, nativeEndian, make([]int, 0)) + err := enc.Encode(msg.Body...) + return len(enc.fds), err +} + +func (msg *Message) EncodeToWithFDs(out io.Writer, order binary.ByteOrder) (fds []int, err error) { if err := msg.IsValid(); err != nil { - return err + return make([]int, 0), err } var vs [7]interface{} switch order { @@ -221,12 +237,16 @@ func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) error { case binary.BigEndian: vs[0] = byte('B') default: - return errors.New("dbus: invalid byte order") + return make([]int, 0), errors.New("dbus: invalid byte order") } body := new(bytes.Buffer) - enc := newEncoder(body, order) + fds = make([]int, 0) + enc := newEncoder(body, order, fds) if len(msg.Body) != 0 { - enc.Encode(msg.Body...) + err = enc.Encode(msg.Body...) + if err != nil { + return + } } vs[1] = msg.Type vs[2] = msg.Flags @@ -239,17 +259,28 @@ func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) error { } vs[6] = headers var buf bytes.Buffer - enc = newEncoder(&buf, order) - enc.Encode(vs[:]...) + enc = newEncoder(&buf, order, enc.fds) + err = enc.Encode(vs[:]...) + if err != nil { + return + } enc.align(8) body.WriteTo(&buf) if buf.Len() > 1<<27 { - return InvalidMessageError("message is too long") + return make([]int, 0), InvalidMessageError("message is too long") } if _, err := buf.WriteTo(out); err != nil { - return err + return make([]int, 0), err } - return nil + return enc.fds, nil +} + +// EncodeTo encodes and sends a message to the given writer. The byte order must +// be either binary.LittleEndian or binary.BigEndian. If the message is not +// valid or an error occurs when writing, an error is returned. +func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) (err error) { + _, err = msg.EncodeToWithFDs(out, order) + return err } // IsValid checks whether msg is a valid message and returns an diff --git a/vendor/github.com/godbus/dbus/v5/sig.go b/vendor/github.com/godbus/dbus/v5/sig.go index 2d326cebc..41a039812 100644 --- a/vendor/github.com/godbus/dbus/v5/sig.go +++ b/vendor/github.com/godbus/dbus/v5/sig.go @@ -34,7 +34,7 @@ type Signature struct { func SignatureOf(vs ...interface{}) Signature { var s string for _, v := range vs { - s += getSignature(reflect.TypeOf(v)) + s += getSignature(reflect.TypeOf(v), &depthCounter{}) } return Signature{s} } @@ -42,11 +42,19 @@ func SignatureOf(vs ...interface{}) Signature { // SignatureOfType returns the signature of the given type. It panics if the // type is not representable in D-Bus. func SignatureOfType(t reflect.Type) Signature { - return Signature{getSignature(t)} + return Signature{getSignature(t, &depthCounter{})} } // getSignature returns the signature of the given type and panics on unknown types. -func getSignature(t reflect.Type) string { +func getSignature(t reflect.Type, depth *depthCounter) (sig string) { + if !depth.Valid() { + panic("container nesting too deep") + } + defer func() { + if len(sig) > 255 { + panic("signature exceeds the length limitation") + } + }() // handle simple types first switch t.Kind() { case reflect.Uint8: @@ -74,7 +82,7 @@ func getSignature(t reflect.Type) string { case reflect.Float64: return "d" case reflect.Ptr: - return getSignature(t.Elem()) + return getSignature(t.Elem(), depth) case reflect.String: if t == objectPathType { return "o" @@ -90,17 +98,20 @@ func getSignature(t reflect.Type) string { for i := 0; i < t.NumField(); i++ { field := t.Field(i) if field.PkgPath == "" && field.Tag.Get("dbus") != "-" { - s += getSignature(t.Field(i).Type) + s += getSignature(t.Field(i).Type, depth.EnterStruct()) } } + if len(s) == 0 { + panic("empty struct") + } return "(" + s + ")" case reflect.Array, reflect.Slice: - return "a" + getSignature(t.Elem()) + return "a" + getSignature(t.Elem(), depth.EnterArray()) case reflect.Map: if !isKeyType(t.Key()) { panic(InvalidTypeError{t}) } - return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}" + return "a{" + getSignature(t.Key(), depth.EnterArray().EnterDictEntry()) + getSignature(t.Elem(), depth.EnterArray().EnterDictEntry()) + "}" case reflect.Interface: return "v" } @@ -118,7 +129,7 @@ func ParseSignature(s string) (sig Signature, err error) { } sig.str = s for err == nil && len(s) != 0 { - err, s = validSingle(s, 0) + err, s = validSingle(s, &depthCounter{}) } if err != nil { sig = Signature{""} @@ -144,7 +155,7 @@ func (s Signature) Empty() bool { // Single returns whether the signature represents a single, complete type. func (s Signature) Single() bool { - err, r := validSingle(s.str, 0) + err, r := validSingle(s.str, &depthCounter{}) return err != nil && r == "" } @@ -164,15 +175,38 @@ func (e SignatureError) Error() string { return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason) } +type depthCounter struct { + arrayDepth, structDepth, dictEntryDepth int +} + +func (cnt *depthCounter) Valid() bool { + return cnt.arrayDepth <= 32 && cnt.structDepth <= 32 && cnt.dictEntryDepth <= 32 +} + +func (cnt depthCounter) EnterArray() *depthCounter { + cnt.arrayDepth++ + return &cnt +} + +func (cnt depthCounter) EnterStruct() *depthCounter { + cnt.structDepth++ + return &cnt +} + +func (cnt depthCounter) EnterDictEntry() *depthCounter { + cnt.dictEntryDepth++ + return &cnt +} + // Try to read a single type from this string. If it was successful, err is nil // and rem is the remaining unparsed part. Otherwise, err is a non-nil // SignatureError and rem is "". depth is the current recursion depth which may // not be greater than 64 and should be given as 0 on the first call. -func validSingle(s string, depth int) (err error, rem string) { +func validSingle(s string, depth *depthCounter) (err error, rem string) { if s == "" { return SignatureError{Sig: s, Reason: "empty signature"}, "" } - if depth > 64 { + if !depth.Valid() { return SignatureError{Sig: s, Reason: "container nesting too deep"}, "" } switch s[0] { @@ -187,10 +221,10 @@ func validSingle(s string, depth int) (err error, rem string) { i++ rem = s[i+1:] s = s[2:i] - if err, _ = validSingle(s[:1], depth+1); err != nil { + if err, _ = validSingle(s[:1], depth.EnterArray().EnterDictEntry()); err != nil { return err, "" } - err, nr := validSingle(s[1:], depth+1) + err, nr := validSingle(s[1:], depth.EnterArray().EnterDictEntry()) if err != nil { return err, "" } @@ -199,7 +233,7 @@ func validSingle(s string, depth int) (err error, rem string) { } return nil, rem } - return validSingle(s[1:], depth+1) + return validSingle(s[1:], depth.EnterArray()) case '(': i := findMatching(s, '(', ')') if i == -1 { @@ -208,7 +242,7 @@ func validSingle(s string, depth int) (err error, rem string) { rem = s[i+1:] s = s[1:i] for err == nil && s != "" { - err, s = validSingle(s, depth+1) + err, s = validSingle(s, depth.EnterStruct()) } if err != nil { rem = "" @@ -236,7 +270,7 @@ func findMatching(s string, left, right rune) int { // typeFor returns the type of the given signature. It ignores any left over // characters and panics if s doesn't start with a valid type signature. func typeFor(s string) (t reflect.Type) { - err, _ := validSingle(s, 0) + err, _ := validSingle(s, &depthCounter{}) if err != nil { panic(err) } diff --git a/vendor/github.com/godbus/dbus/v5/transport_generic.go b/vendor/github.com/godbus/dbus/v5/transport_generic.go index 718a1ff02..a08e2813c 100644 --- a/vendor/github.com/godbus/dbus/v5/transport_generic.go +++ b/vendor/github.com/godbus/dbus/v5/transport_generic.go @@ -41,10 +41,12 @@ func (t genericTransport) ReadMessage() (*Message, error) { } func (t genericTransport) SendMessage(msg *Message) error { - for _, v := range msg.Body { - if _, ok := v.(UnixFD); ok { - return errors.New("dbus: unix fd passing not enabled") - } + fds, err := msg.CountFds() + if err != nil { + return err + } + if fds != 0 { + return errors.New("dbus: unix fd passing not enabled") } return msg.EncodeTo(t, nativeEndian) } diff --git a/vendor/github.com/godbus/dbus/v5/transport_unix.go b/vendor/github.com/godbus/dbus/v5/transport_unix.go index c7cd02f97..2212e7fa7 100644 --- a/vendor/github.com/godbus/dbus/v5/transport_unix.go +++ b/vendor/github.com/godbus/dbus/v5/transport_unix.go @@ -113,7 +113,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { if _, err := io.ReadFull(t.rdr, headerdata[4:]); err != nil { return nil, err } - dec := newDecoder(bytes.NewBuffer(headerdata), order) + dec := newDecoder(bytes.NewBuffer(headerdata), order, make([]int, 0)) dec.pos = 12 vs, err := dec.Decode(Signature{"a(yv)"}) if err != nil { @@ -147,7 +147,7 @@ func (t *unixTransport) ReadMessage() (*Message, error) { if err != nil { return nil, err } - msg, err := DecodeMessage(bytes.NewBuffer(all)) + msg, err := DecodeMessageWithFDs(bytes.NewBuffer(all), fds) if err != nil { return nil, err } @@ -179,21 +179,21 @@ func (t *unixTransport) ReadMessage() (*Message, error) { } func (t *unixTransport) SendMessage(msg *Message) error { - fds := make([]int, 0) - for i, v := range msg.Body { - if fd, ok := v.(UnixFD); ok { - msg.Body[i] = UnixFDIndex(len(fds)) - fds = append(fds, int(fd)) - } + fdcnt, err := msg.CountFds() + if err != nil { + return err } - if len(fds) != 0 { + if fdcnt != 0 { if !t.hasUnixFDs { return errors.New("dbus: unix fd passing not enabled") } - msg.Headers[FieldUnixFDs] = MakeVariant(uint32(len(fds))) - oob := syscall.UnixRights(fds...) + msg.Headers[FieldUnixFDs] = MakeVariant(uint32(fdcnt)) buf := new(bytes.Buffer) - msg.EncodeTo(buf, nativeEndian) + fds, err := msg.EncodeToWithFDs(buf, nativeEndian) + if err != nil { + return err + } + oob := syscall.UnixRights(fds...) n, oobn, err := t.UnixConn.WriteMsgUnix(buf.Bytes(), oob, nil) if err != nil { return err diff --git a/vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go b/vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go new file mode 100644 index 000000000..af7bafdf9 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go @@ -0,0 +1,14 @@ +package dbus + +import "io" + +func (t *unixTransport) SendNullByte() error { + n, _, err := t.UnixConn.WriteMsgUnix([]byte{0}, nil, nil) + if err != nil { + return err + } + if n != 1 { + return io.ErrShortWrite + } + return nil +} diff --git a/vendor/github.com/moby/sys/mountinfo/BUILD.bazel b/vendor/github.com/moby/sys/mountinfo/BUILD.bazel index 06844d1b8..3831f1777 100644 --- a/vendor/github.com/moby/sys/mountinfo/BUILD.bazel +++ b/vendor/github.com/moby/sys/mountinfo/BUILD.bazel @@ -21,9 +21,15 @@ go_library( "@io_bazel_rules_go//go/platform:android": [ "//vendor/golang.org/x/sys/unix:go_default_library", ], + "@io_bazel_rules_go//go/platform:darwin": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], "@io_bazel_rules_go//go/platform:freebsd": [ "//vendor/golang.org/x/sys/unix:go_default_library", ], + "@io_bazel_rules_go//go/platform:ios": [ + "//vendor/golang.org/x/sys/unix:go_default_library", + ], "@io_bazel_rules_go//go/platform:linux": [ "//vendor/golang.org/x/sys/unix:go_default_library", ], diff --git a/vendor/github.com/moby/sys/mountinfo/mounted_linux.go b/vendor/github.com/moby/sys/mountinfo/mounted_linux.go index bc9f6b2ad..5c9e3e30e 100644 --- a/vendor/github.com/moby/sys/mountinfo/mounted_linux.go +++ b/vendor/github.com/moby/sys/mountinfo/mounted_linux.go @@ -16,9 +16,6 @@ func mountedByOpenat2(path string) (bool, error) { Flags: unix.O_PATH | unix.O_CLOEXEC, }) if err != nil { - if err == unix.ENOENT { // not a mount - return false, nil - } return false, &os.PathError{Op: "openat2", Path: dir, Err: err} } fd, err := unix.Openat2(dirfd, last, &unix.OpenHow{ @@ -26,20 +23,22 @@ func mountedByOpenat2(path string) (bool, error) { Resolve: unix.RESOLVE_NO_XDEV, }) _ = unix.Close(dirfd) - switch err { + switch err { //nolint:errorlint // unix errors are bare case nil: // definitely not a mount _ = unix.Close(fd) return false, nil case unix.EXDEV: // definitely a mount return true, nil - case unix.ENOENT: // not a mount - return false, nil } // not sure return false, &os.PathError{Op: "openat2", Path: path, Err: err} } func mounted(path string) (bool, error) { + path, err := normalizePath(path) + if err != nil { + return false, err + } // Try a fast path, using openat2() with RESOLVE_NO_XDEV. mounted, err := mountedByOpenat2(path) if err == nil { diff --git a/vendor/github.com/moby/sys/mountinfo/mounted_unix.go b/vendor/github.com/moby/sys/mountinfo/mounted_unix.go index efb03978b..45ddad236 100644 --- a/vendor/github.com/moby/sys/mountinfo/mounted_unix.go +++ b/vendor/github.com/moby/sys/mountinfo/mounted_unix.go @@ -1,9 +1,9 @@ -// +build linux freebsd,cgo openbsd,cgo +//go:build linux || (freebsd && cgo) || (openbsd && cgo) || (darwin && cgo) +// +build linux freebsd,cgo openbsd,cgo darwin,cgo package mountinfo import ( - "errors" "fmt" "os" "path/filepath" @@ -15,10 +15,6 @@ func mountedByStat(path string) (bool, error) { var st unix.Stat_t if err := unix.Lstat(path, &st); err != nil { - if err == unix.ENOENT { - // Treat ENOENT as "not mounted". - return false, nil - } return false, &os.PathError{Op: "stat", Path: path, Err: err} } dev := st.Dev @@ -49,14 +45,6 @@ func normalizePath(path string) (realPath string, err error) { } func mountedByMountinfo(path string) (bool, error) { - path, err := normalizePath(path) - if err != nil { - if errors.Is(err, unix.ENOENT) { - // treat ENOENT as "not mounted" - return false, nil - } - return false, err - } entries, err := GetMounts(SingleEntryFilter(path)) if err != nil { return false, err diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo.go b/vendor/github.com/moby/sys/mountinfo/mountinfo.go index 403a89331..9867a66dd 100644 --- a/vendor/github.com/moby/sys/mountinfo/mountinfo.go +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo.go @@ -10,11 +10,12 @@ func GetMounts(f FilterFunc) ([]*Info, error) { return parseMountTable(f) } -// Mounted determines if a specified path is a mount point. +// Mounted determines if a specified path is a mount point. In case of any +// error, false (and an error) is returned. // -// The argument must be an absolute path, with all symlinks resolved, and clean. -// One way to ensure it is to process the path using filepath.Abs followed by -// filepath.EvalSymlinks before calling this function. +// The non-existent path returns an error. If a caller is not interested +// in this particular error, it should handle it separately using e.g. +// errors.Is(err, os.ErrNotExist). func Mounted(path string) (bool, error) { // root is always mounted if path == string(os.PathSeparator) { diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_bsd.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_bsd.go index b1c12d02b..d5513a26d 100644 --- a/vendor/github.com/moby/sys/mountinfo/mountinfo_bsd.go +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_bsd.go @@ -1,4 +1,5 @@ -// +build freebsd,cgo openbsd,cgo +//go:build (freebsd && cgo) || (openbsd && cgo) || (darwin && cgo) +// +build freebsd,cgo openbsd,cgo darwin,cgo package mountinfo @@ -21,7 +22,7 @@ func parseMountTable(filter FilterFunc) ([]*Info, error) { count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT)) if count == 0 { - return nil, fmt.Errorf("Failed to call getmntinfo") + return nil, fmt.Errorf("failed to call getmntinfo") } var entries []C.struct_statfs @@ -55,6 +56,10 @@ func parseMountTable(filter FilterFunc) ([]*Info, error) { } func mounted(path string) (bool, error) { + path, err := normalizePath(path) + if err != nil { + return false, err + } // Fast path: compare st.st_dev fields. // This should always work for FreeBSD and OpenBSD. mounted, err := mountedByStat(path) diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go index f09a70fa0..59332b07b 100644 --- a/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_linux.go @@ -52,7 +52,7 @@ func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) { numFields := len(fields) if numFields < 10 { // should be at least 10 fields - return nil, fmt.Errorf("Parsing '%s' failed: not enough fields (%d)", text, numFields) + return nil, fmt.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields) } // separator field @@ -67,7 +67,7 @@ func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) { for fields[sepIdx] != "-" { sepIdx-- if sepIdx == 5 { - return nil, fmt.Errorf("Parsing '%s' failed: missing - separator", text) + return nil, fmt.Errorf("parsing '%s' failed: missing - separator", text) } } @@ -75,46 +75,39 @@ func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) { p.Mountpoint, err = unescape(fields[4]) if err != nil { - return nil, fmt.Errorf("Parsing '%s' failed: mount point: %w", fields[4], err) + return nil, fmt.Errorf("parsing '%s' failed: mount point: %w", fields[4], err) } p.FSType, err = unescape(fields[sepIdx+1]) if err != nil { - return nil, fmt.Errorf("Parsing '%s' failed: fstype: %w", fields[sepIdx+1], err) + return nil, fmt.Errorf("parsing '%s' failed: fstype: %w", fields[sepIdx+1], err) } p.Source, err = unescape(fields[sepIdx+2]) if err != nil { - return nil, fmt.Errorf("Parsing '%s' failed: source: %w", fields[sepIdx+2], err) + return nil, fmt.Errorf("parsing '%s' failed: source: %w", fields[sepIdx+2], err) } p.VFSOptions = fields[sepIdx+3] // ignore any numbers parsing errors, as there should not be any p.ID, _ = strconv.Atoi(fields[0]) p.Parent, _ = strconv.Atoi(fields[1]) - mm := strings.Split(fields[2], ":") + mm := strings.SplitN(fields[2], ":", 3) if len(mm) != 2 { - return nil, fmt.Errorf("Parsing '%s' failed: unexpected minor:major pair %s", text, mm) + return nil, fmt.Errorf("parsing '%s' failed: unexpected major:minor pair %s", text, mm) } p.Major, _ = strconv.Atoi(mm[0]) p.Minor, _ = strconv.Atoi(mm[1]) p.Root, err = unescape(fields[3]) if err != nil { - return nil, fmt.Errorf("Parsing '%s' failed: root: %w", fields[3], err) + return nil, fmt.Errorf("parsing '%s' failed: root: %w", fields[3], err) } p.Options = fields[5] // zero or more optional fields - switch { - case sepIdx == 6: - // zero, do nothing - case sepIdx == 7: - p.Optional = fields[6] - default: - p.Optional = strings.Join(fields[6:sepIdx-1], " ") - } + p.Optional = strings.Join(fields[6:sepIdx], " ") - // Run the filter after parsing all of the fields. + // Run the filter after parsing all fields. var skip, stop bool if filter != nil { skip, stop = filter(p) diff --git a/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go b/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go index d33ebca09..95769a76d 100644 --- a/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go +++ b/vendor/github.com/moby/sys/mountinfo/mountinfo_unsupported.go @@ -1,4 +1,5 @@ -// +build !windows,!linux,!freebsd,!openbsd freebsd,!cgo openbsd,!cgo +//go:build (!windows && !linux && !freebsd && !openbsd && !darwin) || (freebsd && !cgo) || (openbsd && !cgo) || (darwin && !cgo) +// +build !windows,!linux,!freebsd,!openbsd,!darwin freebsd,!cgo openbsd,!cgo darwin,!cgo package mountinfo diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/BUILD.bazel b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/BUILD.bazel index 492ccf55c..6164811b8 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/BUILD.bazel +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/BUILD.bazel @@ -4,8 +4,8 @@ go_library( name = "go_default_library", srcs = [ "cgroups.go", - "cgroups_unsupported.go", "file.go", + "getallpids.go", "stats.go", "utils.go", "v1_utils.go", @@ -16,18 +16,9 @@ go_library( deps = [ "//vendor/github.com/cyphar/filepath-securejoin:go_default_library", "//vendor/github.com/moby/sys/mountinfo:go_default_library", - "//vendor/github.com/pkg/errors:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/configs:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/userns:go_default_library", "//vendor/github.com/sirupsen/logrus:go_default_library", "//vendor/golang.org/x/sys/unix:go_default_library", - ] + select({ - "@io_bazel_rules_go//go/platform:android": [ - "//vendor/github.com/opencontainers/runc/libcontainer/configs:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/userns:go_default_library", - ], - "@io_bazel_rules_go//go/platform:linux": [ - "//vendor/github.com/opencontainers/runc/libcontainer/configs:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/userns:go_default_library", - ], - "//conditions:default": [], - }), + ], ) diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go index 68a346ca5..ba2b2266c 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go @@ -1,5 +1,3 @@ -// +build linux - package cgroups import ( diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups_unsupported.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups_unsupported.go deleted file mode 100644 index 278d507e2..000000000 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups_unsupported.go +++ /dev/null @@ -1,3 +0,0 @@ -// +build !linux - -package cgroups diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/devices/BUILD.bazel b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/devices/BUILD.bazel index c4f5c2c1a..5ec5ea5c5 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/devices/BUILD.bazel +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/devices/BUILD.bazel @@ -6,15 +6,5 @@ go_library( importmap = "kubevirt.io/kubevirt/vendor/github.com/opencontainers/runc/libcontainer/cgroups/devices", importpath = "github.com/opencontainers/runc/libcontainer/cgroups/devices", visibility = ["//visibility:public"], - deps = select({ - "@io_bazel_rules_go//go/platform:android": [ - "//vendor/github.com/opencontainers/runc/libcontainer/devices:go_default_library", - "//vendor/github.com/pkg/errors:go_default_library", - ], - "@io_bazel_rules_go//go/platform:linux": [ - "//vendor/github.com/opencontainers/runc/libcontainer/devices:go_default_library", - "//vendor/github.com/pkg/errors:go_default_library", - ], - "//conditions:default": [], - }), + deps = ["//vendor/github.com/opencontainers/runc/libcontainer/devices:go_default_library"], ) diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/devices/devices_emulator.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/devices/devices_emulator.go index c08477cbb..6c61ee4c0 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/devices/devices_emulator.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/devices/devices_emulator.go @@ -1,5 +1,3 @@ -// +build linux - // SPDX-License-Identifier: Apache-2.0 /* * Copyright (C) 2020 Aleksa Sarai <cyphar@cyphar.com> @@ -22,14 +20,13 @@ package devices import ( "bufio" + "fmt" "io" - "regexp" "sort" "strconv" + "strings" "github.com/opencontainers/runc/libcontainer/devices" - - "github.com/pkg/errors" ) // deviceMeta is a Rule without the Allow or Permissions fields, and no @@ -79,19 +76,21 @@ func (e *Emulator) IsAllowAll() bool { return e.IsBlacklist() && len(e.rules) == 0 } -var devicesListRegexp = regexp.MustCompile(`^([abc])\s+(\d+|\*):(\d+|\*)\s+([rwm]+)$`) - func parseLine(line string) (*deviceRule, error) { - matches := devicesListRegexp.FindStringSubmatch(line) - if matches == nil { - return nil, errors.Errorf("line doesn't match devices.list format") + // Input: node major:minor perms. + fields := strings.FieldsFunc(line, func(r rune) bool { + return r == ' ' || r == ':' + }) + if len(fields) != 4 { + return nil, fmt.Errorf("malformed devices.list rule %s", line) } + var ( rule deviceRule - node = matches[1] - major = matches[2] - minor = matches[3] - perms = matches[4] + node = fields[0] + major = fields[1] + minor = fields[2] + perms = fields[3] ) // Parse the node type. @@ -107,8 +106,7 @@ func parseLine(line string) (*deviceRule, error) { case "c": rule.meta.node = devices.CharDevice default: - // Should never happen! - return nil, errors.Errorf("unknown device type %q", node) + return nil, fmt.Errorf("unknown device type %q", node) } // Parse the major number. @@ -117,7 +115,7 @@ func parseLine(line string) (*deviceRule, error) { } else { val, err := strconv.ParseUint(major, 10, 32) if err != nil { - return nil, errors.Wrap(err, "parse major number") + return nil, fmt.Errorf("invalid major number: %w", err) } rule.meta.major = int64(val) } @@ -128,7 +126,7 @@ func parseLine(line string) (*deviceRule, error) { } else { val, err := strconv.ParseUint(minor, 10, 32) if err != nil { - return nil, errors.Wrap(err, "parse minor number") + return nil, fmt.Errorf("invalid minor number: %w", err) } rule.meta.minor = int64(val) } @@ -136,13 +134,12 @@ func parseLine(line string) (*deviceRule, error) { // Parse the access permissions. rule.perms = devices.Permissions(perms) if !rule.perms.IsValid() || rule.perms.IsEmpty() { - // Should never happen! - return nil, errors.Errorf("parse access mode: contained unknown modes or is empty: %q", perms) + return nil, fmt.Errorf("parse access mode: contained unknown modes or is empty: %q", perms) } return &rule, nil } -func (e *Emulator) addRule(rule deviceRule) error { +func (e *Emulator) addRule(rule deviceRule) error { //nolint:unparam if e.rules == nil { e.rules = make(map[deviceMeta]devices.Permissions) } @@ -180,7 +177,7 @@ func (e *Emulator) rmRule(rule deviceRule) error { // Only give an error if the set of permissions overlap. partialPerms := e.rules[partialMeta] if !partialPerms.Intersection(rule.perms).IsEmpty() { - return errors.Errorf("requested rule [%v %v] not supported by devices cgroupv1 (cannot punch hole in existing wildcard rule [%v %v])", rule.meta, rule.perms, partialMeta, partialPerms) + return fmt.Errorf("requested rule [%v %v] not supported by devices cgroupv1 (cannot punch hole in existing wildcard rule [%v %v])", rule.meta, rule.perms, partialMeta, partialPerms) } } @@ -212,9 +209,9 @@ func (e *Emulator) allow(rule *deviceRule) error { var err error if e.defaultAllow { - err = errors.Wrap(e.rmRule(*rule), "remove 'deny' exception") + err = wrapErr(e.rmRule(*rule), "unable to remove 'deny' exception") } else { - err = errors.Wrap(e.addRule(*rule), "add 'allow' exception") + err = wrapErr(e.addRule(*rule), "unable to add 'allow' exception") } return err } @@ -232,16 +229,16 @@ func (e *Emulator) deny(rule *deviceRule) error { var err error if e.defaultAllow { - err = errors.Wrap(e.addRule(*rule), "add 'deny' exception") + err = wrapErr(e.addRule(*rule), "unable to add 'deny' exception") } else { - err = errors.Wrap(e.rmRule(*rule), "remove 'allow' exception") + err = wrapErr(e.rmRule(*rule), "unable to remove 'allow' exception") } return err } func (e *Emulator) Apply(rule devices.Rule) error { if !rule.Type.CanCgroup() { - return errors.Errorf("cannot add rule [%#v] with non-cgroup type %q", rule, rule.Type) + return fmt.Errorf("cannot add rule [%#v] with non-cgroup type %q", rule, rule.Type) } innerRule := &deviceRule{ @@ -283,17 +280,17 @@ func EmulatorFromList(list io.Reader) (*Emulator, error) { line := s.Text() deviceRule, err := parseLine(line) if err != nil { - return nil, errors.Wrapf(err, "parsing line %q", line) + return nil, fmt.Errorf("error parsing line %q: %w", line, err) } // "devices.list" is an allow list. Note that this means that in // black-list mode, we have no idea what rules are in play. As a // result, we need to be very careful in Transition(). if err := e.allow(deviceRule); err != nil { - return nil, errors.Wrapf(err, "adding devices.list rule") + return nil, fmt.Errorf("error adding devices.list rule: %w", err) } } if err := s.Err(); err != nil { - return nil, errors.Wrap(err, "reading devices.list lines") + return nil, fmt.Errorf("error reading devices.list lines: %w", err) } return e, nil } @@ -305,7 +302,7 @@ func EmulatorFromList(list io.Reader) (*Emulator, error) { // necessary. // // This function is the sole reason for all of Emulator -- to allow us -// to figure out how to update a containers' cgroups without causing spurrious +// to figure out how to update a containers' cgroups without causing spurious // device errors (if possible). func (source *Emulator) Transition(target *Emulator) ([]*devices.Rule, error) { var transitionRules []*devices.Rule @@ -380,3 +377,10 @@ func (e *Emulator) Rules() ([]*devices.Rule, error) { defaultCgroup := &Emulator{defaultAllow: false} return defaultCgroup.Transition(e) } + +func wrapErr(err error, text string) error { + if err == nil { + return nil + } + return fmt.Errorf(text+": %w", err) +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/BUILD.bazel b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/BUILD.bazel index 1136626f5..d667bebaf 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/BUILD.bazel +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/BUILD.bazel @@ -11,7 +11,6 @@ go_library( "//vendor/github.com/cilium/ebpf:go_default_library", "//vendor/github.com/cilium/ebpf/asm:go_default_library", "//vendor/github.com/cilium/ebpf/link:go_default_library", - "//vendor/github.com/pkg/errors:go_default_library", "//vendor/github.com/sirupsen/logrus:go_default_library", "//vendor/golang.org/x/sys/unix:go_default_library", ], @@ -19,7 +18,6 @@ go_library( "//vendor/github.com/cilium/ebpf:go_default_library", "//vendor/github.com/cilium/ebpf/asm:go_default_library", "//vendor/github.com/cilium/ebpf/link:go_default_library", - "//vendor/github.com/pkg/errors:go_default_library", "//vendor/github.com/sirupsen/logrus:go_default_library", "//vendor/golang.org/x/sys/unix:go_default_library", ], diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter/BUILD.bazel b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter/BUILD.bazel index 5eabf95e0..9f9901a1a 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter/BUILD.bazel +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter/BUILD.bazel @@ -10,7 +10,6 @@ go_library( "//vendor/github.com/cilium/ebpf/asm:go_default_library", "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/devices:go_default_library", "//vendor/github.com/opencontainers/runc/libcontainer/devices:go_default_library", - "//vendor/github.com/pkg/errors:go_default_library", "//vendor/golang.org/x/sys/unix:go_default_library", ], ) diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter/devicefilter.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter/devicefilter.go index 96cbca391..4e69b35bc 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter/devicefilter.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter/devicefilter.go @@ -7,13 +7,14 @@ package devicefilter import ( + "errors" + "fmt" "math" "strconv" "github.com/cilium/ebpf/asm" devicesemulator "github.com/opencontainers/runc/libcontainer/cgroups/devices" "github.com/opencontainers/runc/libcontainer/devices" - "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -51,21 +52,20 @@ func DeviceFilter(rules []*devices.Rule) (asm.Instructions, string, error) { // only be one (at most) at the very start to instruct cgroupv1 to // go into allow-list mode. However we do double-check this here. if idx != 0 || rule.Allow != emu.IsBlacklist() { - return nil, "", errors.Errorf("[internal error] emulated cgroupv2 devices ruleset had bad wildcard at idx %v (%s)", idx, rule.CgroupString()) + return nil, "", fmt.Errorf("[internal error] emulated cgroupv2 devices ruleset had bad wildcard at idx %v (%s)", idx, rule.CgroupString()) } continue } if rule.Allow == p.defaultAllow { // There should be no rules which have an action equal to the // default action, the emulator removes those. - return nil, "", errors.Errorf("[internal error] emulated cgroupv2 devices ruleset had no-op rule at idx %v (%s)", idx, rule.CgroupString()) + return nil, "", fmt.Errorf("[internal error] emulated cgroupv2 devices ruleset had no-op rule at idx %v (%s)", idx, rule.CgroupString()) } if err := p.appendRule(rule); err != nil { return nil, "", err } } - insts, err := p.finalize() - return insts, license, err + return p.finalize(), license, nil } type program struct { @@ -118,13 +118,13 @@ func (p *program) appendRule(rule *devices.Rule) error { bpfType = int32(unix.BPF_DEVCG_DEV_BLOCK) default: // We do not permit 'a', nor any other types we don't know about. - return errors.Errorf("invalid type %q", string(rule.Type)) + return fmt.Errorf("invalid type %q", string(rule.Type)) } if rule.Major > math.MaxUint32 { - return errors.Errorf("invalid major %d", rule.Major) + return fmt.Errorf("invalid major %d", rule.Major) } if rule.Minor > math.MaxUint32 { - return errors.Errorf("invalid minor %d", rule.Major) + return fmt.Errorf("invalid minor %d", rule.Major) } hasMajor := rule.Major >= 0 // if not specified in OCI json, major is set to -1 hasMinor := rule.Minor >= 0 @@ -138,7 +138,7 @@ func (p *program) appendRule(rule *devices.Rule) error { case 'm': bpfAccess |= unix.BPF_DEVCG_ACC_MKNOD default: - return errors.Errorf("unknown device access %v", r) + return fmt.Errorf("unknown device access %v", r) } } // If the access is rwm, skip the check. @@ -180,7 +180,7 @@ func (p *program) appendRule(rule *devices.Rule) error { return nil } -func (p *program) finalize() (asm.Instructions, error) { +func (p *program) finalize() asm.Instructions { var v int32 if p.defaultAllow { v = 1 @@ -192,7 +192,7 @@ func (p *program) finalize() (asm.Instructions, error) { asm.Return(), ) p.blockID = -1 - return p.insts, nil + return p.insts } func acceptBlock(accept bool) asm.Instructions { diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/ebpf_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/ebpf_linux.go index 6c8de80dd..104c74a89 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/ebpf_linux.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/ebpf_linux.go @@ -1,6 +1,7 @@ package ebpf import ( + "errors" "fmt" "os" "runtime" @@ -10,7 +11,6 @@ import ( "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/link" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -134,7 +134,7 @@ func haveBpfProgReplace() bool { // not supported return } - // attach_flags test succeded. + // attach_flags test succeeded. if !errors.Is(err, unix.EBADF) { logrus.Debugf("checking for BPF_F_REPLACE: got unexpected (not EBADF or EINVAL) error: %v", err) } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/file.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/file.go index 5f6ab9fd6..0cdaf7478 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/file.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/file.go @@ -2,20 +2,27 @@ package cgroups import ( "bytes" + "errors" + "fmt" "os" + "path" + "strconv" "strings" "sync" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) // OpenFile opens a cgroup file in a given dir with given flags. -// It is supposed to be used for cgroup files only. +// It is supposed to be used for cgroup files only, and returns +// an error if the file is not a cgroup file. +// +// Arguments dir and file are joined together to form an absolute path +// to a file being opened. func OpenFile(dir, file string, flags int) (*os.File, error) { if dir == "" { - return nil, errors.Errorf("no directory specified for %s", file) + return nil, fmt.Errorf("no directory specified for %s", file) } return openFile(dir, file, flags) } @@ -43,7 +50,8 @@ func WriteFile(dir, file, data string) error { } defer fd.Close() if err := retryingWriteFile(fd, data); err != nil { - return errors.Wrapf(err, "failed to write %q", data) + // Having data in the error message helps in debugging. + return fmt.Errorf("failed to write %q: %w", data, err) } return nil } @@ -81,7 +89,7 @@ func prepareOpenat2() error { }) if err != nil { prepErr = &os.PathError{Op: "openat2", Path: cgroupfsDir, Err: err} - if err != unix.ENOSYS { + if err != unix.ENOSYS { //nolint:errorlint // unix errors are bare logrus.Warnf("falling back to securejoin: %s", prepErr) } else { logrus.Debug("openat2 not available, falling back to securejoin") @@ -107,8 +115,6 @@ func prepareOpenat2() error { return prepErr } -// OpenFile opens a cgroup file in a given dir with given flags. -// It is supposed to be used for cgroup files only. func openFile(dir, file string, flags int) (*os.File, error) { mode := os.FileMode(0) if TestMode && flags&os.O_WRONLY != 0 { @@ -116,34 +122,52 @@ func openFile(dir, file string, flags int) (*os.File, error) { flags |= os.O_TRUNC | os.O_CREATE mode = 0o600 } + path := path.Join(dir, file) if prepareOpenat2() != nil { - return openFallback(dir, file, flags, mode) + return openFallback(path, flags, mode) } - reldir := strings.TrimPrefix(dir, cgroupfsPrefix) - if len(reldir) == len(dir) { // non-standard path, old system? - return openFallback(dir, file, flags, mode) + relPath := strings.TrimPrefix(path, cgroupfsPrefix) + if len(relPath) == len(path) { // non-standard path, old system? + return openFallback(path, flags, mode) } - relname := reldir + "/" + file - fd, err := unix.Openat2(cgroupFd, relname, + fd, err := unix.Openat2(cgroupFd, relPath, &unix.OpenHow{ Resolve: resolveFlags, Flags: uint64(flags) | unix.O_CLOEXEC, Mode: uint64(mode), }) if err != nil { - return nil, &os.PathError{Op: "openat2", Path: dir + "/" + file, Err: err} + err = &os.PathError{Op: "openat2", Path: path, Err: err} + // Check if cgroupFd is still opened to cgroupfsDir + // (happens when this package is incorrectly used + // across the chroot/pivot_root/mntns boundary, or + // when /sys/fs/cgroup is remounted). + // + // TODO: if such usage will ever be common, amend this + // to reopen cgroupFd and retry openat2. + fdStr := strconv.Itoa(cgroupFd) + fdDest, _ := os.Readlink("/proc/self/fd/" + fdStr) + if fdDest != cgroupfsDir { + // Wrap the error so it is clear that cgroupFd + // is opened to an unexpected/wrong directory. + err = fmt.Errorf("cgroupFd %s unexpectedly opened to %s != %s: %w", + fdStr, fdDest, cgroupfsDir, err) + } + return nil, err } - return os.NewFile(uintptr(fd), cgroupfsPrefix+relname), nil + return os.NewFile(uintptr(fd), path), nil } var errNotCgroupfs = errors.New("not a cgroup file") -// openFallback is used when openat2(2) is not available. It checks the opened +// Can be changed by unit tests. +var openFallback = openAndCheck + +// openAndCheck is used when openat2(2) is not available. It checks the opened // file is on cgroupfs, returning an error otherwise. -func openFallback(dir, file string, flags int, mode os.FileMode) (*os.File, error) { - path := dir + "/" + file +func openAndCheck(path string, flags int, mode os.FileMode) (*os.File, error) { fd, err := os.OpenFile(path, flags, mode) if err != nil { return nil, err diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/BUILD.bazel b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/BUILD.bazel index 6ada94d56..12508e89f 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/BUILD.bazel +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/BUILD.bazel @@ -8,6 +8,7 @@ go_library( "cpuacct.go", "cpuset.go", "devices.go", + "error.go", "freezer.go", "fs.go", "hugetlb.go", @@ -15,38 +16,23 @@ go_library( "name.go", "net_cls.go", "net_prio.go", + "paths.go", "perf_event.go", "pids.go", - "unsupported.go", + "rdma.go", ], importmap = "kubevirt.io/kubevirt/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs", importpath = "github.com/opencontainers/runc/libcontainer/cgroups/fs", visibility = ["//visibility:public"], - deps = select({ - "@io_bazel_rules_go//go/platform:android": [ - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/devices:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/configs:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/devices:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/userns:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/utils:go_default_library", - "//vendor/github.com/pkg/errors:go_default_library", - "//vendor/github.com/sirupsen/logrus:go_default_library", - "//vendor/golang.org/x/sys/unix:go_default_library", - ], - "@io_bazel_rules_go//go/platform:linux": [ - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/devices:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/configs:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/devices:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/userns:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/utils:go_default_library", - "//vendor/github.com/pkg/errors:go_default_library", - "//vendor/github.com/sirupsen/logrus:go_default_library", - "//vendor/golang.org/x/sys/unix:go_default_library", - ], - "//conditions:default": [], - }), + deps = [ + "//vendor/github.com/opencontainers/runc/libcontainer/cgroups:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/devices:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/configs:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/devices:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/userns:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/utils:go_default_library", + "//vendor/github.com/sirupsen/logrus:go_default_library", + "//vendor/golang.org/x/sys/unix:go_default_library", + ], ) diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio.go index 88012a2f5..c81b6562a 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/blkio.go @@ -1,10 +1,7 @@ -// +build linux - package fs import ( "bufio" - "fmt" "os" "path/filepath" "strconv" @@ -23,8 +20,8 @@ func (s *BlkioGroup) Name() string { return "blkio" } -func (s *BlkioGroup) Apply(path string, d *cgroupData) error { - return join(path, d.pid) +func (s *BlkioGroup) Apply(path string, _ *configs.Resources, pid int) error { + return apply(path, pid) } func (s *BlkioGroup) Set(path string, r *configs.Resources) error { @@ -131,19 +128,19 @@ func getBlkioStat(dir, file string) ([]cgroups.BlkioStatEntry, error) { // skip total line continue } else { - return nil, fmt.Errorf("Invalid line found while parsing %s/%s: %s", dir, file, sc.Text()) + return nil, malformedLine(dir, file, sc.Text()) } } v, err := strconv.ParseUint(fields[0], 10, 64) if err != nil { - return nil, err + return nil, &parseError{Path: dir, File: file, Err: err} } major := v v, err = strconv.ParseUint(fields[1], 10, 64) if err != nil { - return nil, err + return nil, &parseError{Path: dir, File: file, Err: err} } minor := v @@ -155,10 +152,13 @@ func getBlkioStat(dir, file string) ([]cgroups.BlkioStatEntry, error) { } v, err = strconv.ParseUint(fields[valueField], 10, 64) if err != nil { - return nil, err + return nil, &parseError{Path: dir, File: file, Err: err} } blkioStats = append(blkioStats, cgroups.BlkioStatEntry{Major: major, Minor: minor, Op: op, Value: v}) } + if err := sc.Err(); err != nil { + return nil, &parseError{Path: dir, File: file, Err: err} + } return blkioStats, nil } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu.go index 7ca8b8a8d..6c79f899b 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpu.go @@ -1,5 +1,3 @@ -// +build linux - package fs import ( @@ -21,24 +19,19 @@ func (s *CpuGroup) Name() string { return "cpu" } -func (s *CpuGroup) Apply(path string, d *cgroupData) error { - // This might happen if we have no cpu cgroup mounted. - // Just do nothing and don't fail. - if path == "" { - return nil - } +func (s *CpuGroup) Apply(path string, r *configs.Resources, pid int) error { if err := os.MkdirAll(path, 0o755); err != nil { return err } // We should set the real-Time group scheduling settings before moving // in the process because if the process is already in SCHED_RR mode // and no RT bandwidth is set, adding it will fail. - if err := s.SetRtSched(path, d.config.Resources); err != nil { + if err := s.SetRtSched(path, r); err != nil { return err } - // Since we are not using join(), we need to place the pid - // into the procs file unlike other subsystems. - return cgroups.WriteCgroupProc(path, d.pid) + // Since we are not using apply(), we need to place the pid + // into the procs file. + return cgroups.WriteCgroupProc(path, pid) } func (s *CpuGroup) SetRtSched(path string, r *configs.Resources) error { @@ -105,7 +98,8 @@ func (s *CpuGroup) Set(path string, r *configs.Resources) error { } func (s *CpuGroup) GetStats(path string, stats *cgroups.Stats) error { - f, err := cgroups.OpenFile(path, "cpu.stat", os.O_RDONLY) + const file = "cpu.stat" + f, err := cgroups.OpenFile(path, file, os.O_RDONLY) if err != nil { if os.IsNotExist(err) { return nil @@ -118,7 +112,7 @@ func (s *CpuGroup) GetStats(path string, stats *cgroups.Stats) error { for sc.Scan() { t, v, err := fscommon.ParseKeyValue(sc.Text()) if err != nil { - return err + return &parseError{Path: path, File: file, Err: err} } switch t { case "nr_periods": diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuacct.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuacct.go index 4fbf07849..d3bd7e111 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuacct.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuacct.go @@ -1,12 +1,8 @@ -// +build linux - package fs import ( "bufio" - "fmt" "os" - "path/filepath" "strconv" "strings" @@ -38,8 +34,8 @@ func (s *CpuacctGroup) Name() string { return "cpuacct" } -func (s *CpuacctGroup) Apply(path string, d *cgroupData) error { - return join(path, d.pid) +func (s *CpuacctGroup) Apply(path string, _ *configs.Resources, pid int) error { + return apply(path, pid) } func (s *CpuacctGroup) Set(_ string, _ *configs.Resources) error { @@ -85,45 +81,43 @@ func getCpuUsageBreakdown(path string) (uint64, uint64, error) { const ( userField = "user" systemField = "system" + file = cgroupCpuacctStat ) // Expected format: // user <usage in ticks> // system <usage in ticks> - data, err := cgroups.ReadFile(path, cgroupCpuacctStat) + data, err := cgroups.ReadFile(path, file) if err != nil { return 0, 0, err } + // TODO: use strings.SplitN instead. fields := strings.Fields(data) - if len(fields) < 4 { - return 0, 0, fmt.Errorf("failure - %s is expected to have at least 4 fields", filepath.Join(path, cgroupCpuacctStat)) - } - if fields[0] != userField { - return 0, 0, fmt.Errorf("unexpected field %q in %q, expected %q", fields[0], cgroupCpuacctStat, userField) - } - if fields[2] != systemField { - return 0, 0, fmt.Errorf("unexpected field %q in %q, expected %q", fields[2], cgroupCpuacctStat, systemField) + if len(fields) < 4 || fields[0] != userField || fields[2] != systemField { + return 0, 0, malformedLine(path, file, data) } if userModeUsage, err = strconv.ParseUint(fields[1], 10, 64); err != nil { - return 0, 0, err + return 0, 0, &parseError{Path: path, File: file, Err: err} } if kernelModeUsage, err = strconv.ParseUint(fields[3], 10, 64); err != nil { - return 0, 0, err + return 0, 0, &parseError{Path: path, File: file, Err: err} } return (userModeUsage * nanosecondsInSecond) / clockTicks, (kernelModeUsage * nanosecondsInSecond) / clockTicks, nil } func getPercpuUsage(path string) ([]uint64, error) { + const file = "cpuacct.usage_percpu" percpuUsage := []uint64{} - data, err := cgroups.ReadFile(path, "cpuacct.usage_percpu") + data, err := cgroups.ReadFile(path, file) if err != nil { return percpuUsage, err } + // TODO: use strings.SplitN instead. for _, value := range strings.Fields(data) { value, err := strconv.ParseUint(value, 10, 64) if err != nil { - return percpuUsage, fmt.Errorf("Unable to convert param value to uint64: %s", err) + return percpuUsage, &parseError{Path: path, File: file, Err: err} } percpuUsage = append(percpuUsage, value) } @@ -133,16 +127,17 @@ func getPercpuUsage(path string) ([]uint64, error) { func getPercpuUsageInModes(path string) ([]uint64, []uint64, error) { usageKernelMode := []uint64{} usageUserMode := []uint64{} + const file = cgroupCpuacctUsageAll - file, err := cgroups.OpenFile(path, cgroupCpuacctUsageAll, os.O_RDONLY) + fd, err := cgroups.OpenFile(path, file, os.O_RDONLY) if os.IsNotExist(err) { return usageKernelMode, usageUserMode, nil } else if err != nil { return nil, nil, err } - defer file.Close() + defer fd.Close() - scanner := bufio.NewScanner(file) + scanner := bufio.NewScanner(fd) scanner.Scan() // skipping header line for scanner.Scan() { @@ -153,19 +148,18 @@ func getPercpuUsageInModes(path string) ([]uint64, []uint64, error) { usageInKernelMode, err := strconv.ParseUint(lineFields[kernelModeColumn], 10, 64) if err != nil { - return nil, nil, fmt.Errorf("Unable to convert CPU usage in kernel mode to uint64: %s", err) + return nil, nil, &parseError{Path: path, File: file, Err: err} } usageKernelMode = append(usageKernelMode, usageInKernelMode) usageInUserMode, err := strconv.ParseUint(lineFields[userModeColumn], 10, 64) if err != nil { - return nil, nil, fmt.Errorf("Unable to convert CPU usage in user mode to uint64: %s", err) + return nil, nil, &parseError{Path: path, File: file, Err: err} } usageUserMode = append(usageUserMode, usageInUserMode) } - if err := scanner.Err(); err != nil { - return nil, nil, fmt.Errorf("Problem in reading %s line by line, %s", cgroupCpuacctUsageAll, err) + return nil, nil, &parseError{Path: path, File: file, Err: err} } return usageKernelMode, usageUserMode, nil diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go index 58a0f0406..550baa427 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go @@ -1,19 +1,17 @@ -// +build linux - package fs import ( - "fmt" + "errors" "os" "path/filepath" "strconv" "strings" + "golang.org/x/sys/unix" + "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups/fscommon" "github.com/opencontainers/runc/libcontainer/configs" - "github.com/pkg/errors" - "golang.org/x/sys/unix" ) type CpusetGroup struct{} @@ -22,8 +20,8 @@ func (s *CpusetGroup) Name() string { return "cpuset" } -func (s *CpusetGroup) Apply(path string, d *cgroupData) error { - return s.ApplyDir(path, d.config.Resources, d.pid) +func (s *CpusetGroup) Apply(path string, r *configs.Resources, pid int) error { + return s.ApplyDir(path, r, pid) } func (s *CpusetGroup) Set(path string, r *configs.Resources) error { @@ -40,32 +38,32 @@ func (s *CpusetGroup) Set(path string, r *configs.Resources) error { return nil } -func getCpusetStat(path string, filename string) ([]uint16, error) { +func getCpusetStat(path string, file string) ([]uint16, error) { var extracted []uint16 - fileContent, err := fscommon.GetCgroupParamString(path, filename) + fileContent, err := fscommon.GetCgroupParamString(path, file) if err != nil { return extracted, err } if len(fileContent) == 0 { - return extracted, fmt.Errorf("%s found to be empty", filepath.Join(path, filename)) + return extracted, &parseError{Path: path, File: file, Err: errors.New("empty file")} } for _, s := range strings.Split(fileContent, ",") { - splitted := strings.SplitN(s, "-", 3) - switch len(splitted) { + sp := strings.SplitN(s, "-", 3) + switch len(sp) { case 3: - return extracted, fmt.Errorf("invalid values in %s", filepath.Join(path, filename)) + return extracted, &parseError{Path: path, File: file, Err: errors.New("extra dash")} case 2: - min, err := strconv.ParseUint(splitted[0], 10, 16) + min, err := strconv.ParseUint(sp[0], 10, 16) if err != nil { - return extracted, err + return extracted, &parseError{Path: path, File: file, Err: err} } - max, err := strconv.ParseUint(splitted[1], 10, 16) + max, err := strconv.ParseUint(sp[1], 10, 16) if err != nil { - return extracted, err + return extracted, &parseError{Path: path, File: file, Err: err} } if min > max { - return extracted, fmt.Errorf("invalid values in %s", filepath.Join(path, filename)) + return extracted, &parseError{Path: path, File: file, Err: errors.New("invalid values, min > max")} } for i := min; i <= max; i++ { extracted = append(extracted, uint16(i)) @@ -73,7 +71,7 @@ func getCpusetStat(path string, filename string) ([]uint16, error) { case 1: value, err := strconv.ParseUint(s, 10, 16) if err != nil { - return extracted, err + return extracted, &parseError{Path: path, File: file, Err: err} } extracted = append(extracted, uint16(value)) } @@ -168,9 +166,8 @@ func (s *CpusetGroup) ApplyDir(dir string, r *configs.Resources, pid int) error if err := s.ensureCpusAndMems(dir, r); err != nil { return err } - - // because we are not using d.join we need to place the pid into the procs file - // unlike the other subsystems + // Since we are not using apply(), we need to place the pid + // into the procs file. return cgroups.WriteCgroupProc(dir, pid) } @@ -198,7 +195,7 @@ func cpusetEnsureParent(current string) error { } // Treat non-existing directory as cgroupfs as it will be created, // and the root cpuset directory obviously exists. - if err != nil && err != unix.ENOENT { + if err != nil && err != unix.ENOENT { //nolint:errorlint // unix errors are bare return &os.PathError{Op: "statfs", Path: parent, Err: err} } @@ -224,12 +221,12 @@ func cpusetCopyIfNeeded(current, parent string) error { } if isEmptyCpuset(currentCpus) { - if err := cgroups.WriteFile(current, "cpuset.cpus", string(parentCpus)); err != nil { + if err := cgroups.WriteFile(current, "cpuset.cpus", parentCpus); err != nil { return err } } if isEmptyCpuset(currentMems) { - if err := cgroups.WriteFile(current, "cpuset.mems", string(parentMems)); err != nil { + if err := cgroups.WriteFile(current, "cpuset.mems", parentMems); err != nil { return err } } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices.go index dcf69ce13..4527a70eb 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/devices.go @@ -1,5 +1,3 @@ -// +build linux - package fs import ( @@ -15,15 +13,15 @@ import ( ) type DevicesGroup struct { - testingSkipFinalCheck bool + TestingSkipFinalCheck bool } func (s *DevicesGroup) Name() string { return "devices" } -func (s *DevicesGroup) Apply(path string, d *cgroupData) error { - if d.config.SkipDevices { +func (s *DevicesGroup) Apply(path string, r *configs.Resources, pid int) error { + if r.SkipDevices { return nil } if path == "" { @@ -31,7 +29,8 @@ func (s *DevicesGroup) Apply(path string, d *cgroupData) error { // is a hard requirement for container's security. return errSubsystemDoesNotExist } - return join(path, d.pid) + + return apply(path, pid) } func loadEmulator(path string) (*cgroupdevices.Emulator, error) { @@ -91,7 +90,7 @@ func (s *DevicesGroup) Set(path string, r *configs.Resources) error { // // This safety-check is skipped for the unit tests because we cannot // currently mock devices.list correctly. - if !s.testingSkipFinalCheck { + if !s.TestingSkipFinalCheck { currentAfter, err := loadEmulator(path) if err != nil { return err diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/error.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/error.go new file mode 100644 index 000000000..f2ab6f130 --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/error.go @@ -0,0 +1,15 @@ +package fs + +import ( + "fmt" + + "github.com/opencontainers/runc/libcontainer/cgroups/fscommon" +) + +type parseError = fscommon.ParseError + +// malformedLine is used by all cgroupfs file parsers that expect a line +// in a particular format but get some garbage instead. +func malformedLine(path, file, line string) error { + return &parseError{Path: path, File: file, Err: fmt.Errorf("malformed line: %s", line)} +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer.go index 4baa2798a..987f1bf5e 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/freezer.go @@ -1,5 +1,3 @@ -// +build linux - package fs import ( @@ -21,8 +19,8 @@ func (s *FreezerGroup) Name() string { return "freezer" } -func (s *FreezerGroup) Apply(path string, d *cgroupData) error { - return join(path, d.pid) +func (s *FreezerGroup) Apply(path string, _ *configs.Resources, pid int) error { + return apply(path, pid) } func (s *FreezerGroup) Set(path string, r *configs.Resources) (Err error) { diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/fs.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/fs.go index 777b94f03..fb4fcc7f7 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/fs.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/fs.go @@ -1,165 +1,86 @@ -// +build linux - package fs import ( + "errors" "fmt" "os" - "path/filepath" "sync" + "golang.org/x/sys/unix" + "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups/fscommon" "github.com/opencontainers/runc/libcontainer/configs" - libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils" - "github.com/pkg/errors" - "golang.org/x/sys/unix" ) -var ( - subsystems = []subsystem{ - &CpusetGroup{}, - &DevicesGroup{}, - &MemoryGroup{}, - &CpuGroup{}, - &CpuacctGroup{}, - &PidsGroup{}, - &BlkioGroup{}, - &HugetlbGroup{}, - &NetClsGroup{}, - &NetPrioGroup{}, - &PerfEventGroup{}, - &FreezerGroup{}, - &NameGroup{GroupName: "name=systemd", Join: true}, - } - HugePageSizes, _ = cgroups.GetHugePageSize() -) +var subsystems = []subsystem{ + &CpusetGroup{}, + &DevicesGroup{}, + &MemoryGroup{}, + &CpuGroup{}, + &CpuacctGroup{}, + &PidsGroup{}, + &BlkioGroup{}, + &HugetlbGroup{}, + &NetClsGroup{}, + &NetPrioGroup{}, + &PerfEventGroup{}, + &FreezerGroup{}, + &RdmaGroup{}, + &NameGroup{GroupName: "name=systemd", Join: true}, +} var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist") +func init() { + // If using cgroups-hybrid mode then add a "" controller indicating + // it should join the cgroups v2. + if cgroups.IsCgroup2HybridMode() { + subsystems = append(subsystems, &NameGroup{GroupName: "", Join: true}) + } +} + type subsystem interface { // Name returns the name of the subsystem. Name() string - // Returns the stats, as 'stats', corresponding to the cgroup under 'path'. + // GetStats fills in the stats for the subsystem. GetStats(path string, stats *cgroups.Stats) error - // Creates and joins the cgroup represented by 'cgroupData'. - Apply(path string, c *cgroupData) error + // Apply creates and joins a cgroup, adding pid into it. Some + // subsystems use resources to pre-configure the cgroup parents + // before creating or joining it. + Apply(path string, r *configs.Resources, pid int) error // Set sets the cgroup resources. Set(path string, r *configs.Resources) error } type manager struct { - mu sync.Mutex - cgroups *configs.Cgroup - rootless bool // ignore permission-related errors - paths map[string]string -} - -func NewManager(cg *configs.Cgroup, paths map[string]string, rootless bool) cgroups.Manager { - return &manager{ - cgroups: cg, - paths: paths, - rootless: rootless, - } -} - -// The absolute path to the root of the cgroup hierarchies. -var ( - cgroupRootLock sync.Mutex - cgroupRoot string -) - -const defaultCgroupRoot = "/sys/fs/cgroup" - -func tryDefaultCgroupRoot() string { - var st, pst unix.Stat_t - - // (1) it should be a directory... - err := unix.Lstat(defaultCgroupRoot, &st) - if err != nil || st.Mode&unix.S_IFDIR == 0 { - return "" - } - - // (2) ... and a mount point ... - err = unix.Lstat(filepath.Dir(defaultCgroupRoot), &pst) - if err != nil { - return "" - } - - if st.Dev == pst.Dev { - // parent dir has the same dev -- not a mount point - return "" - } - - // (3) ... of 'tmpfs' fs type. - var fst unix.Statfs_t - err = unix.Statfs(defaultCgroupRoot, &fst) - if err != nil || fst.Type != unix.TMPFS_MAGIC { - return "" - } - - // (4) it should have at least 1 entry ... - dir, err := os.Open(defaultCgroupRoot) - if err != nil { - return "" - } - names, err := dir.Readdirnames(1) - if err != nil { - return "" - } - if len(names) < 1 { - return "" - } - // ... which is a cgroup mount point. - err = unix.Statfs(filepath.Join(defaultCgroupRoot, names[0]), &fst) - if err != nil || fst.Type != unix.CGROUP_SUPER_MAGIC { - return "" - } - - return defaultCgroupRoot + mu sync.Mutex + cgroups *configs.Cgroup + paths map[string]string } -// Gets the cgroupRoot. -func getCgroupRoot() (string, error) { - cgroupRootLock.Lock() - defer cgroupRootLock.Unlock() - - if cgroupRoot != "" { - return cgroupRoot, nil - } - - // fast path - cgroupRoot = tryDefaultCgroupRoot() - if cgroupRoot != "" { - return cgroupRoot, nil +func NewManager(cg *configs.Cgroup, paths map[string]string) (cgroups.Manager, error) { + // Some v1 controllers (cpu, cpuset, and devices) expect + // cgroups.Resources to not be nil in Apply. + if cg.Resources == nil { + return nil, errors.New("cgroup v1 manager needs configs.Resources to be set during manager creation") } - - // slow path: parse mountinfo - mi, err := cgroups.GetCgroupMounts(false) - if err != nil { - return "", err - } - if len(mi) < 1 { - return "", errors.New("no cgroup mount found in mountinfo") + if cg.Resources.Unified != nil { + return nil, cgroups.ErrV1NoUnified } - // Get the first cgroup mount (e.g. "/sys/fs/cgroup/memory"), - // use its parent directory. - root := filepath.Dir(mi[0].Mountpoint) - - if _, err := os.Stat(root); err != nil { - return "", err + if paths == nil { + var err error + paths, err = initPaths(cg) + if err != nil { + return nil, err + } } - cgroupRoot = root - return cgroupRoot, nil -} - -type cgroupData struct { - root string - innerPath string - config *configs.Cgroup - pid int + return &manager{ + cgroups: cg, + paths: paths, + }, nil } // isIgnorableError returns whether err is a permission error (in the loose @@ -171,8 +92,6 @@ func isIgnorableError(rootless bool, err error) bool { if !rootless { return false } - // TODO: rm errors.Cause once we switch to %w everywhere - err = errors.Cause(err) // Is it an ordinary EPERM? if errors.Is(err, os.ErrPermission) { return true @@ -186,56 +105,30 @@ func isIgnorableError(rootless bool, err error) bool { } func (m *manager) Apply(pid int) (err error) { - if m.cgroups == nil { - return nil - } m.mu.Lock() defer m.mu.Unlock() c := m.cgroups - if c.Resources.Unified != nil { - return cgroups.ErrV1NoUnified - } - - m.paths = make(map[string]string) - if c.Paths != nil { - cgMap, err := cgroups.ParseCgroupFile("/proc/self/cgroup") - if err != nil { - return err - } - for name, path := range c.Paths { - // XXX(kolyshkin@): why this check is needed? - if _, ok := cgMap[name]; ok { - m.paths[name] = path - } - } - return cgroups.EnterPid(m.paths, pid) - } - - d, err := getCgroupData(m.cgroups, pid) - if err != nil { - return err - } for _, sys := range subsystems { - p, err := d.path(sys.Name()) - if err != nil { - // The non-presence of the devices subsystem is - // considered fatal for security reasons. - if cgroups.IsNotFound(err) && (c.SkipDevices || sys.Name() != "devices") { - continue - } - return err + name := sys.Name() + p, ok := m.paths[name] + if !ok { + continue } - m.paths[sys.Name()] = p - if err := sys.Apply(p, d); err != nil { + if err := sys.Apply(p, c.Resources, pid); err != nil { // In the case of rootless (including euid=0 in userns), where an // explicit cgroup path hasn't been set, we don't bail on error in - // case of permission problems. Cases where limits have been set - // (and we couldn't create our own cgroup) are handled by Set. - if isIgnorableError(m.rootless, err) && m.cgroups.Path == "" { - delete(m.paths, sys.Name()) + // case of permission problems here, but do delete the path from + // the m.paths map, since it is either non-existent and could not + // be created, or the pid could not be added to it. + // + // Cases where limits for the subsystem have been set are handled + // later by Set, which fails with a friendly error (see + // if path == "" in Set). + if isIgnorableError(c.Rootless, err) && c.Path == "" { + delete(m.paths, name) continue } return err @@ -246,9 +139,6 @@ func (m *manager) Apply(pid int) (err error) { } func (m *manager) Destroy() error { - if m.cgroups == nil || m.cgroups.Paths != nil { - return nil - } m.mu.Lock() defer m.mu.Unlock() return cgroups.RemovePaths(m.paths) @@ -281,11 +171,6 @@ func (m *manager) Set(r *configs.Resources) error { return nil } - // If Paths are set, then we are just joining cgroups paths - // and there is no need to set any values. - if m.cgroups != nil && m.cgroups.Paths != nil { - return nil - } if r.Unified != nil { return cgroups.ErrV1NoUnified } @@ -295,10 +180,11 @@ func (m *manager) Set(r *configs.Resources) error { for _, sys := range subsystems { path := m.paths[sys.Name()] if err := sys.Set(path, r); err != nil { - if m.rootless && sys.Name() == "devices" { + // When rootless is true, errors from the device subsystem + // are ignored, as it is really not expected to work. + if m.cgroups.Rootless && sys.Name() == "devices" { continue } - // When m.rootless is true, errors from the device subsystem are ignored because it is really not expected to work. // However, errors from other subsystems are not ignored. // see @test "runc create (rootless + limits + no cgrouppath + no permission) fails with informative error" if path == "" { @@ -317,7 +203,7 @@ func (m *manager) Set(r *configs.Resources) error { // provided func (m *manager) Freeze(state configs.FreezerState) error { path := m.Path("freezer") - if m.cgroups == nil || path == "" { + if path == "" { return errors.New("cannot toggle freezer: cgroups not configured for container") } @@ -339,68 +225,6 @@ func (m *manager) GetAllPids() ([]int, error) { return cgroups.GetAllPids(m.Path("devices")) } -func getCgroupData(c *configs.Cgroup, pid int) (*cgroupData, error) { - root, err := getCgroupRoot() - if err != nil { - return nil, err - } - - if (c.Name != "" || c.Parent != "") && c.Path != "" { - return nil, errors.New("cgroup: either Path or Name and Parent should be used") - } - - // XXX: Do not remove this code. Path safety is important! -- cyphar - cgPath := libcontainerUtils.CleanPath(c.Path) - cgParent := libcontainerUtils.CleanPath(c.Parent) - cgName := libcontainerUtils.CleanPath(c.Name) - - innerPath := cgPath - if innerPath == "" { - innerPath = filepath.Join(cgParent, cgName) - } - - return &cgroupData{ - root: root, - innerPath: innerPath, - config: c, - pid: pid, - }, nil -} - -func (raw *cgroupData) path(subsystem string) (string, error) { - // If the cgroup name/path is absolute do not look relative to the cgroup of the init process. - if filepath.IsAbs(raw.innerPath) { - mnt, err := cgroups.FindCgroupMountpoint(raw.root, subsystem) - // If we didn't mount the subsystem, there is no point we make the path. - if err != nil { - return "", err - } - - // Sometimes subsystems can be mounted together as 'cpu,cpuacct'. - return filepath.Join(raw.root, filepath.Base(mnt), raw.innerPath), nil - } - - // Use GetOwnCgroupPath instead of GetInitCgroupPath, because the creating - // process could in container and shared pid namespace with host, and - // /proc/1/cgroup could point to whole other world of cgroups. - parentPath, err := cgroups.GetOwnCgroupPath(subsystem) - if err != nil { - return "", err - } - - return filepath.Join(parentPath, raw.innerPath), nil -} - -func join(path string, pid int) error { - if path == "" { - return nil - } - if err := os.MkdirAll(path, 0o755); err != nil { - return err - } - return cgroups.WriteCgroupProc(path, pid) -} - func (m *manager) GetPaths() map[string]string { m.mu.Lock() defer m.mu.Unlock() @@ -432,7 +256,7 @@ func OOMKillCount(path string) (uint64, error) { func (m *manager) OOMKillCount() (uint64, error) { c, err := OOMKillCount(m.Path("memory")) // Ignore ENOENT when rootless as it couldn't create cgroup. - if err != nil && m.rootless && os.IsNotExist(err) { + if err != nil && m.cgroups.Rootless && os.IsNotExist(err) { err = nil } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb.go index 3cafc5399..8ddd6fdd8 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/hugetlb.go @@ -1,9 +1,6 @@ -// +build linux - package fs import ( - "fmt" "strconv" "github.com/opencontainers/runc/libcontainer/cgroups" @@ -17,8 +14,8 @@ func (s *HugetlbGroup) Name() string { return "hugetlb" } -func (s *HugetlbGroup) Apply(path string, d *cgroupData) error { - return join(path, d.pid) +func (s *HugetlbGroup) Apply(path string, _ *configs.Resources, pid int) error { + return apply(path, pid) } func (s *HugetlbGroup) Set(path string, r *configs.Resources) error { @@ -32,29 +29,29 @@ func (s *HugetlbGroup) Set(path string, r *configs.Resources) error { } func (s *HugetlbGroup) GetStats(path string, stats *cgroups.Stats) error { - hugetlbStats := cgroups.HugetlbStats{} if !cgroups.PathExists(path) { return nil } - for _, pageSize := range HugePageSizes { + hugetlbStats := cgroups.HugetlbStats{} + for _, pageSize := range cgroups.HugePageSizes() { usage := "hugetlb." + pageSize + ".usage_in_bytes" value, err := fscommon.GetCgroupParamUint(path, usage) if err != nil { - return fmt.Errorf("failed to parse %s - %v", usage, err) + return err } hugetlbStats.Usage = value maxUsage := "hugetlb." + pageSize + ".max_usage_in_bytes" value, err = fscommon.GetCgroupParamUint(path, maxUsage) if err != nil { - return fmt.Errorf("failed to parse %s - %v", maxUsage, err) + return err } hugetlbStats.MaxUsage = value failcnt := "hugetlb." + pageSize + ".failcnt" value, err = fscommon.GetCgroupParamUint(path, failcnt) if err != nil { - return fmt.Errorf("failed to parse %s - %v", failcnt, err) + return err } hugetlbStats.Failcnt = value diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go index 33946726f..b7c75f941 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go @@ -1,9 +1,8 @@ -// +build linux - package fs import ( "bufio" + "errors" "fmt" "math" "os" @@ -11,11 +10,11 @@ import ( "strconv" "strings" + "golang.org/x/sys/unix" + "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups/fscommon" "github.com/opencontainers/runc/libcontainer/configs" - "github.com/pkg/errors" - "golang.org/x/sys/unix" ) const ( @@ -31,8 +30,8 @@ func (s *MemoryGroup) Name() string { return "memory" } -func (s *MemoryGroup) Apply(path string, d *cgroupData) (err error) { - return join(path, d.pid) +func (s *MemoryGroup) Apply(path string, _ *configs.Resources, pid int) error { + return apply(path, pid) } func setMemory(path string, val int64) error { @@ -56,7 +55,7 @@ func setMemory(path string, val int64) error { return err } - return errors.Errorf("unable to set memory limit to %d (current usage: %d, peak usage: %d)", val, usage, max) + return fmt.Errorf("unable to set memory limit to %d (current usage: %d, peak usage: %d)", val, usage, max) } func setSwap(path string, val int64) error { @@ -134,15 +133,15 @@ func (s *MemoryGroup) Set(path string, r *configs.Resources) error { return err } } else { - return fmt.Errorf("invalid value:%d. valid memory swappiness range is 0-100", *r.MemorySwappiness) + return fmt.Errorf("invalid memory swappiness value: %d (valid range is 0-100)", *r.MemorySwappiness) } return nil } func (s *MemoryGroup) GetStats(path string, stats *cgroups.Stats) error { - // Set stats from memory.stat. - statsFile, err := cgroups.OpenFile(path, "memory.stat", os.O_RDONLY) + const file = "memory.stat" + statsFile, err := cgroups.OpenFile(path, file, os.O_RDONLY) if err != nil { if os.IsNotExist(err) { return nil @@ -155,7 +154,7 @@ func (s *MemoryGroup) GetStats(path string, stats *cgroups.Stats) error { for sc.Scan() { t, v, err := fscommon.ParseKeyValue(sc.Text()) if err != nil { - return fmt.Errorf("failed to parse memory.stat (%q) - %v", sc.Text(), err) + return &parseError{Path: path, File: file, Err: err} } stats.MemoryStats.Stats[t] = v } @@ -220,42 +219,42 @@ func getMemoryData(path, name string) (cgroups.MemoryData, error) { // are optional in the kernel. return cgroups.MemoryData{}, nil } - return cgroups.MemoryData{}, fmt.Errorf("failed to parse %s - %v", usage, err) + return cgroups.MemoryData{}, err } memoryData.Usage = value value, err = fscommon.GetCgroupParamUint(path, maxUsage) if err != nil { - return cgroups.MemoryData{}, fmt.Errorf("failed to parse %s - %v", maxUsage, err) + return cgroups.MemoryData{}, err } memoryData.MaxUsage = value value, err = fscommon.GetCgroupParamUint(path, failcnt) if err != nil { - return cgroups.MemoryData{}, fmt.Errorf("failed to parse %s - %v", failcnt, err) + return cgroups.MemoryData{}, err } memoryData.Failcnt = value value, err = fscommon.GetCgroupParamUint(path, limit) if err != nil { - return cgroups.MemoryData{}, fmt.Errorf("failed to parse %s - %v", limit, err) + return cgroups.MemoryData{}, err } memoryData.Limit = value return memoryData, nil } -func getPageUsageByNUMA(cgroupPath string) (cgroups.PageUsageByNUMA, error) { +func getPageUsageByNUMA(path string) (cgroups.PageUsageByNUMA, error) { const ( maxColumns = math.MaxUint8 + 1 - filename = "memory.numa_stat" + file = "memory.numa_stat" ) stats := cgroups.PageUsageByNUMA{} - file, err := cgroups.OpenFile(cgroupPath, filename, os.O_RDONLY) + fd, err := cgroups.OpenFile(path, file, os.O_RDONLY) if os.IsNotExist(err) { return stats, nil } else if err != nil { return stats, err } - defer file.Close() + defer fd.Close() // File format is documented in linux/Documentation/cgroup-v1/memory.txt // and it looks like this: @@ -266,7 +265,7 @@ func getPageUsageByNUMA(cgroupPath string) (cgroups.PageUsageByNUMA, error) { // unevictable=<total anon pages> N0=<node 0 pages> N1=<node 1 pages> ... // hierarchical_<counter>=<counter pages> N0=<node 0 pages> N1=<node 1 pages> ... - scanner := bufio.NewScanner(file) + scanner := bufio.NewScanner(fd) for scanner.Scan() { var field *cgroups.PageStats @@ -284,8 +283,7 @@ func getPageUsageByNUMA(cgroupPath string) (cgroups.PageUsageByNUMA, error) { } else { // The first column was already validated, // so be strict to the rest. - return stats, fmt.Errorf("malformed line %q in %s", - line, filename) + return stats, malformedLine(path, file, line) } } key, val := byNode[0], byNode[1] @@ -296,24 +294,23 @@ func getPageUsageByNUMA(cgroupPath string) (cgroups.PageUsageByNUMA, error) { } field.Total, err = strconv.ParseUint(val, 0, 64) if err != nil { - return stats, err + return stats, &parseError{Path: path, File: file, Err: err} } field.Nodes = map[uint8]uint64{} } else { // Subsequent columns: key is N<id>, val is usage. if len(key) < 2 || key[0] != 'N' { // This is definitely an error. - return stats, fmt.Errorf("malformed line %q in %s", - line, filename) + return stats, malformedLine(path, file, line) } n, err := strconv.ParseUint(key[1:], 10, 8) if err != nil { - return cgroups.PageUsageByNUMA{}, err + return stats, &parseError{Path: path, File: file, Err: err} } usage, err := strconv.ParseUint(val, 10, 64) if err != nil { - return cgroups.PageUsageByNUMA{}, err + return stats, &parseError{Path: path, File: file, Err: err} } field.Nodes[uint8(n)] = usage @@ -321,9 +318,8 @@ func getPageUsageByNUMA(cgroupPath string) (cgroups.PageUsageByNUMA, error) { } } - err = scanner.Err() - if err != nil { - return cgroups.PageUsageByNUMA{}, err + if err := scanner.Err(); err != nil { + return cgroups.PageUsageByNUMA{}, &parseError{Path: path, File: file, Err: err} } return stats, nil diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/name.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/name.go index 94a94b5e8..b8d5d849c 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/name.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/name.go @@ -1,5 +1,3 @@ -// +build linux - package fs import ( @@ -16,10 +14,10 @@ func (s *NameGroup) Name() string { return s.GroupName } -func (s *NameGroup) Apply(path string, d *cgroupData) error { +func (s *NameGroup) Apply(path string, _ *configs.Resources, pid int) error { if s.Join { - // ignore errors if the named cgroup does not exist - _ = join(path, d.pid) + // Ignore errors if the named cgroup does not exist. + _ = apply(path, pid) } return nil } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls.go index f2617aa44..abfd09ce8 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_cls.go @@ -1,5 +1,3 @@ -// +build linux - package fs import ( @@ -15,8 +13,8 @@ func (s *NetClsGroup) Name() string { return "net_cls" } -func (s *NetClsGroup) Apply(path string, d *cgroupData) error { - return join(path, d.pid) +func (s *NetClsGroup) Apply(path string, _ *configs.Resources, pid int) error { + return apply(path, pid) } func (s *NetClsGroup) Set(path string, r *configs.Resources) error { diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio.go index d0ac5e66b..da74d3779 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/net_prio.go @@ -1,5 +1,3 @@ -// +build linux - package fs import ( @@ -13,8 +11,8 @@ func (s *NetPrioGroup) Name() string { return "net_prio" } -func (s *NetPrioGroup) Apply(path string, d *cgroupData) error { - return join(path, d.pid) +func (s *NetPrioGroup) Apply(path string, _ *configs.Resources, pid int) error { + return apply(path, pid) } func (s *NetPrioGroup) Set(path string, r *configs.Resources) error { diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/paths.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/paths.go new file mode 100644 index 000000000..1092331b2 --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/paths.go @@ -0,0 +1,186 @@ +package fs + +import ( + "errors" + "os" + "path/filepath" + "sync" + + "golang.org/x/sys/unix" + + "github.com/opencontainers/runc/libcontainer/cgroups" + "github.com/opencontainers/runc/libcontainer/configs" + "github.com/opencontainers/runc/libcontainer/utils" +) + +// The absolute path to the root of the cgroup hierarchies. +var ( + cgroupRootLock sync.Mutex + cgroupRoot string +) + +const defaultCgroupRoot = "/sys/fs/cgroup" + +func initPaths(cg *configs.Cgroup) (map[string]string, error) { + root, err := rootPath() + if err != nil { + return nil, err + } + + inner, err := innerPath(cg) + if err != nil { + return nil, err + } + + paths := make(map[string]string) + for _, sys := range subsystems { + name := sys.Name() + path, err := subsysPath(root, inner, name) + if err != nil { + // The non-presence of the devices subsystem + // is considered fatal for security reasons. + if cgroups.IsNotFound(err) && (cg.SkipDevices || name != "devices") { + continue + } + + return nil, err + } + paths[name] = path + } + + return paths, nil +} + +func tryDefaultCgroupRoot() string { + var st, pst unix.Stat_t + + // (1) it should be a directory... + err := unix.Lstat(defaultCgroupRoot, &st) + if err != nil || st.Mode&unix.S_IFDIR == 0 { + return "" + } + + // (2) ... and a mount point ... + err = unix.Lstat(filepath.Dir(defaultCgroupRoot), &pst) + if err != nil { + return "" + } + + if st.Dev == pst.Dev { + // parent dir has the same dev -- not a mount point + return "" + } + + // (3) ... of 'tmpfs' fs type. + var fst unix.Statfs_t + err = unix.Statfs(defaultCgroupRoot, &fst) + if err != nil || fst.Type != unix.TMPFS_MAGIC { + return "" + } + + // (4) it should have at least 1 entry ... + dir, err := os.Open(defaultCgroupRoot) + if err != nil { + return "" + } + names, err := dir.Readdirnames(1) + if err != nil { + return "" + } + if len(names) < 1 { + return "" + } + // ... which is a cgroup mount point. + err = unix.Statfs(filepath.Join(defaultCgroupRoot, names[0]), &fst) + if err != nil || fst.Type != unix.CGROUP_SUPER_MAGIC { + return "" + } + + return defaultCgroupRoot +} + +// rootPath finds and returns path to the root of the cgroup hierarchies. +func rootPath() (string, error) { + cgroupRootLock.Lock() + defer cgroupRootLock.Unlock() + + if cgroupRoot != "" { + return cgroupRoot, nil + } + + // fast path + cgroupRoot = tryDefaultCgroupRoot() + if cgroupRoot != "" { + return cgroupRoot, nil + } + + // slow path: parse mountinfo + mi, err := cgroups.GetCgroupMounts(false) + if err != nil { + return "", err + } + if len(mi) < 1 { + return "", errors.New("no cgroup mount found in mountinfo") + } + + // Get the first cgroup mount (e.g. "/sys/fs/cgroup/memory"), + // use its parent directory. + root := filepath.Dir(mi[0].Mountpoint) + + if _, err := os.Stat(root); err != nil { + return "", err + } + + cgroupRoot = root + return cgroupRoot, nil +} + +func innerPath(c *configs.Cgroup) (string, error) { + if (c.Name != "" || c.Parent != "") && c.Path != "" { + return "", errors.New("cgroup: either Path or Name and Parent should be used") + } + + // XXX: Do not remove CleanPath. Path safety is important! -- cyphar + innerPath := utils.CleanPath(c.Path) + if innerPath == "" { + cgParent := utils.CleanPath(c.Parent) + cgName := utils.CleanPath(c.Name) + innerPath = filepath.Join(cgParent, cgName) + } + + return innerPath, nil +} + +func subsysPath(root, inner, subsystem string) (string, error) { + // If the cgroup name/path is absolute do not look relative to the cgroup of the init process. + if filepath.IsAbs(inner) { + mnt, err := cgroups.FindCgroupMountpoint(root, subsystem) + // If we didn't mount the subsystem, there is no point we make the path. + if err != nil { + return "", err + } + + // Sometimes subsystems can be mounted together as 'cpu,cpuacct'. + return filepath.Join(root, filepath.Base(mnt), inner), nil + } + + // Use GetOwnCgroupPath instead of GetInitCgroupPath, because the creating + // process could in container and shared pid namespace with host, and + // /proc/1/cgroup could point to whole other world of cgroups. + parentPath, err := cgroups.GetOwnCgroupPath(subsystem) + if err != nil { + return "", err + } + + return filepath.Join(parentPath, inner), nil +} + +func apply(path string, pid int) error { + if path == "" { + return nil + } + if err := os.MkdirAll(path, 0o755); err != nil { + return err + } + return cgroups.WriteCgroupProc(path, pid) +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/perf_event.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/perf_event.go index 1a306fbe3..b86955c8f 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/perf_event.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/perf_event.go @@ -1,5 +1,3 @@ -// +build linux - package fs import ( @@ -13,8 +11,8 @@ func (s *PerfEventGroup) Name() string { return "perf_event" } -func (s *PerfEventGroup) Apply(path string, d *cgroupData) error { - return join(path, d.pid) +func (s *PerfEventGroup) Apply(path string, _ *configs.Resources, pid int) error { + return apply(path, pid) } func (s *PerfEventGroup) Set(_ string, _ *configs.Resources) error { diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids.go index 1b08433c4..1f13532a5 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/pids.go @@ -1,10 +1,7 @@ -// +build linux - package fs import ( - "fmt" - "path/filepath" + "math" "strconv" "github.com/opencontainers/runc/libcontainer/cgroups" @@ -18,8 +15,8 @@ func (s *PidsGroup) Name() string { return "pids" } -func (s *PidsGroup) Apply(path string, d *cgroupData) error { - return join(path, d.pid) +func (s *PidsGroup) Apply(path string, _ *configs.Resources, pid int) error { + return apply(path, pid) } func (s *PidsGroup) Set(path string, r *configs.Resources) error { @@ -45,21 +42,18 @@ func (s *PidsGroup) GetStats(path string, stats *cgroups.Stats) error { } current, err := fscommon.GetCgroupParamUint(path, "pids.current") if err != nil { - return fmt.Errorf("failed to parse pids.current - %s", err) + return err } - maxString, err := fscommon.GetCgroupParamString(path, "pids.max") + max, err := fscommon.GetCgroupParamUint(path, "pids.max") if err != nil { - return fmt.Errorf("failed to parse pids.max - %s", err) + return err } - - // Default if pids.max == "max" is 0 -- which represents "no limit". - var max uint64 - if maxString != "max" { - max, err = fscommon.ParseUint(maxString, 10, 64) - if err != nil { - return fmt.Errorf("failed to parse pids.max - unable to parse %q as a uint from Cgroup file %q", maxString, filepath.Join(path, "pids.max")) - } + // If no limit is set, read from pids.max returns "max", which is + // converted to MaxUint64 by GetCgroupParamUint. Historically, we + // represent "no limit" for pids as 0, thus this conversion. + if max == math.MaxUint64 { + max = 0 } stats.PidsStats.Current = current diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/rdma.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/rdma.go new file mode 100644 index 000000000..5bbe0f35f --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/rdma.go @@ -0,0 +1,25 @@ +package fs + +import ( + "github.com/opencontainers/runc/libcontainer/cgroups" + "github.com/opencontainers/runc/libcontainer/cgroups/fscommon" + "github.com/opencontainers/runc/libcontainer/configs" +) + +type RdmaGroup struct{} + +func (s *RdmaGroup) Name() string { + return "rdma" +} + +func (s *RdmaGroup) Apply(path string, _ *configs.Resources, pid int) error { + return apply(path, pid) +} + +func (s *RdmaGroup) Set(path string, r *configs.Resources) error { + return fscommon.RdmaSet(path, r) +} + +func (s *RdmaGroup) GetStats(path string, stats *cgroups.Stats) error { + return fscommon.RdmaGetStats(path, stats) +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/unsupported.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/unsupported.go deleted file mode 100644 index 3ef9e0315..000000000 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/unsupported.go +++ /dev/null @@ -1,3 +0,0 @@ -// +build !linux - -package fs diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/BUILD.bazel b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/BUILD.bazel index 8034a53d0..f800d9340 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/BUILD.bazel +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/BUILD.bazel @@ -20,28 +20,14 @@ go_library( visibility = ["//visibility:public"], deps = [ "//vendor/github.com/opencontainers/runc/libcontainer/cgroups:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon:go_default_library", "//vendor/github.com/opencontainers/runc/libcontainer/configs:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/devices:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/userns:go_default_library", "//vendor/github.com/opencontainers/runc/libcontainer/utils:go_default_library", - "//vendor/github.com/pkg/errors:go_default_library", - ] + select({ - "@io_bazel_rules_go//go/platform:android": [ - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/devices:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/userns:go_default_library", - "//vendor/github.com/sirupsen/logrus:go_default_library", - "//vendor/golang.org/x/sys/unix:go_default_library", - ], - "@io_bazel_rules_go//go/platform:linux": [ - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/devices:go_default_library", - "//vendor/github.com/opencontainers/runc/libcontainer/userns:go_default_library", - "//vendor/github.com/sirupsen/logrus:go_default_library", - "//vendor/golang.org/x/sys/unix:go_default_library", - ], - "//conditions:default": [], - }), + "//vendor/github.com/sirupsen/logrus:go_default_library", + "//vendor/golang.org/x/sys/unix:go_default_library", + ], ) diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/cpu.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/cpu.go index 25c47c961..bbbae4d58 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/cpu.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/cpu.go @@ -1,5 +1,3 @@ -// +build linux - package fs2 import ( @@ -49,7 +47,8 @@ func setCpu(dirPath string, r *configs.Resources) error { } func statCpu(dirPath string, stats *cgroups.Stats) error { - f, err := cgroups.OpenFile(dirPath, "cpu.stat", os.O_RDONLY) + const file = "cpu.stat" + f, err := cgroups.OpenFile(dirPath, file, os.O_RDONLY) if err != nil { return err } @@ -59,7 +58,7 @@ func statCpu(dirPath string, stats *cgroups.Stats) error { for sc.Scan() { t, v, err := fscommon.ParseKeyValue(sc.Text()) if err != nil { - return err + return &parseError{Path: dirPath, File: file, Err: err} } switch t { case "usage_usec": @@ -81,5 +80,8 @@ func statCpu(dirPath string, stats *cgroups.Stats) error { stats.CpuStats.ThrottlingData.ThrottledTime = v * 1000 } } + if err := sc.Err(); err != nil { + return &parseError{Path: dirPath, File: file, Err: err} + } return nil } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/cpuset.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/cpuset.go index da29d7f2b..16c45bad8 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/cpuset.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/cpuset.go @@ -1,5 +1,3 @@ -// +build linux - package fs2 import ( diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/defaultpath.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/defaultpath.go index ba81ce0b4..9c949c91f 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/defaultpath.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/defaultpath.go @@ -18,41 +18,37 @@ package fs2 import ( "bufio" + "errors" + "fmt" "io" "os" "path/filepath" "strings" "github.com/opencontainers/runc/libcontainer/configs" - libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils" - "github.com/pkg/errors" + "github.com/opencontainers/runc/libcontainer/utils" ) const UnifiedMountpoint = "/sys/fs/cgroup" func defaultDirPath(c *configs.Cgroup) (string, error) { if (c.Name != "" || c.Parent != "") && c.Path != "" { - return "", errors.Errorf("cgroup: either Path or Name and Parent should be used, got %+v", c) + return "", fmt.Errorf("cgroup: either Path or Name and Parent should be used, got %+v", c) } - if len(c.Paths) != 0 { - // never set by specconv - return "", errors.Errorf("cgroup: Paths is unsupported, use Path, got %+v", c) - } - - // XXX: Do not remove this code. Path safety is important! -- cyphar - cgPath := libcontainerUtils.CleanPath(c.Path) - cgParent := libcontainerUtils.CleanPath(c.Parent) - cgName := libcontainerUtils.CleanPath(c.Name) - return _defaultDirPath(UnifiedMountpoint, cgPath, cgParent, cgName) + return _defaultDirPath(UnifiedMountpoint, c.Path, c.Parent, c.Name) } func _defaultDirPath(root, cgPath, cgParent, cgName string) (string, error) { if (cgName != "" || cgParent != "") && cgPath != "" { return "", errors.New("cgroup: either Path or Name and Parent should be used") } - innerPath := cgPath + + // XXX: Do not remove CleanPath. Path safety is important! -- cyphar + innerPath := utils.CleanPath(cgPath) if innerPath == "" { + cgParent := utils.CleanPath(cgParent) + cgName := utils.CleanPath(cgName) innerPath = filepath.Join(cgParent, cgName) } if filepath.IsAbs(innerPath) { @@ -89,7 +85,7 @@ func parseCgroupFromReader(r io.Reader) (string, error) { parts = strings.SplitN(text, ":", 3) ) if len(parts) < 3 { - return "", errors.Errorf("invalid cgroup entry: %q", text) + return "", fmt.Errorf("invalid cgroup entry: %q", text) } // text is like "0::/user.slice/user-1001.slice/session-1.scope" if parts[0] == "0" && parts[1] == "" { diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/devices.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/devices.go index 7c501cad8..0d2345607 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/devices.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/devices.go @@ -1,16 +1,15 @@ -// +build linux - package fs2 import ( + "fmt" + + "golang.org/x/sys/unix" + "github.com/opencontainers/runc/libcontainer/cgroups/ebpf" "github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter" "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/devices" "github.com/opencontainers/runc/libcontainer/userns" - - "github.com/pkg/errors" - "golang.org/x/sys/unix" ) func isRWM(perms devices.Permissions) bool { @@ -64,7 +63,7 @@ func setDevices(dirPath string, r *configs.Resources) error { } dirFD, err := unix.Open(dirPath, unix.O_DIRECTORY|unix.O_RDONLY, 0o600) if err != nil { - return errors.Errorf("cannot get dir FD for %s", dirPath) + return fmt.Errorf("cannot get dir FD for %s", dirPath) } defer unix.Close(dirFD) if _, err := ebpf.LoadAttachCgroupDeviceFilter(insts, license, dirFD); err != nil { diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/freezer.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/freezer.go index e901f7a07..8917a6411 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/freezer.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/freezer.go @@ -1,19 +1,17 @@ -// +build linux - package fs2 import ( "bufio" - stdErrors "errors" + "errors" "fmt" "os" "strings" "time" + "golang.org/x/sys/unix" + "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/configs" - "github.com/pkg/errors" - "golang.org/x/sys/unix" ) func setFreezer(dirPath string, state configs.FreezerState) error { @@ -26,7 +24,7 @@ func setFreezer(dirPath string, state configs.FreezerState) error { case configs.Thawed: stateStr = "0" default: - return errors.Errorf("invalid freezer state %q requested", state) + return fmt.Errorf("invalid freezer state %q requested", state) } fd, err := cgroups.OpenFile(dirPath, "cgroup.freeze", unix.O_RDWR) @@ -37,7 +35,7 @@ func setFreezer(dirPath string, state configs.FreezerState) error { if state != configs.Frozen { return nil } - return errors.Wrap(err, "freezer not supported") + return fmt.Errorf("freezer not supported: %w", err) } defer fd.Close() @@ -48,7 +46,7 @@ func setFreezer(dirPath string, state configs.FreezerState) error { if actualState, err := readFreezer(dirPath, fd); err != nil { return err } else if actualState != state { - return errors.Errorf(`expected "cgroup.freeze" to be in state %q but was in %q`, state, actualState) + return fmt.Errorf(`expected "cgroup.freeze" to be in state %q but was in %q`, state, actualState) } return nil } @@ -58,7 +56,7 @@ func getFreezer(dirPath string) (configs.FreezerState, error) { if err != nil { // If the kernel is too old, then we just treat the freezer as being in // an "undefined" state. - if os.IsNotExist(err) || stdErrors.Is(err, unix.ENODEV) { + if os.IsNotExist(err) || errors.Is(err, unix.ENODEV) { err = nil } return configs.Undefined, err @@ -82,7 +80,7 @@ func readFreezer(dirPath string, fd *os.File) (configs.FreezerState, error) { case "1\n": return waitFrozen(dirPath) default: - return configs.Undefined, errors.Errorf(`unknown "cgroup.freeze" state: %q`, state) + return configs.Undefined, fmt.Errorf(`unknown "cgroup.freeze" state: %q`, state) } } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/fs2.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/fs2.go index afba0ab1c..492778e31 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/fs2.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/fs2.go @@ -1,8 +1,7 @@ -// +build linux - package fs2 import ( + "errors" "fmt" "os" "strings" @@ -10,9 +9,10 @@ import ( "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups/fscommon" "github.com/opencontainers/runc/libcontainer/configs" - "github.com/pkg/errors" ) +type parseError = fscommon.ParseError + type manager struct { config *configs.Cgroup // dirPath is like "/sys/fs/cgroup/user.slice/user-1001.slice/session-1.scope" @@ -20,16 +20,12 @@ type manager struct { // controllers is content of "cgroup.controllers" file. // excludes pseudo-controllers ("devices" and "freezer"). controllers map[string]struct{} - rootless bool } // NewManager creates a manager for cgroup v2 unified hierarchy. // dirPath is like "/sys/fs/cgroup/user.slice/user-1001.slice/session-1.scope". // If dirPath is empty, it is automatically set using config. -func NewManager(config *configs.Cgroup, dirPath string, rootless bool) (cgroups.Manager, error) { - if config == nil { - config = &configs.Cgroup{} - } +func NewManager(config *configs.Cgroup, dirPath string) (cgroups.Manager, error) { if dirPath == "" { var err error dirPath, err = defaultDirPath(config) @@ -39,9 +35,8 @@ func NewManager(config *configs.Cgroup, dirPath string, rootless bool) (cgroups. } m := &manager{ - config: config, - dirPath: dirPath, - rootless: rootless, + config: config, + dirPath: dirPath, } return m, nil } @@ -53,7 +48,7 @@ func (m *manager) getControllers() error { data, err := cgroups.ReadFile(m.dirPath, "cgroup.controllers") if err != nil { - if m.rootless && m.config.Path == "" { + if m.config.Rootless && m.config.Path == "" { return nil } return err @@ -73,12 +68,12 @@ func (m *manager) Apply(pid int) error { // - "runc create (no limits + no cgrouppath + no permission) succeeds" // - "runc create (rootless + no limits + cgrouppath + no permission) fails with permission error" // - "runc create (rootless + limits + no cgrouppath + no permission) fails with informative error" - if m.rootless { + if m.config.Rootless { if m.config.Path == "" { if blNeed, nErr := needAnyControllers(m.config.Resources); nErr == nil && !blNeed { return nil } - return errors.Wrap(err, "rootless needs no limits + no cgrouppath when no permission is granted for cgroups") + return fmt.Errorf("rootless needs no limits + no cgrouppath when no permission is granted for cgroups: %w", err) } } return err @@ -123,13 +118,20 @@ func (m *manager) GetStats() (*cgroups.Stats, error) { if err := statHugeTlb(m.dirPath, st); err != nil && !os.IsNotExist(err) { errs = append(errs, err) } - if len(errs) > 0 && !m.rootless { - return st, errors.Errorf("error while statting cgroup v2: %+v", errs) + // rdma (since kernel 4.11) + if err := fscommon.RdmaGetStats(m.dirPath, st); err != nil && !os.IsNotExist(err) { + errs = append(errs, err) + } + if len(errs) > 0 && !m.config.Rootless { + return st, fmt.Errorf("error while statting cgroup v2: %+v", errs) } return st, nil } func (m *manager) Freeze(state configs.FreezerState) error { + if m.config.Resources == nil { + return errors.New("cannot toggle freezer: cgroups not configured for container") + } if err := setFreezer(m.dirPath, state); err != nil { return err } @@ -146,6 +148,9 @@ func (m *manager) Path(_ string) string { } func (m *manager) Set(r *configs.Resources) error { + if r == nil { + return nil + } if err := m.getControllers(); err != nil { return err } @@ -167,10 +172,10 @@ func (m *manager) Set(r *configs.Resources) error { } // devices (since kernel 4.15, pseudo-controller) // - // When m.rootless is true, errors from the device subsystem are ignored because it is really not expected to work. + // When rootless is true, errors from the device subsystem are ignored because it is really not expected to work. // However, errors from other subsystems are not ignored. // see @test "runc create (rootless + limits + no cgrouppath + no permission) fails with informative error" - if err := setDevices(m.dirPath, r); err != nil && !m.rootless { + if err := setDevices(m.dirPath, r); err != nil && !m.config.Rootless { return err } // cpuset (since kernel 5.0) @@ -181,6 +186,10 @@ func (m *manager) Set(r *configs.Resources) error { if err := setHugeTlb(m.dirPath, r); err != nil { return err } + // rdma (since kernel 4.11) + if err := fscommon.RdmaSet(m.dirPath, r); err != nil { + return err + } // freezer (since kernel 5.2, pseudo-controller) if err := setFreezer(m.dirPath, r.Freezer); err != nil { return err @@ -198,9 +207,8 @@ func (m *manager) setUnified(res map[string]string) error { return fmt.Errorf("unified resource %q must be a file name (no slashes)", k) } if err := cgroups.WriteFile(m.dirPath, k, v); err != nil { - errC := errors.Cause(err) // Check for both EPERM and ENOENT since O_CREAT is used by WriteFile. - if errors.Is(errC, os.ErrPermission) || errors.Is(errC, os.ErrNotExist) { + if errors.Is(err, os.ErrPermission) || errors.Is(err, os.ErrNotExist) { // Check if a controller is available, // to give more specific error if not. sk := strings.SplitN(k, ".", 2) @@ -212,7 +220,7 @@ func (m *manager) setUnified(res map[string]string) error { return fmt.Errorf("unified resource %q can't be set: controller %q not available", k, c) } } - return errors.Wrapf(err, "can't set unified resource %q", k) + return fmt.Errorf("unable to set unified resource %q: %w", k, err) } } @@ -243,7 +251,7 @@ func OOMKillCount(path string) (uint64, error) { func (m *manager) OOMKillCount() (uint64, error) { c, err := OOMKillCount(m.dirPath) - if err != nil && m.rootless && os.IsNotExist(err) { + if err != nil && m.config.Rootless && os.IsNotExist(err) { err = nil } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/hugetlb.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/hugetlb.go index 96390a224..c92a7e64a 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/hugetlb.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/hugetlb.go @@ -1,12 +1,8 @@ -// +build linux - package fs2 import ( "strconv" - "github.com/pkg/errors" - "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups/fscommon" "github.com/opencontainers/runc/libcontainer/configs" @@ -30,10 +26,8 @@ func setHugeTlb(dirPath string, r *configs.Resources) error { } func statHugeTlb(dirPath string, stats *cgroups.Stats) error { - hugePageSizes, _ := cgroups.GetHugePageSize() hugetlbStats := cgroups.HugetlbStats{} - - for _, pagesize := range hugePageSizes { + for _, pagesize := range cgroups.HugePageSizes() { value, err := fscommon.GetCgroupParamUint(dirPath, "hugetlb."+pagesize+".current") if err != nil { return err @@ -43,7 +37,7 @@ func statHugeTlb(dirPath string, stats *cgroups.Stats) error { fileName := "hugetlb." + pagesize + ".events" value, err = fscommon.GetValueByKey(dirPath, fileName, "max") if err != nil { - return errors.Wrap(err, "failed to read stats") + return err } hugetlbStats.Failcnt = value diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/io.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/io.go index fd3f0993e..b2ff7d340 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/io.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/io.go @@ -1,5 +1,3 @@ -// +build linux - package fs2 import ( @@ -117,13 +115,14 @@ func readCgroup2MapFile(dirPath string, name string) (map[string][]string, error ret[parts[0]] = parts[1:] } if err := scanner.Err(); err != nil { - return nil, err + return nil, &parseError{Path: dirPath, File: name, Err: err} } return ret, nil } func statIo(dirPath string, stats *cgroups.Stats) error { - values, err := readCgroup2MapFile(dirPath, "io.stat") + const file = "io.stat" + values, err := readCgroup2MapFile(dirPath, file) if err != nil { return err } @@ -136,11 +135,11 @@ func statIo(dirPath string, stats *cgroups.Stats) error { } major, err := strconv.ParseUint(d[0], 10, 64) if err != nil { - return err + return &parseError{Path: dirPath, File: file, Err: err} } minor, err := strconv.ParseUint(d[1], 10, 64) if err != nil { - return err + return &parseError{Path: dirPath, File: file, Err: err} } for _, item := range v { @@ -177,7 +176,7 @@ func statIo(dirPath string, stats *cgroups.Stats) error { value, err := strconv.ParseUint(d[1], 10, 64) if err != nil { - return err + return &parseError{Path: dirPath, File: file, Err: err} } entry := cgroups.BlkioStatEntry{ diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/memory.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/memory.go index 53e8f1e93..adbc4b230 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/memory.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/memory.go @@ -1,19 +1,18 @@ -// +build linux - package fs2 import ( "bufio" + "errors" "math" "os" "strconv" "strings" + "golang.org/x/sys/unix" + "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups/fscommon" "github.com/opencontainers/runc/libcontainer/configs" - "github.com/pkg/errors" - "golang.org/x/sys/unix" ) // numToStr converts an int64 value to a string for writing to a @@ -75,8 +74,8 @@ func setMemory(dirPath string, r *configs.Resources) error { } func statMemory(dirPath string, stats *cgroups.Stats) error { - // Set stats from memory.stat. - statsFile, err := cgroups.OpenFile(dirPath, "memory.stat", os.O_RDONLY) + const file = "memory.stat" + statsFile, err := cgroups.OpenFile(dirPath, file, os.O_RDONLY) if err != nil { return err } @@ -86,10 +85,13 @@ func statMemory(dirPath string, stats *cgroups.Stats) error { for sc.Scan() { t, v, err := fscommon.ParseKeyValue(sc.Text()) if err != nil { - return errors.Wrapf(err, "failed to parse memory.stat (%q)", sc.Text()) + return &parseError{Path: dirPath, File: file, Err: err} } stats.MemoryStats.Stats[t] = v } + if err := sc.Err(); err != nil { + return &parseError{Path: dirPath, File: file, Err: err} + } stats.MemoryStats.Cache = stats.MemoryStats.Stats["file"] // Unlike cgroup v1 which has memory.use_hierarchy binary knob, // cgroup v2 is always hierarchical. @@ -139,13 +141,13 @@ func getMemoryDataV2(path, name string) (cgroups.MemoryData, error) { // swapaccount=0 kernel boot parameter is given. return cgroups.MemoryData{}, nil } - return cgroups.MemoryData{}, errors.Wrapf(err, "failed to parse %s", usage) + return cgroups.MemoryData{}, err } memoryData.Usage = value value, err = fscommon.GetCgroupParamUint(path, limit) if err != nil { - return cgroups.MemoryData{}, errors.Wrapf(err, "failed to parse %s", limit) + return cgroups.MemoryData{}, err } memoryData.Limit = value @@ -153,7 +155,8 @@ func getMemoryDataV2(path, name string) (cgroups.MemoryData, error) { } func statsFromMeminfo(stats *cgroups.Stats) error { - f, err := os.Open("/proc/meminfo") + const file = "/proc/meminfo" + f, err := os.Open(file) if err != nil { return err } @@ -190,7 +193,7 @@ func statsFromMeminfo(stats *cgroups.Stats) error { vStr := strings.TrimSpace(strings.TrimSuffix(parts[1], " kB")) *p, err = strconv.ParseUint(vStr, 10, 64) if err != nil { - return errors.Wrap(err, "parsing /proc/meminfo "+k) + return &parseError{File: file, Err: errors.New("bad value for " + k)} } found++ @@ -199,8 +202,8 @@ func statsFromMeminfo(stats *cgroups.Stats) error { break } } - if sc.Err() != nil { - return sc.Err() + if err := sc.Err(); err != nil { + return &parseError{Path: "", File: file, Err: err} } stats.MemoryStats.SwapUsage.Usage = (swap_total - swap_free) * 1024 diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/pids.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/pids.go index e2050002d..c8c4a3658 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/pids.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs2/pids.go @@ -1,17 +1,16 @@ -// +build linux - package fs2 import ( + "errors" + "math" "os" - "path/filepath" "strings" + "golang.org/x/sys/unix" + "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups/fscommon" "github.com/opencontainers/runc/libcontainer/configs" - "github.com/pkg/errors" - "golang.org/x/sys/unix" ) func isPidsSet(r *configs.Resources) bool { @@ -53,22 +52,18 @@ func statPids(dirPath string, stats *cgroups.Stats) error { if os.IsNotExist(err) { return statPidsFromCgroupProcs(dirPath, stats) } - return errors.Wrap(err, "failed to parse pids.current") + return err } - maxString, err := fscommon.GetCgroupParamString(dirPath, "pids.max") + max, err := fscommon.GetCgroupParamUint(dirPath, "pids.max") if err != nil { - return errors.Wrap(err, "failed to parse pids.max") + return err } - - // Default if pids.max == "max" is 0 -- which represents "no limit". - var max uint64 - if maxString != "max" { - max, err = fscommon.ParseUint(maxString, 10, 64) - if err != nil { - return errors.Wrapf(err, "failed to parse pids.max - unable to parse %q as a uint from Cgroup file %q", - maxString, filepath.Join(dirPath, "pids.max")) - } + // If no limit is set, read from pids.max returns "max", which is + // converted to MaxUint64 by GetCgroupParamUint. Historically, we + // represent "no limit" for pids as 0, thus this conversion. + if max == math.MaxUint64 { + max = 0 } stats.PidsStats.Current = current diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/BUILD.bazel b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/BUILD.bazel index 185eda6db..d3c587757 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/BUILD.bazel +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/BUILD.bazel @@ -2,17 +2,16 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "go_default_library", - srcs = ["utils.go"], + srcs = [ + "rdma.go", + "utils.go", + ], importmap = "kubevirt.io/kubevirt/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon", importpath = "github.com/opencontainers/runc/libcontainer/cgroups/fscommon", visibility = ["//visibility:public"], - deps = select({ - "@io_bazel_rules_go//go/platform:android": [ - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups:go_default_library", - ], - "@io_bazel_rules_go//go/platform:linux": [ - "//vendor/github.com/opencontainers/runc/libcontainer/cgroups:go_default_library", - ], - "//conditions:default": [], - }), + deps = [ + "//vendor/github.com/opencontainers/runc/libcontainer/cgroups:go_default_library", + "//vendor/github.com/opencontainers/runc/libcontainer/configs:go_default_library", + "//vendor/golang.org/x/sys/unix:go_default_library", + ], ) diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/rdma.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/rdma.go new file mode 100644 index 000000000..d463d15ee --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/rdma.go @@ -0,0 +1,121 @@ +package fscommon + +import ( + "bufio" + "errors" + "math" + "os" + "strconv" + "strings" + + "github.com/opencontainers/runc/libcontainer/cgroups" + "github.com/opencontainers/runc/libcontainer/configs" + "golang.org/x/sys/unix" +) + +// parseRdmaKV parses raw string to RdmaEntry. +func parseRdmaKV(raw string, entry *cgroups.RdmaEntry) error { + var value uint32 + + parts := strings.SplitN(raw, "=", 3) + + if len(parts) != 2 { + return errors.New("Unable to parse RDMA entry") + } + + k, v := parts[0], parts[1] + + if v == "max" { + value = math.MaxUint32 + } else { + val64, err := strconv.ParseUint(v, 10, 32) + if err != nil { + return err + } + value = uint32(val64) + } + if k == "hca_handle" { + entry.HcaHandles = value + } else if k == "hca_object" { + entry.HcaObjects = value + } + + return nil +} + +// readRdmaEntries reads and converts array of rawstrings to RdmaEntries from file. +// example entry: mlx4_0 hca_handle=2 hca_object=2000 +func readRdmaEntries(dir, file string) ([]cgroups.RdmaEntry, error) { + rdmaEntries := make([]cgroups.RdmaEntry, 0) + fd, err := cgroups.OpenFile(dir, file, unix.O_RDONLY) + if err != nil { + return nil, err + } + defer fd.Close() //nolint:errorlint + scanner := bufio.NewScanner(fd) + for scanner.Scan() { + parts := strings.SplitN(scanner.Text(), " ", 4) + if len(parts) == 3 { + entry := new(cgroups.RdmaEntry) + entry.Device = parts[0] + err = parseRdmaKV(parts[1], entry) + if err != nil { + continue + } + err = parseRdmaKV(parts[2], entry) + if err != nil { + continue + } + + rdmaEntries = append(rdmaEntries, *entry) + } + } + return rdmaEntries, scanner.Err() +} + +// RdmaGetStats returns rdma stats such as totalLimit and current entries. +func RdmaGetStats(path string, stats *cgroups.Stats) error { + currentEntries, err := readRdmaEntries(path, "rdma.current") + if err != nil { + if errors.Is(err, os.ErrNotExist) { + err = nil + } + return err + } + maxEntries, err := readRdmaEntries(path, "rdma.max") + if err != nil { + return err + } + // If device got removed between reading two files, ignore returning stats. + if len(currentEntries) != len(maxEntries) { + return nil + } + + stats.RdmaStats = cgroups.RdmaStats{ + RdmaLimit: maxEntries, + RdmaCurrent: currentEntries, + } + + return nil +} + +func createCmdString(device string, limits configs.LinuxRdma) string { + cmdString := device + if limits.HcaHandles != nil { + cmdString += " hca_handle=" + strconv.FormatUint(uint64(*limits.HcaHandles), 10) + } + if limits.HcaObjects != nil { + cmdString += " hca_object=" + strconv.FormatUint(uint64(*limits.HcaObjects), 10) + } + return cmdString +} + +// RdmaSet sets RDMA resources. +func RdmaSet(path string, r *configs.Resources) error { + for device, limits := range r.Rdma { + if err := cgroups.WriteFile(path, "rdma.max", createCmdString(device, limits)); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/utils.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/utils.go index e31146ae9..f4a51c9e5 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/utils.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/utils.go @@ -1,11 +1,10 @@ -// +build linux - package fscommon import ( "errors" "fmt" "math" + "path" "strconv" "strings" @@ -13,8 +12,6 @@ import ( ) var ( - ErrNotValidFormat = errors.New("line is not a valid key value format") - // Deprecated: use cgroups.OpenFile instead. OpenFile = cgroups.OpenFile // Deprecated: use cgroups.ReadFile instead. @@ -23,6 +20,19 @@ var ( WriteFile = cgroups.WriteFile ) +// ParseError records a parse error details, including the file path. +type ParseError struct { + Path string + File string + Err error +} + +func (e *ParseError) Error() string { + return "unable to parse " + path.Join(e.Path, e.File) + ": " + e.Err.Error() +} + +func (e *ParseError) Unwrap() error { return e.Err } + // ParseUint converts a string to an uint64 integer. // Negative values are returned at zero as, due to kernel bugs, // some of the memory cgroup stats can be negative. @@ -34,7 +44,7 @@ func ParseUint(s string, base, bitSize int) (uint64, error) { // 2. Handle negative values lesser than MinInt64 if intErr == nil && intValue < 0 { return 0, nil - } else if intErr != nil && intErr.(*strconv.NumError).Err == strconv.ErrRange && intValue < 0 { + } else if errors.Is(intErr, strconv.ErrRange) && intValue < 0 { return 0, nil } @@ -56,7 +66,7 @@ func ParseKeyValue(t string) (string, uint64, error) { value, err := ParseUint(parts[1], 10, 64) if err != nil { - return "", 0, fmt.Errorf("unable to convert to uint64: %v", err) + return "", 0, err } return parts[0], value, nil @@ -71,11 +81,15 @@ func GetValueByKey(path, file, key string) (uint64, error) { return 0, err } - lines := strings.Split(string(content), "\n") + lines := strings.Split(content, "\n") for _, line := range lines { arr := strings.Split(line, " ") if len(arr) == 2 && arr[0] == key { - return ParseUint(arr[1], 10, 64) + val, err := ParseUint(arr[1], 10, 64) + if err != nil { + err = &ParseError{Path: path, File: file, Err: err} + } + return val, err } } @@ -96,7 +110,7 @@ func GetCgroupParamUint(path, file string) (uint64, error) { res, err := ParseUint(contents, 10, 64) if err != nil { - return res, fmt.Errorf("unable to parse file %q", path+"/"+file) + return res, &ParseError{Path: path, File: file, Err: err} } return res, nil } @@ -115,7 +129,7 @@ func GetCgroupParamInt(path, file string) (int64, error) { res, err := strconv.ParseInt(contents, 10, 64) if err != nil { - return res, fmt.Errorf("unable to parse %q as a int from Cgroup file %q", contents, path+"/"+file) + return res, &ParseError{Path: path, File: file, Err: err} } return res, nil } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/getallpids.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/getallpids.go new file mode 100644 index 000000000..1355a5101 --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/getallpids.go @@ -0,0 +1,27 @@ +package cgroups + +import ( + "io/fs" + "path/filepath" +) + +// GetAllPids returns all pids from the cgroup identified by path, and all its +// sub-cgroups. +func GetAllPids(path string) ([]int, error) { + var pids []int + err := filepath.WalkDir(path, func(p string, d fs.DirEntry, iErr error) error { + if iErr != nil { + return iErr + } + if !d.IsDir() { + return nil + } + cPids, err := readProcsFile(p) + if err != nil { + return err + } + pids = append(pids, cPids...) + return nil + }) + return pids, err +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go index e7f9c4626..40a81dd5a 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go @@ -1,5 +1,3 @@ -// +build linux - package cgroups type ThrottlingData struct { @@ -126,7 +124,7 @@ type BlkioStatEntry struct { } type BlkioStats struct { - // number of bytes tranferred to and from the block device + // number of bytes transferred to and from the block device IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive,omitempty"` IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive,omitempty"` IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive,omitempty"` @@ -146,6 +144,17 @@ type HugetlbStats struct { Failcnt uint64 `json:"failcnt"` } +type RdmaEntry struct { + Device string `json:"device,omitempty"` + HcaHandles uint32 `json:"hca_handles,omitempty"` + HcaObjects uint32 `json:"hca_objects,omitempty"` +} + +type RdmaStats struct { + RdmaLimit []RdmaEntry `json:"rdma_limit,omitempty"` + RdmaCurrent []RdmaEntry `json:"rdma_current,omitempty"` +} + type Stats struct { CpuStats CpuStats `json:"cpu_stats,omitempty"` CPUSetStats CPUSetStats `json:"cpuset_stats,omitempty"` @@ -154,6 +163,7 @@ type Stats struct { BlkioStats BlkioStats `json:"blkio_stats,omitempty"` // the map is in the format "size of hugepage: stats of the hugepage" HugetlbStats map[string]HugetlbStats `json:"hugetlb_stats,omitempty"` + RdmaStats RdmaStats `json:"rdma_stats,omitempty"` } func NewStats() *Stats { diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go index 92606525b..b32af4ee5 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go @@ -1,5 +1,3 @@ -// +build linux - package cgroups import ( @@ -7,7 +5,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "os" "path/filepath" "strconv" @@ -23,11 +20,14 @@ import ( const ( CgroupProcesses = "cgroup.procs" unifiedMountpoint = "/sys/fs/cgroup" + hybridMountpoint = "/sys/fs/cgroup/unified" ) var ( isUnifiedOnce sync.Once isUnified bool + isHybridOnce sync.Once + isHybrid bool ) // IsCgroup2UnifiedMode returns whether we are running in cgroup v2 unified mode. @@ -49,6 +49,24 @@ func IsCgroup2UnifiedMode() bool { return isUnified } +// IsCgroup2HybridMode returns whether we are running in cgroup v2 hybrid mode. +func IsCgroup2HybridMode() bool { + isHybridOnce.Do(func() { + var st unix.Statfs_t + err := unix.Statfs(hybridMountpoint, &st) + if err != nil { + isHybrid = false + if !os.IsNotExist(err) { + // Report unexpected errors. + logrus.WithError(err).Debugf("statfs(%q) failed", hybridMountpoint) + } + return + } + isHybrid = st.Type == unix.CGROUP2_SUPER_MAGIC + }) + return isHybrid +} + type Mount struct { Mountpoint string Root string @@ -118,8 +136,8 @@ func GetAllSubsystems() ([]string, error) { return subsystems, nil } -func readProcsFile(file string) ([]int, error) { - f, err := os.Open(file) +func readProcsFile(dir string) ([]int, error) { + f, err := OpenFile(dir, CgroupProcesses, os.O_RDONLY) if err != nil { return nil, err } @@ -210,7 +228,7 @@ func EnterPid(cgroupPaths map[string]string, pid int) error { func rmdir(path string) error { err := unix.Rmdir(path) - if err == nil || err == unix.ENOENT { + if err == nil || err == unix.ENOENT { //nolint:errorlint // unix errors are bare return nil } return &os.PathError{Op: "rmdir", Path: path, Err: err} @@ -224,7 +242,7 @@ func RemovePath(path string) error { return nil } - infos, err := ioutil.ReadDir(path) + infos, err := os.ReadDir(path) if err != nil { if os.IsNotExist(err) { err = nil @@ -284,40 +302,61 @@ func RemovePaths(paths map[string]string) (err error) { return fmt.Errorf("Failed to remove paths: %v", paths) } -func GetHugePageSize() ([]string, error) { - dir, err := os.OpenFile("/sys/kernel/mm/hugepages", unix.O_DIRECTORY|unix.O_RDONLY, 0) - if err != nil { - return nil, err - } - files, err := dir.Readdirnames(0) - dir.Close() - if err != nil { - return nil, err - } +var ( + hugePageSizes []string + initHPSOnce sync.Once +) + +func HugePageSizes() []string { + initHPSOnce.Do(func() { + dir, err := os.OpenFile("/sys/kernel/mm/hugepages", unix.O_DIRECTORY|unix.O_RDONLY, 0) + if err != nil { + return + } + files, err := dir.Readdirnames(0) + dir.Close() + if err != nil { + return + } + + hugePageSizes, err = getHugePageSizeFromFilenames(files) + if err != nil { + logrus.Warn("HugePageSizes: ", err) + } + }) - return getHugePageSizeFromFilenames(files) + return hugePageSizes } func getHugePageSizeFromFilenames(fileNames []string) ([]string, error) { pageSizes := make([]string, 0, len(fileNames)) + var warn error for _, file := range fileNames { // example: hugepages-1048576kB val := strings.TrimPrefix(file, "hugepages-") if len(val) == len(file) { - // unexpected file name: no prefix found + // Unexpected file name: no prefix found, ignore it. continue } - // The suffix is always "kB" (as of Linux 5.9) + // The suffix is always "kB" (as of Linux 5.13). If we find + // something else, produce an error but keep going. eLen := len(val) - 2 val = strings.TrimSuffix(val, "kB") if len(val) != eLen { - logrus.Warnf("GetHugePageSize: %s: invalid filename suffix (expected \"kB\")", file) + // Highly unlikely. + if warn == nil { + warn = errors.New(file + `: invalid suffix (expected "kB")`) + } continue } size, err := strconv.Atoi(val) if err != nil { - return nil, err + // Highly unlikely. + if warn == nil { + warn = fmt.Errorf("%s: %w", file, err) + } + continue } // Model after https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/mm/hugetlb_cgroup.c?id=eff48ddeab782e35e58ccc8853f7386bbae9dec4#n574 // but in our case the size is in KB already. @@ -331,34 +370,12 @@ func getHugePageSizeFromFilenames(fileNames []string) ([]string, error) { pageSizes = append(pageSizes, val) } - return pageSizes, nil + return pageSizes, warn } // GetPids returns all pids, that were added to cgroup at path. func GetPids(dir string) ([]int, error) { - return readProcsFile(filepath.Join(dir, CgroupProcesses)) -} - -// GetAllPids returns all pids, that were added to cgroup at path and to all its -// subcgroups. -func GetAllPids(path string) ([]int, error) { - var pids []int - // collect pids from all sub-cgroups - err := filepath.Walk(path, func(p string, info os.FileInfo, iErr error) error { - if iErr != nil { - return iErr - } - if info.IsDir() || info.Name() != CgroupProcesses { - return nil - } - cPids, err := readProcsFile(p) - if err != nil { - return err - } - pids = append(pids, cPids...) - return nil - }) - return pids, err + return readProcsFile(dir) } // WriteCgroupProc writes the specified pid into the cgroup's cgroup.procs file @@ -376,7 +393,7 @@ func WriteCgroupProc(dir string, pid int) error { file, err := OpenFile(dir, CgroupProcesses, os.O_WRONLY) if err != nil { - return fmt.Errorf("failed to write %v to %v: %v", pid, CgroupProcesses, err) + return fmt.Errorf("failed to write %v: %w", pid, err) } defer file.Close() @@ -393,7 +410,7 @@ func WriteCgroupProc(dir string, pid int) error { continue } - return fmt.Errorf("failed to write %v to %v: %v", pid, CgroupProcesses, err) + return fmt.Errorf("failed to write %v: %w", pid, err) } return err } @@ -446,5 +463,5 @@ func ConvertBlkIOToIOWeightValue(blkIoWeight uint16) uint64 { if blkIoWeight == 0 { return 0 } - return uint64(1 + (uint64(blkIoWeight)-10)*9999/990) + return 1 + (uint64(blkIoWeight)-10)*9999/990 } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go index 95ec9dff0..47c75f22b 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go @@ -46,11 +46,8 @@ func NewNotFoundError(sub string) error { } func IsNotFound(err error) bool { - if err == nil { - return false - } - _, ok := err.(*NotFoundError) - return ok + var nfErr *NotFoundError + return errors.As(err, &nfErr) } func tryDefaultPath(cgroupPath, subsystem string) string { @@ -116,6 +113,11 @@ func FindCgroupMountpoint(cgroupPath, subsystem string) (string, error) { return "", errUnified } + // If subsystem is empty, we look for the cgroupv2 hybrid path. + if len(subsystem) == 0 { + return hybridMountpoint, nil + } + // Avoid parsing mountinfo by trying the default path first, if possible. if path := tryDefaultPath(cgroupPath, subsystem); path != "" { return path, nil @@ -154,7 +156,7 @@ func findCgroupMountpointAndRootFromMI(mounts []*mountinfo.Info, cgroupPath, sub func (m Mount) GetOwnCgroup(cgroups map[string]string) (string, error) { if len(m.Subsystems) == 0 { - return "", fmt.Errorf("no subsystem for mount") + return "", errors.New("no subsystem for mount") } return getControllerPath(m.Subsystems[0], cgroups) @@ -226,6 +228,11 @@ func GetOwnCgroupPath(subsystem string) (string, error) { return "", err } + // If subsystem is empty, we look for the cgroupv2 hybrid path. + if len(subsystem) == 0 { + return hybridMountpoint, nil + } + return getCgroupPathHelper(subsystem, cgroup) } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/BUILD.bazel b/vendor/github.com/opencontainers/runc/libcontainer/configs/BUILD.bazel index acccbbee9..f945c9a20 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/BUILD.bazel +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "namespaces_syscall_unsupported.go", "namespaces_unsupported.go", "network.go", + "rdma.go", ], importmap = "kubevirt.io/kubevirt/vendor/github.com/opencontainers/runc/libcontainer/configs", importpath = "github.com/opencontainers/runc/libcontainer/configs", @@ -25,16 +26,14 @@ go_library( deps = [ "//vendor/github.com/opencontainers/runc/libcontainer/devices:go_default_library", "//vendor/github.com/opencontainers/runtime-spec/specs-go:go_default_library", - "//vendor/github.com/pkg/errors:go_default_library", "//vendor/github.com/sirupsen/logrus:go_default_library", + "//vendor/golang.org/x/sys/unix:go_default_library", ] + select({ "@io_bazel_rules_go//go/platform:android": [ "//vendor/github.com/coreos/go-systemd/v22/dbus:go_default_library", - "//vendor/golang.org/x/sys/unix:go_default_library", ], "@io_bazel_rules_go//go/platform:linux": [ "//vendor/github.com/coreos/go-systemd/v22/dbus:go_default_library", - "//vendor/golang.org/x/sys/unix:go_default_library", ], "//conditions:default": [], }), diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go index 5ea9d940c..2d4a89871 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go @@ -28,17 +28,26 @@ type Cgroup struct { // ScopePrefix describes prefix for the scope name ScopePrefix string `json:"scope_prefix"` - // Paths represent the absolute cgroups paths to join. - // This takes precedence over Path. - Paths map[string]string - // Resources contains various cgroups settings to apply *Resources + // Systemd tells if systemd should be used to manage cgroups. + Systemd bool + // SystemdProps are any additional properties for systemd, // derived from org.systemd.property.xxx annotations. // Ignored unless systemd is used for managing cgroups. SystemdProps []systemdDbus.Property `json:"-"` + + // Rootless tells if rootless cgroups should be used. + Rootless bool + + // The host UID that should own the cgroup, or nil to accept + // the default ownership. This should only be set when the + // cgroupfs is to be mounted read/write. + // Not all cgroup manager implementations support changing + // the ownership. + OwnerUID *int `json:"owner_uid,omitempty"` } type Resources struct { @@ -117,6 +126,9 @@ type Resources struct { // Set class identifier for container's network packets NetClsClassid uint32 `json:"net_cls_classid_u"` + // Rdma resource restriction configuration + Rdma map[string]LinuxRdma `json:"rdma"` + // Used on cgroups v2: // CpuWeight sets a proportional bandwidth limit. diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unsupported.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unsupported.go index 2a519f582..7e383020f 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unsupported.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package configs diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go index 4281593f0..c1b4a0041 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go @@ -7,10 +7,10 @@ import ( "os/exec" "time" + "github.com/sirupsen/logrus" + "github.com/opencontainers/runc/libcontainer/devices" "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) type Rlimit struct { @@ -31,10 +31,12 @@ type IDMap struct { // for syscalls. Additional architectures can be added by specifying them in // Architectures. type Seccomp struct { - DefaultAction Action `json:"default_action"` - Architectures []string `json:"architectures"` - Syscalls []*Syscall `json:"syscalls"` - DefaultErrnoRet *uint `json:"default_errno_ret"` + DefaultAction Action `json:"default_action"` + Architectures []string `json:"architectures"` + Syscalls []*Syscall `json:"syscalls"` + DefaultErrnoRet *uint `json:"default_errno_ret"` + ListenerPath string `json:"listener_path,omitempty"` + ListenerMetadata string `json:"listener_metadata,omitempty"` } // Action is taken upon rule match in Seccomp @@ -47,6 +49,9 @@ const ( Allow Trace Log + Notify + KillThread + KillProcess ) // Operator is a comparison operator to be used when matching syscall arguments in Seccomp @@ -246,6 +251,19 @@ const ( Poststop HookName = "poststop" ) +// KnownHookNames returns the known hook names. +// Used by `runc features`. +func KnownHookNames() []string { + return []string{ + string(Prestart), // deprecated + string(CreateRuntime), + string(CreateContainer), + string(StartContainer), + string(Poststart), + string(Poststop), + } +} + type Capabilities struct { // Bounding is the set of capabilities checked by the kernel. Bounding []string @@ -262,7 +280,7 @@ type Capabilities struct { func (hooks HookList) RunHooks(state *specs.State) error { for i, h := range hooks { if err := h.Run(state); err != nil { - return errors.Wrapf(err, "Running hook #%d:", i) + return fmt.Errorf("error running hook #%d: %w", i, err) } } @@ -375,7 +393,7 @@ func (c Command) Run(s *specs.State) error { go func() { err := cmd.Wait() if err != nil { - err = fmt.Errorf("error running hook: %v, stdout: %s, stderr: %s", err, stdout.String(), stderr.String()) + err = fmt.Errorf("error running hook: %w, stdout: %s, stderr: %s", err, stdout.String(), stderr.String()) } errC <- err }() diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/config_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/config_linux.go index 07da10804..8c02848b7 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/config_linux.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/config_linux.go @@ -1,17 +1,24 @@ package configs -import "fmt" +import "errors" + +var ( + errNoUIDMap = errors.New("User namespaces enabled, but no uid mappings found.") + errNoUserMap = errors.New("User namespaces enabled, but no user mapping found.") + errNoGIDMap = errors.New("User namespaces enabled, but no gid mappings found.") + errNoGroupMap = errors.New("User namespaces enabled, but no group mapping found.") +) // HostUID gets the translated uid for the process on host which could be // different when user namespaces are enabled. func (c Config) HostUID(containerId int) (int, error) { if c.Namespaces.Contains(NEWUSER) { if c.UidMappings == nil { - return -1, fmt.Errorf("User namespaces enabled, but no uid mappings found.") + return -1, errNoUIDMap } id, found := c.hostIDFromMapping(containerId, c.UidMappings) if !found { - return -1, fmt.Errorf("User namespaces enabled, but no user mapping found.") + return -1, errNoUserMap } return id, nil } @@ -30,11 +37,11 @@ func (c Config) HostRootUID() (int, error) { func (c Config) HostGID(containerId int) (int, error) { if c.Namespaces.Contains(NEWUSER) { if c.GidMappings == nil { - return -1, fmt.Errorf("User namespaces enabled, but no gid mappings found.") + return -1, errNoGIDMap } id, found := c.hostIDFromMapping(containerId, c.GidMappings) if !found { - return -1, fmt.Errorf("User namespaces enabled, but no group mapping found.") + return -1, errNoGroupMap } return id, nil } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/configs_fuzzer.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/configs_fuzzer.go index 93bf41c8d..bce829e29 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/configs_fuzzer.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/configs_fuzzer.go @@ -1,3 +1,4 @@ +//go:build gofuzz // +build gofuzz package configs diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/intelrdt.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/intelrdt.go index 57e9f037d..f8d951ab8 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/intelrdt.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/intelrdt.go @@ -1,6 +1,9 @@ package configs type IntelRdt struct { + // The identity for RDT Class of Service + ClosID string `json:"closID,omitempty"` + // The schema for L3 cache id and capacity bitmask (CBM) // Format: "L3:<cache_id0>=<cbm0>;<cache_id1>=<cbm1>;..." L3CacheSchema string `json:"l3_cache_schema,omitempty"` diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/mount.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/mount.go index a75ff10ec..784c61820 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/mount.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/mount.go @@ -1,5 +1,7 @@ package configs +import "golang.org/x/sys/unix" + const ( // EXT_COPYUP is a directive to copy up the contents of a directory when // a tmpfs is mounted over it. @@ -28,6 +30,9 @@ type Mount struct { // Relabel source if set, "z" indicates shared, "Z" indicates unshared. Relabel string `json:"relabel"` + // RecAttr represents mount properties to be applied recursively (AT_RECURSIVE), see mount_setattr(2). + RecAttr *unix.MountAttr `json:"rec_attr"` + // Extensions are additional flags that are specific to runc. Extensions int `json:"extensions"` @@ -37,3 +42,7 @@ type Mount struct { // Optional Command to be run after Source is mounted. PostmountCmds []Command `json:"postmount_cmds"` } + +func (m *Mount) IsBind() bool { + return m.Flags&unix.MS_BIND != 0 +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall.go index 2dc7adfc9..0516dba8d 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall.go @@ -1,3 +1,4 @@ +//go:build linux // +build linux package configs diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall_unsupported.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall_unsupported.go index 5d9a5c81f..fbb0d4907 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall_unsupported.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux && !windows // +build !linux,!windows package configs diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go index cc76e2f58..946db30a5 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package configs diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/rdma.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/rdma.go new file mode 100644 index 000000000..c69f2c802 --- /dev/null +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/rdma.go @@ -0,0 +1,9 @@ +package configs + +// LinuxRdma for Linux cgroup 'rdma' resource management (Linux 4.11) +type LinuxRdma struct { + // Maximum number of HCA handles that can be opened. Default is "no limit". + HcaHandles *uint32 `json:"hca_handles,omitempty"` + // Maximum number of HCA objects that can be created. Default is "no limit". + HcaObjects *uint32 `json:"hca_objects,omitempty"` +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go b/vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go index 6d5b3d09d..7d8e9fc31 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go @@ -1,10 +1,10 @@ +//go:build !windows // +build !windows package devices import ( "errors" - "io/ioutil" "os" "path/filepath" @@ -16,8 +16,8 @@ var ErrNotADevice = errors.New("not a device node") // Testing dependencies var ( - unixLstat = unix.Lstat - ioutilReadDir = ioutil.ReadDir + unixLstat = unix.Lstat + osReadDir = os.ReadDir ) func mkDev(d *Rule) (uint64, error) { @@ -40,7 +40,7 @@ func DeviceFromPath(path, permissions string) (*Device, error) { var ( devType Type mode = stat.Mode - devNumber = uint64(stat.Rdev) + devNumber = uint64(stat.Rdev) //nolint:unconvert // Rdev is uint32 on e.g. MIPS. major = unix.Major(devNumber) minor = unix.Minor(devNumber) ) @@ -76,7 +76,7 @@ func HostDevices() ([]*Device, error) { // GetDevices recursively traverses a directory specified by path // and returns all devices found there. func GetDevices(path string) ([]*Device, error) { - files, err := ioutilReadDir(path) + files, err := osReadDir(path) if err != nil { return nil, err } @@ -103,7 +103,7 @@ func GetDevices(path string) ([]*Device, error) { } device, err := DeviceFromPath(filepath.Join(path, f.Name()), "rwm") if err != nil { - if err == ErrNotADevice { + if errors.Is(err, ErrNotADevice) { continue } if os.IsNotExist(err) { diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go index 967717a1b..f95c1409f 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go @@ -1,3 +1,4 @@ +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris // +build darwin dragonfly freebsd linux netbsd openbsd solaris package user diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/user.go b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go index cc7a106be..2473c5ead 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/user/user.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go @@ -120,7 +120,7 @@ func ParsePasswdFileFilter(path string, filter func(User) bool) ([]User, error) func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) { if r == nil { - return nil, fmt.Errorf("nil source for passwd-formatted data") + return nil, errors.New("nil source for passwd-formatted data") } var ( @@ -178,7 +178,7 @@ func ParseGroupFileFilter(path string, filter func(Group) bool) ([]Group, error) func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) { if r == nil { - return nil, fmt.Errorf("nil source for group-formatted data") + return nil, errors.New("nil source for group-formatted data") } rd := bufio.NewReader(r) out := []Group{} @@ -339,7 +339,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) ( if userArg == "" { userArg = strconv.Itoa(user.Uid) } - return nil, fmt.Errorf("unable to find user %s: %v", userArg, err) + return nil, fmt.Errorf("unable to find user %s: %w", userArg, err) } var matchedUserName string @@ -355,7 +355,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) ( if uidErr != nil { // Not numeric. - return nil, fmt.Errorf("unable to find user %s: %v", userArg, ErrNoPasswdEntries) + return nil, fmt.Errorf("unable to find user %s: %w", userArg, ErrNoPasswdEntries) } user.Uid = uidArg @@ -390,7 +390,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) ( return g.Name == groupArg }) if err != nil && group != nil { - return nil, fmt.Errorf("unable to find groups for spec %v: %v", matchedUserName, err) + return nil, fmt.Errorf("unable to find groups for spec %v: %w", matchedUserName, err) } // Only start modifying user.Gid if it is in explicit form. @@ -404,7 +404,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) ( if gidErr != nil { // Not numeric. - return nil, fmt.Errorf("unable to find group %s: %v", groupArg, ErrNoGroupEntries) + return nil, fmt.Errorf("unable to find group %s: %w", groupArg, ErrNoGroupEntries) } user.Gid = gidArg @@ -445,7 +445,7 @@ func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, err return false }) if err != nil { - return nil, fmt.Errorf("Unable to find additional groups %v: %v", additionalGroups, err) + return nil, fmt.Errorf("Unable to find additional groups %v: %w", additionalGroups, err) } } @@ -468,7 +468,8 @@ func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, err if !found { gid, err := strconv.ParseInt(ag, 10, 64) if err != nil { - return nil, fmt.Errorf("Unable to find group %s", ag) + // Not a numeric ID either. + return nil, fmt.Errorf("Unable to find group %s: %w", ag, ErrNoGroupEntries) } // Ensure gid is inside gid range. if gid < minID || gid > maxID { @@ -521,7 +522,7 @@ func ParseSubIDFileFilter(path string, filter func(SubID) bool) ([]SubID, error) func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) { if r == nil { - return nil, fmt.Errorf("nil source for subid-formatted data") + return nil, errors.New("nil source for subid-formatted data") } var ( @@ -574,7 +575,7 @@ func ParseIDMapFileFilter(path string, filter func(IDMap) bool) ([]IDMap, error) func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) { if r == nil { - return nil, fmt.Errorf("nil source for idmap-formatted data") + return nil, errors.New("nil source for idmap-formatted data") } var ( diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/user_fuzzer.go b/vendor/github.com/opencontainers/runc/libcontainer/user/user_fuzzer.go index 8c9bb5df3..e018eae61 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/user/user_fuzzer.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/user/user_fuzzer.go @@ -1,3 +1,4 @@ +//go:build gofuzz // +build gofuzz package user diff --git a/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_fuzzer.go b/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_fuzzer.go index 529f8eaea..1e00ab8b5 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_fuzzer.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_fuzzer.go @@ -1,3 +1,4 @@ +//go:build gofuzz // +build gofuzz package userns diff --git a/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_unsupported.go b/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_unsupported.go index f45bb0c31..f35c13a10 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_unsupported.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_unsupported.go @@ -1,3 +1,4 @@ +//go:build !linux // +build !linux package userns diff --git a/vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go b/vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go index c8a9364d5..7ef9da21f 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go @@ -1,5 +1,3 @@ -// +build linux - package utils /* @@ -88,6 +86,11 @@ func SendFd(socket *os.File, name string, fd uintptr) error { if len(name) >= MaxNameLen { return fmt.Errorf("sendfd: filename too long: %s", name) } - oob := unix.UnixRights(int(fd)) - return unix.Sendmsg(int(socket.Fd()), []byte(name), oob, nil, 0) + return SendFds(socket, []byte(name), int(fd)) +} + +// SendFds sends a list of files descriptor and msg over the given AF_UNIX socket. +func SendFds(socket *os.File, msg []byte, fds ...int) error { + oob := unix.UnixRights(fds...) + return unix.Sendmsg(int(socket.Fd()), msg, oob, nil, 0) } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go b/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go index cd78f23e1..6b9fc3435 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go @@ -11,7 +11,7 @@ import ( "strings" "unsafe" - "github.com/cyphar/filepath-securejoin" + securejoin "github.com/cyphar/filepath-securejoin" "golang.org/x/sys/unix" ) @@ -33,16 +33,6 @@ func init() { } } -// ResolveRootfs ensures that the current working directory is -// not a symlink and returns the absolute path to the rootfs -func ResolveRootfs(uncleanRootfs string) (string, error) { - rootfs, err := filepath.Abs(uncleanRootfs) - if err != nil { - return "", err - } - return filepath.EvalSymlinks(rootfs) -} - // ExitStatus returns the correct exit status for a process based on if it // was signaled or exited cleanly func ExitStatus(status unix.WaitStatus) int { @@ -120,7 +110,7 @@ func WithProcfd(root, unsafePath string, fn func(procfd string) error) error { unsafePath = stripRoot(root, unsafePath) path, err := securejoin.SecureJoin(root, unsafePath) if err != nil { - return fmt.Errorf("resolving path inside rootfs failed: %v", err) + return fmt.Errorf("resolving path inside rootfs failed: %w", err) } // Open the target path. diff --git a/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go b/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go index 1576f2d4a..220d0b439 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package utils @@ -14,7 +15,7 @@ import ( func EnsureProcHandle(fh *os.File) error { var buf unix.Statfs_t if err := unix.Fstatfs(int(fh.Fd()), &buf); err != nil { - return fmt.Errorf("ensure %s is on procfs: %v", fh.Name(), err) + return fmt.Errorf("ensure %s is on procfs: %w", fh.Name(), err) } if buf.Type != unix.PROC_SUPER_MAGIC { return fmt.Errorf("%s is not on procfs", fh.Name()) @@ -52,7 +53,7 @@ func CloseExecFrom(minFd int) error { // Intentionally ignore errors from unix.CloseOnExec -- the cases where // this might fail are basically file descriptors that have already // been closed (including and especially the one that was created when - // ioutil.ReadDir did the "opendir" syscall). + // os.ReadDir did the "opendir" syscall). unix.CloseOnExec(fd) } return nil diff --git a/vendor/modules.txt b/vendor/modules.txt index 16daeeaa3..49dc79d62 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -44,8 +44,8 @@ github.com/c9s/goprocinfo/linux # github.com/cespare/xxhash/v2 v2.1.1 ## explicit; go 1.11 github.com/cespare/xxhash/v2 -# github.com/cilium/ebpf v0.6.2 -## explicit; go 1.15 +# github.com/cilium/ebpf v0.7.0 +## explicit; go 1.16 github.com/cilium/ebpf github.com/cilium/ebpf/asm github.com/cilium/ebpf/internal @@ -68,8 +68,8 @@ github.com/coreos/go-systemd/v22/dbus ## explicit; go 1.13 github.com/coreos/prometheus-operator/pkg/apis/monitoring github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1 -# github.com/cyphar/filepath-securejoin v0.2.2 -## explicit +# github.com/cyphar/filepath-securejoin v0.2.3 +## explicit; go 1.13 github.com/cyphar/filepath-securejoin # github.com/davecgh/go-spew v1.1.1 ## explicit @@ -147,7 +147,7 @@ github.com/go-stack/stack # github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 ## explicit; go 1.13 github.com/go-task/slim-sprig -# github.com/godbus/dbus/v5 v5.0.4 +# github.com/godbus/dbus/v5 v5.0.6 ## explicit; go 1.12 github.com/godbus/dbus/v5 # github.com/gogo/protobuf v1.3.2 @@ -270,8 +270,8 @@ github.com/mitchellh/mapstructure ## explicit; go 1.13 github.com/moby/spdystream github.com/moby/spdystream/spdy -# github.com/moby/sys/mountinfo v0.4.1 -## explicit; go 1.14 +# github.com/moby/sys/mountinfo v0.5.0 +## explicit; go 1.16 github.com/moby/sys/mountinfo # github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd ## explicit @@ -317,8 +317,8 @@ github.com/onsi/gomega/matchers/support/goraph/edge github.com/onsi/gomega/matchers/support/goraph/node github.com/onsi/gomega/matchers/support/goraph/util github.com/onsi/gomega/types -# github.com/opencontainers/runc v1.0.3 -## explicit; go 1.13 +# github.com/opencontainers/runc v1.1.2 +## explicit; go 1.16 github.com/opencontainers/runc/libcontainer/cgroups github.com/opencontainers/runc/libcontainer/cgroups/devices github.com/opencontainers/runc/libcontainer/cgroups/ebpf @@ -334,7 +334,7 @@ github.com/opencontainers/runc/libcontainer/utils # github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 ## explicit github.com/opencontainers/runtime-spec/specs-go -# github.com/opencontainers/selinux v1.8.2 => github.com/opencontainers/selinux v1.6.0 +# github.com/opencontainers/selinux v1.10.0 => github.com/opencontainers/selinux v1.6.0 ## explicit; go 1.13 github.com/opencontainers/selinux/go-selinux github.com/opencontainers/selinux/pkg/pwalk -- 2.37.1
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor