From c8dee73f86a5b915ad3bbbbe0c2bc1a36ffe77fe Mon Sep 17 00:00:00 2001 From: CI Bot Date: Sat, 26 Oct 2024 10:02:36 +0000 Subject: [PATCH] Update vendored dependencies --- src/bpm/go.mod | 11 +- src/bpm/go.sum | 22 +- .../vendor/github.com/moby/sys/userns/LICENSE | 202 ++++++ .../github.com/moby/sys/userns/userns.go | 16 + .../moby/sys/userns/userns_linux.go | 53 ++ .../moby/sys/userns/userns_linux_fuzzer.go | 8 + .../moby/sys/userns/userns_unsupported.go | 6 + .../github.com/opencontainers/runc/NOTICE | 4 +- .../runc/libcontainer/cgroups/cgroups.go | 21 + .../runc/libcontainer/cgroups/file.go | 44 +- .../runc/libcontainer/cgroups/stats.go | 27 +- .../runc/libcontainer/cgroups/utils.go | 114 ++-- .../runc/libcontainer/cgroups/v1_utils.go | 31 +- .../runc/libcontainer/configs/blkio_device.go | 8 +- .../runc/libcontainer/configs/cgroup_linux.go | 11 + .../configs/cgroup_unsupported.go | 1 - .../runc/libcontainer/configs/config.go | 122 +++- .../runc/libcontainer/configs/config_linux.go | 31 +- .../libcontainer/configs/configs_fuzzer.go | 1 - .../runc/libcontainer/configs/mount.go | 43 +- .../runc/libcontainer/configs/mount_linux.go | 66 ++ .../libcontainer/configs/mount_unsupported.go | 9 + .../libcontainer/configs/namespaces_linux.go | 7 + .../configs/namespaces_syscall.go | 14 +- .../configs/namespaces_syscall_unsupported.go | 1 - .../configs/namespaces_unsupported.go | 1 - .../runc/libcontainer/devices/device_unix.go | 1 - .../runc/libcontainer/system/linux.go | 145 ----- .../runc/libcontainer/system/proc.go | 127 ---- .../runc/libcontainer/system/rlimit_linux.go | 15 - .../libcontainer/system/rlimit_linux_go122.go | 27 - .../runc/libcontainer/system/rlimit_stub.go | 7 - .../libcontainer/system/syscall_linux_32.go | 27 - .../libcontainer/system/syscall_linux_64.go | 27 - .../runc/libcontainer/user/lookup_unix.go | 157 ----- .../runc/libcontainer/user/user.go | 604 ------------------ .../runc/libcontainer/user/user_fuzzer.go | 43 -- .../runc/libcontainer/userns/userns.go | 5 - .../runc/libcontainer/userns/userns_fuzzer.go | 16 - .../runc/libcontainer/userns/userns_linux.go | 37 -- .../runc/libcontainer/userns/userns_maps.c | 79 --- .../libcontainer/userns/userns_maps_linux.go | 186 ------ .../libcontainer/userns/userns_unsupported.go | 18 - .../runc/libcontainer/utils/cmsg.go | 85 ++- .../runc/libcontainer/utils/utils.go | 84 +-- .../runc/libcontainer/utils/utils_unix.go | 257 ++++++-- src/bpm/vendor/modules.txt | 18 +- 47 files changed, 950 insertions(+), 1889 deletions(-) create mode 100644 src/bpm/vendor/github.com/moby/sys/userns/LICENSE create mode 100644 src/bpm/vendor/github.com/moby/sys/userns/userns.go create mode 100644 src/bpm/vendor/github.com/moby/sys/userns/userns_linux.go create mode 100644 src/bpm/vendor/github.com/moby/sys/userns/userns_linux_fuzzer.go create mode 100644 src/bpm/vendor/github.com/moby/sys/userns/userns_unsupported.go create mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/mount_linux.go create mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/mount_unsupported.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/proc.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux_go122.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_stub.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_32.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_64.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/user/user.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/user/user_fuzzer.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_fuzzer.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_linux.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_maps.c delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_maps_linux.go delete mode 100644 src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_unsupported.go diff --git a/src/bpm/go.mod b/src/bpm/go.mod index bd6a45ef..1f076d1e 100644 --- a/src/bpm/go.mod +++ b/src/bpm/go.mod @@ -3,15 +3,15 @@ module bpm go 1.22.0 require ( - code.cloudfoundry.org/bytefmt v0.13.0 - code.cloudfoundry.org/clock v1.16.0 - code.cloudfoundry.org/lager/v3 v3.10.0 + code.cloudfoundry.org/bytefmt v0.14.0 + code.cloudfoundry.org/clock v1.17.0 + code.cloudfoundry.org/lager/v3 v3.11.0 github.com/cyphar/filepath-securejoin v0.3.4 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/kr/pty v1.1.8 github.com/moby/sys/mountinfo v0.7.2 github.com/onsi/gomega v1.34.2 - github.com/opencontainers/runc v1.1.15 + github.com/opencontainers/runc v1.2.0 github.com/opencontainers/runtime-spec v1.2.0 github.com/satori/go.uuid v1.2.0 github.com/spf13/cobra v1.8.1 @@ -31,8 +31,9 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20241017200806-017d972448fc // indirect + github.com/google/pprof v0.0.0-20241023014458-598669927662 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect diff --git a/src/bpm/go.sum b/src/bpm/go.sum index 679a2740..a86220e2 100644 --- a/src/bpm/go.sum +++ b/src/bpm/go.sum @@ -1,9 +1,9 @@ -code.cloudfoundry.org/bytefmt v0.13.0 h1:T8tgTe4CoM8QFWBJ1i6UEz1Q2pi0i3r24tHUFqvD36Y= -code.cloudfoundry.org/bytefmt v0.13.0/go.mod h1:hYyMgjFZOCFaMfibVRhfVDaxub983kKGA/YiRHdciYw= -code.cloudfoundry.org/clock v1.16.0 h1:55I1lelxZn45V1DxDGCiwNc6dEXk1KQ2CuYKlSMo948= -code.cloudfoundry.org/clock v1.16.0/go.mod h1:pYcfbpnOG23567+Mafw9J+aKfKbmD9fegEQxAsks8y0= -code.cloudfoundry.org/lager/v3 v3.10.0 h1:63K7qjkVFLEU1Yz3BOcMigRyMh5y7T+mWHLBoQzoczA= -code.cloudfoundry.org/lager/v3 v3.10.0/go.mod h1:k3MEaJUFTzPYosFWnzaS9ojfiYw/vqNqApOK2RIqgQY= +code.cloudfoundry.org/bytefmt v0.14.0 h1:F109/rZI5mLOP2+BLj9TOzz3M3qVQ/F1IrI1fsRzakU= +code.cloudfoundry.org/bytefmt v0.14.0/go.mod h1:KkTqS6HXZkTOfw2Wvm0+R82P+p13nVS7bQddej57WJI= +code.cloudfoundry.org/clock v1.17.0 h1:0Zo1EIJkYSQdo6vv97rt5gzSXymjyXnjOzashoKEO6w= +code.cloudfoundry.org/clock v1.17.0/go.mod h1:5SbAgLf/ZTQY11g3jB68e94/DKtZHr9DJbD8h6xUdTg= +code.cloudfoundry.org/lager/v3 v3.11.0 h1:YjRSl1USPIz1FFZPVvwFYgXUUodSIWDdXaLVvME3jZg= +code.cloudfoundry.org/lager/v3 v3.11.0/go.mod h1:8Ot/5fRETEzdf9U3N1kCWp00oZteNwew5Cl2Ck3t2ho= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -23,20 +23,22 @@ github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20241017200806-017d972448fc h1:NGyrhhFhwvRAZg02jnYVg3GBQy0qGBKmFQJwaPmpmxs= -github.com/google/pprof v0.0.0-20241017200806-017d972448fc/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241023014458-598669927662 h1:SKMkD83p7FwUqKmBsPdLHF5dNyxq3jOWwu9w9UyH5vA= +github.com/google/pprof v0.0.0-20241023014458-598669927662/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= -github.com/opencontainers/runc v1.1.15 h1:QMmSU2q1YUg3iOJX11phnaDi2A5/zhx4BR6h+XZ1DMA= -github.com/opencontainers/runc v1.1.15/go.mod h1:E4C2z+7BxR7GHXp0hAY53mek+x49X1LjPNeMTfRGvOA= +github.com/opencontainers/runc v1.2.0 h1:qke7ZVCmJcKrJVY2iHJVC+0kql9uYdkusOPsQOOeBw4= +github.com/opencontainers/runc v1.2.0/go.mod h1:/PXzF0h531HTMsYQnmxXkBD7YaGShm/2zcRB79dksUc= github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= diff --git a/src/bpm/vendor/github.com/moby/sys/userns/LICENSE b/src/bpm/vendor/github.com/moby/sys/userns/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/src/bpm/vendor/github.com/moby/sys/userns/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/bpm/vendor/github.com/moby/sys/userns/userns.go b/src/bpm/vendor/github.com/moby/sys/userns/userns.go new file mode 100644 index 00000000..56b24c44 --- /dev/null +++ b/src/bpm/vendor/github.com/moby/sys/userns/userns.go @@ -0,0 +1,16 @@ +// Package userns provides utilities to detect whether we are currently running +// in a Linux user namespace. +// +// This code was migrated from [libcontainer/runc], which based its implementation +// on code from [lcx/incus]. +// +// [libcontainer/runc]: https://github.com/opencontainers/runc/blob/3778ae603c706494fd1e2c2faf83b406e38d687d/libcontainer/userns/userns_linux.go#L12-L49 +// [lcx/incus]: https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700 +package userns + +// RunningInUserNS detects whether we are currently running in a Linux +// user namespace and memoizes the result. It returns false on non-Linux +// platforms. +func RunningInUserNS() bool { + return inUserNS() +} diff --git a/src/bpm/vendor/github.com/moby/sys/userns/userns_linux.go b/src/bpm/vendor/github.com/moby/sys/userns/userns_linux.go new file mode 100644 index 00000000..87c1c38e --- /dev/null +++ b/src/bpm/vendor/github.com/moby/sys/userns/userns_linux.go @@ -0,0 +1,53 @@ +package userns + +import ( + "bufio" + "fmt" + "os" + "sync" +) + +var inUserNS = sync.OnceValue(runningInUserNS) + +// runningInUserNS detects whether we are currently running in a user namespace. +// +// This code was migrated from [libcontainer/runc] and based on an implementation +// from [lcx/incus]. +// +// [libcontainer/runc]: https://github.com/opencontainers/runc/blob/3778ae603c706494fd1e2c2faf83b406e38d687d/libcontainer/userns/userns_linux.go#L12-L49 +// [lcx/incus]: https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700 +func runningInUserNS() bool { + file, err := os.Open("/proc/self/uid_map") + if err != nil { + // This kernel-provided file only exists if user namespaces are supported. + return false + } + defer file.Close() + + buf := bufio.NewReader(file) + l, _, err := buf.ReadLine() + if err != nil { + return false + } + + return uidMapInUserNS(string(l)) +} + +func uidMapInUserNS(uidMap string) bool { + if uidMap == "" { + // File exist but empty (the initial state when userns is created, + // see user_namespaces(7)). + return true + } + + var a, b, c int64 + if _, err := fmt.Sscanf(uidMap, "%d %d %d", &a, &b, &c); err != nil { + // Assume we are in a regular, non user namespace. + return false + } + + // As per user_namespaces(7), /proc/self/uid_map of + // the initial user namespace shows 0 0 4294967295. + initNS := a == 0 && b == 0 && c == 4294967295 + return !initNS +} diff --git a/src/bpm/vendor/github.com/moby/sys/userns/userns_linux_fuzzer.go b/src/bpm/vendor/github.com/moby/sys/userns/userns_linux_fuzzer.go new file mode 100644 index 00000000..26ba2e16 --- /dev/null +++ b/src/bpm/vendor/github.com/moby/sys/userns/userns_linux_fuzzer.go @@ -0,0 +1,8 @@ +//go:build linux && gofuzz + +package userns + +func FuzzUIDMap(uidmap []byte) int { + _ = uidMapInUserNS(string(uidmap)) + return 1 +} diff --git a/src/bpm/vendor/github.com/moby/sys/userns/userns_unsupported.go b/src/bpm/vendor/github.com/moby/sys/userns/userns_unsupported.go new file mode 100644 index 00000000..8ed83072 --- /dev/null +++ b/src/bpm/vendor/github.com/moby/sys/userns/userns_unsupported.go @@ -0,0 +1,6 @@ +//go:build !linux + +package userns + +// inUserNS is a stub for non-Linux systems. Always returns false. +func inUserNS() bool { return false } diff --git a/src/bpm/vendor/github.com/opencontainers/runc/NOTICE b/src/bpm/vendor/github.com/opencontainers/runc/NOTICE index 5c97abce..c29775c0 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/NOTICE +++ b/src/bpm/vendor/github.com/opencontainers/runc/NOTICE @@ -8,9 +8,9 @@ The following is courtesy of our legal counsel: Use and transfer of Docker may be subject to certain restrictions by the -United States and other governments. +United States and other governments. It is your responsibility to ensure that your use and/or transfer does not -violate applicable laws. +violate applicable laws. For more information, please see http://www.bis.doc.gov diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go index ba2b2266..53e194c7 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/cgroups.go @@ -1,9 +1,30 @@ package cgroups import ( + "errors" + "github.com/opencontainers/runc/libcontainer/configs" ) +var ( + // ErrDevicesUnsupported is an error returned when a cgroup manager + // is not configured to set device rules. + ErrDevicesUnsupported = errors.New("cgroup manager is not configured to set device rules") + + // ErrRootless is returned by [Manager.Apply] when there is an error + // creating cgroup directory, and cgroup.Rootless is set. In general, + // this error is to be ignored. + ErrRootless = errors.New("cgroup manager can not access cgroup (rootless container)") + + // DevicesSetV1 and DevicesSetV2 are functions to set devices for + // cgroup v1 and v2, respectively. Unless + // [github.com/opencontainers/runc/libcontainer/cgroups/devices] + // package is imported, it is set to nil, so cgroup managers can't + // manage devices. + DevicesSetV1 func(path string, r *configs.Resources) error + DevicesSetV2 func(path string, r *configs.Resources) error +) + type Manager interface { // Apply creates a cgroup, if not yet created, and adds a process // with the specified pid into that cgroup. A special value of -1 diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/file.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/file.go index f6e1b73b..78c5bcf0 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/file.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/file.go @@ -50,22 +50,45 @@ func WriteFile(dir, file, data string) error { return err } defer fd.Close() - if err := retryingWriteFile(fd, data); err != nil { + if _, err := fd.WriteString(data); err != nil { // Having data in the error message helps in debugging. return fmt.Errorf("failed to write %q: %w", data, err) } return nil } -func retryingWriteFile(fd *os.File, data string) error { +// WriteFileByLine is the same as WriteFile, except if data contains newlines, +// it is written line by line. +func WriteFileByLine(dir, file, data string) error { + i := strings.Index(data, "\n") + if i == -1 { + return WriteFile(dir, file, data) + } + + fd, err := OpenFile(dir, file, unix.O_WRONLY) + if err != nil { + return err + } + defer fd.Close() + start := 0 for { - _, err := fd.Write([]byte(data)) - if errors.Is(err, unix.EINTR) { - logrus.Infof("interrupted while writing %s to %s", data, fd.Name()) - continue + var line string + if i == -1 { + line = data[start:] + } else { + line = data[start : start+i+1] } - return err + _, err := fd.WriteString(line) + if err != nil { + return fmt.Errorf("failed to write %q: %w", line, err) + } + if i == -1 { + break + } + start += i + 1 + i = strings.Index(data[start:], "\n") } + return nil } const ( @@ -90,7 +113,7 @@ func prepareOpenat2() error { }) if err != nil { prepErr = &os.PathError{Op: "openat2", Path: cgroupfsDir, Err: err} - if err != unix.ENOSYS { //nolint:errorlint // unix errors are bare + if err != unix.ENOSYS { logrus.Warnf("falling back to securejoin: %s", prepErr) } else { logrus.Debug("openat2 not available, falling back to securejoin") @@ -148,8 +171,9 @@ func openFile(dir, file string, flags int) (*os.File, error) { // // TODO: if such usage will ever be common, amend this // to reopen cgroupRootHandle and retry openat2. - fdStr := strconv.Itoa(int(cgroupRootHandle.Fd())) - fdDest, _ := os.Readlink("/proc/self/fd/" + fdStr) + fdPath, closer := utils.ProcThreadSelf("fd/" + strconv.Itoa(int(cgroupRootHandle.Fd()))) + defer closer() + fdDest, _ := os.Readlink(fdPath) if fdDest != cgroupfsDir { // Wrap the error so it is clear that cgroupRootHandle // is opened to an unexpected/wrong directory. diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go index 0d8371b0..b475567d 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go @@ -32,9 +32,22 @@ type CpuUsage struct { UsageInUsermode uint64 `json:"usage_in_usermode"` } +type PSIData struct { + Avg10 float64 `json:"avg10"` + Avg60 float64 `json:"avg60"` + Avg300 float64 `json:"avg300"` + Total uint64 `json:"total"` +} + +type PSIStats struct { + Some PSIData `json:"some,omitempty"` + Full PSIData `json:"full,omitempty"` +} + type CpuStats struct { CpuUsage CpuUsage `json:"cpu_usage,omitempty"` ThrottlingData ThrottlingData `json:"throttling_data,omitempty"` + PSI *PSIStats `json:"psi,omitempty"` } type CPUSetStats struct { @@ -91,6 +104,7 @@ type MemoryStats struct { UseHierarchy bool `json:"use_hierarchy"` Stats map[string]uint64 `json:"stats,omitempty"` + PSI *PSIStats `json:"psi,omitempty"` } type PageUsageByNUMA struct { @@ -135,6 +149,7 @@ type BlkioStats struct { IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive,omitempty"` IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive,omitempty"` SectorsRecursive []BlkioStatEntry `json:"sectors_recursive,omitempty"` + PSI *PSIStats `json:"psi,omitempty"` } type HugetlbStats struct { @@ -157,6 +172,13 @@ type RdmaStats struct { RdmaCurrent []RdmaEntry `json:"rdma_current,omitempty"` } +type MiscStats struct { + // current resource usage for a key in misc + Usage uint64 `json:"usage,omitempty"` + // number of times the resource usage was about to go over the max boundary + Events uint64 `json:"events,omitempty"` +} + type Stats struct { CpuStats CpuStats `json:"cpu_stats,omitempty"` CPUSetStats CPUSetStats `json:"cpuset_stats,omitempty"` @@ -166,10 +188,13 @@ type Stats struct { // 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"` + // the map is in the format "misc resource name: stats of the key" + MiscStats map[string]MiscStats `json:"misc_stats,omitempty"` } func NewStats() *Stats { memoryStats := MemoryStats{Stats: make(map[string]uint64)} hugetlbStats := make(map[string]HugetlbStats) - return &Stats{MemoryStats: memoryStats, HugetlbStats: hugetlbStats} + miscStats := make(map[string]MiscStats) + return &Stats{MemoryStats: memoryStats, HugetlbStats: hugetlbStats, MiscStats: miscStats} } diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go index fc4ae44a..67341e69 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go @@ -12,7 +12,7 @@ import ( "sync" "time" - "github.com/opencontainers/runc/libcontainer/userns" + "github.com/moby/sys/userns" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -36,13 +36,13 @@ func IsCgroup2UnifiedMode() bool { var st unix.Statfs_t err := unix.Statfs(unifiedMountpoint, &st) if err != nil { + level := logrus.WarnLevel if os.IsNotExist(err) && userns.RunningInUserNS() { - // ignore the "not found" error if running in userns - logrus.WithError(err).Debugf("%s missing, assuming cgroup v1", unifiedMountpoint) - isUnified = false - return + // For rootless containers, sweep it under the rug. + level = logrus.DebugLevel } - panic(fmt.Sprintf("cannot statfs cgroup root: %s", err)) + logrus.StandardLogger().Logf(level, + "statfs %s: %v; assuming cgroup v1", unifiedMountpoint, err) } isUnified = st.Type == unix.CGROUP2_SUPER_MAGIC }) @@ -136,18 +136,18 @@ func GetAllSubsystems() ([]string, error) { return subsystems, nil } -func readProcsFile(dir string) ([]int, error) { - f, err := OpenFile(dir, CgroupProcesses, os.O_RDONLY) +func readProcsFile(dir string) (out []int, _ error) { + file := CgroupProcesses + retry := true + +again: + f, err := OpenFile(dir, file, os.O_RDONLY) if err != nil { return nil, err } defer f.Close() - var ( - s = bufio.NewScanner(f) - out = []int{} - ) - + s := bufio.NewScanner(f) for s.Scan() { if t := s.Text(); t != "" { pid, err := strconv.Atoi(t) @@ -157,6 +157,13 @@ func readProcsFile(dir string) ([]int, error) { out = append(out, pid) } } + if errors.Is(s.Err(), unix.ENOTSUP) && retry { + // For a threaded cgroup, read returns ENOTSUP, and we should + // read from cgroup.threads instead. + file = "cgroup.threads" + retry = false + goto again + } return out, s.Err() } @@ -217,21 +224,26 @@ func PathExists(path string) bool { return true } -func EnterPid(cgroupPaths map[string]string, pid int) error { - for _, path := range cgroupPaths { - if PathExists(path) { - if err := WriteCgroupProc(path, pid); err != nil { - return err - } - } - } - return nil -} +// rmdir tries to remove a directory, optionally retrying on EBUSY. +func rmdir(path string, retry bool) error { + delay := time.Millisecond + tries := 10 -func rmdir(path string) error { +again: err := unix.Rmdir(path) - if err == nil || err == unix.ENOENT { //nolint:errorlint // unix errors are bare + switch err { // nolint:errorlint // unix errors are bare + case nil, unix.ENOENT: return nil + case unix.EINTR: + goto again + case unix.EBUSY: + if retry && tries > 0 { + time.Sleep(delay) + delay *= 2 + tries-- + goto again + + } } return &os.PathError{Op: "rmdir", Path: path, Err: err} } @@ -239,68 +251,40 @@ func rmdir(path string) error { // RemovePath aims to remove cgroup path. It does so recursively, // by removing any subdirectories (sub-cgroups) first. func RemovePath(path string) error { - // try the fast path first - if err := rmdir(path); err == nil { + // Try the fast path first. + if err := rmdir(path, false); err == nil { return nil } infos, err := os.ReadDir(path) - if err != nil { - if os.IsNotExist(err) { - err = nil - } + if err != nil && !os.IsNotExist(err) { return err } for _, info := range infos { if info.IsDir() { - // We should remove subcgroups dir first + // We should remove subcgroup first. if err = RemovePath(filepath.Join(path, info.Name())); err != nil { break } } } if err == nil { - err = rmdir(path) + err = rmdir(path, true) } return err } // RemovePaths iterates over the provided paths removing them. -// We trying to remove all paths five times with increasing delay between tries. -// If after all there are not removed cgroups - appropriate error will be -// returned. func RemovePaths(paths map[string]string) (err error) { - const retries = 5 - delay := 10 * time.Millisecond - for i := 0; i < retries; i++ { - if i != 0 { - time.Sleep(delay) - delay *= 2 - } - for s, p := range paths { - if err := RemovePath(p); err != nil { - // do not log intermediate iterations - switch i { - case 0: - logrus.WithError(err).Warnf("Failed to remove cgroup (will retry)") - case retries - 1: - logrus.WithError(err).Error("Failed to remove cgroup") - } - } - _, err := os.Stat(p) - // We need this strange way of checking cgroups existence because - // RemoveAll almost always returns error, even on already removed - // cgroups - if os.IsNotExist(err) { - delete(paths, s) - } - } - if len(paths) == 0 { - //nolint:ineffassign,staticcheck // done to help garbage collecting: opencontainers/runc#2506 - paths = make(map[string]string) - return nil + for s, p := range paths { + if err := RemovePath(p); err == nil { + delete(paths, s) } } + if len(paths) == 0 { + clear(paths) + return nil + } return fmt.Errorf("Failed to remove paths: %v", paths) } diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go index 47c75f22..81193e20 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/cgroups/v1_utils.go @@ -99,11 +99,12 @@ func tryDefaultPath(cgroupPath, subsystem string) string { // expensive), so it is assumed that cgroup mounts are not being changed. func readCgroupMountinfo() ([]*mountinfo.Info, error) { readMountinfoOnce.Do(func() { + // mountinfo.GetMounts uses /proc/thread-self, so we can use it without + // issues. cgroupMountinfo, readMountinfoErr = mountinfo.GetMounts( mountinfo.FSTypeFilter("cgroup"), ) }) - return cgroupMountinfo, readMountinfoErr } @@ -196,6 +197,9 @@ func getCgroupMountsV1(all bool) ([]Mount, error) { return nil, err } + // We don't need to use /proc/thread-self here because runc always runs + // with every thread in the same cgroup. This lets us avoid having to do + // runtime.LockOSThread. allSubsystems, err := ParseCgroupFile("/proc/self/cgroup") if err != nil { return nil, err @@ -214,6 +218,10 @@ func GetOwnCgroup(subsystem string) (string, error) { if IsCgroup2UnifiedMode() { return "", errUnified } + + // We don't need to use /proc/thread-self here because runc always runs + // with every thread in the same cgroup. This lets us avoid having to do + // runtime.LockOSThread. cgroups, err := ParseCgroupFile("/proc/self/cgroup") if err != nil { return "", err @@ -236,27 +244,6 @@ func GetOwnCgroupPath(subsystem string) (string, error) { return getCgroupPathHelper(subsystem, cgroup) } -func GetInitCgroup(subsystem string) (string, error) { - if IsCgroup2UnifiedMode() { - return "", errUnified - } - cgroups, err := ParseCgroupFile("/proc/1/cgroup") - if err != nil { - return "", err - } - - return getControllerPath(subsystem, cgroups) -} - -func GetInitCgroupPath(subsystem string) (string, error) { - cgroup, err := GetInitCgroup(subsystem) - if err != nil { - return "", err - } - - return getCgroupPathHelper(subsystem, cgroup) -} - func getCgroupPathHelper(subsystem, cgroup string) (string, error) { mnt, root, err := FindCgroupMountpointAndRoot("", subsystem) if err != nil { diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/blkio_device.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/blkio_device.go index fa195bf9..865344f9 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/blkio_device.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/blkio_device.go @@ -2,8 +2,8 @@ package configs import "fmt" -// blockIODevice holds major:minor format supported in blkio cgroup -type blockIODevice struct { +// BlockIODevice holds major:minor format supported in blkio cgroup. +type BlockIODevice struct { // Major is the device's major number Major int64 `json:"major"` // Minor is the device's minor number @@ -12,7 +12,7 @@ type blockIODevice struct { // WeightDevice struct holds a `major:minor weight`|`major:minor leaf_weight` pair type WeightDevice struct { - blockIODevice + BlockIODevice // Weight is the bandwidth rate for the device, range is from 10 to 1000 Weight uint16 `json:"weight"` // LeafWeight is the bandwidth rate for the device while competing with the cgroup's child cgroups, range is from 10 to 1000, cfq scheduler only @@ -41,7 +41,7 @@ func (wd *WeightDevice) LeafWeightString() string { // ThrottleDevice struct holds a `major:minor rate_per_second` pair type ThrottleDevice struct { - blockIODevice + BlockIODevice // Rate is the IO rate limit per cgroup per device Rate uint64 `json:"rate"` } diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go index 2d4a8987..4a34cf76 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go @@ -69,6 +69,9 @@ type Resources struct { // CPU hardcap limit (in usecs). Allowed cpu time in a given period. CpuQuota int64 `json:"cpu_quota"` + // CPU hardcap burst limit (in usecs). Allowed accumulated cpu time additionally for burst in a given period. + CpuBurst *uint64 `json:"cpu_burst"` //nolint:revive + // CPU period to be used for hardcapping (in usecs). 0 to use system default. CpuPeriod uint64 `json:"cpu_period"` @@ -84,6 +87,9 @@ type Resources struct { // MEM to use CpusetMems string `json:"cpuset_mems"` + // cgroup SCHED_IDLE + CPUIdle *int64 `json:"cpu_idle,omitempty"` + // Process limit; set <= `0' to disable limit. PidsLimit int64 `json:"pids_limit"` @@ -155,4 +161,9 @@ type Resources struct { // during Set() to figure out whether the freeze is required. Those // methods may be relatively slow, thus this flag. SkipFreezeOnSet bool `json:"-"` + + // MemoryCheckBeforeUpdate is a flag for cgroup v2 managers to check + // if the new memory limits (Memory and MemorySwap) being set are lower + // than the current memory usage, and reject if so. + MemoryCheckBeforeUpdate bool `json:"memory_check_before_update"` } diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unsupported.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unsupported.go index 7e383020..53f5ec5a 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unsupported.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unsupported.go @@ -1,5 +1,4 @@ //go:build !linux -// +build !linux package configs diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go index 6ebf5ec7..22fe0f9b 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go @@ -8,6 +8,7 @@ import ( "time" "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" "github.com/opencontainers/runc/libcontainer/devices" "github.com/opencontainers/runtime-spec/specs-go" @@ -31,12 +32,13 @@ 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"` - ListenerPath string `json:"listener_path,omitempty"` - ListenerMetadata string `json:"listener_metadata,omitempty"` + DefaultAction Action `json:"default_action"` + Architectures []string `json:"architectures"` + Flags []specs.LinuxSeccompFlag `json:"flags"` + 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 @@ -83,9 +85,6 @@ type Syscall struct { Args []*Arg `json:"args"` } -// TODO Windows. Many of these fields should be factored out into those parts -// which are common across platforms, and those which are platform specific. - // Config defines configuration options for executing a process inside a contained environment. type Config struct { // NoPivotRoot will use MS_MOVE and a chroot to jail the process into the container's rootfs @@ -121,6 +120,9 @@ type Config struct { // Hostname optionally sets the container's hostname if provided Hostname string `json:"hostname"` + // Domainname optionally sets the container's domainname if provided + Domainname string `json:"domainname"` + // Namespaces specifies the container's namespaces that it should setup when cloning the init process // If a namespace is not provided that namespace is shared from the container's parent process Namespaces Namespaces `json:"namespaces"` @@ -158,11 +160,11 @@ type Config struct { // More information about kernel oom score calculation here: https://lwn.net/Articles/317814/ OomScoreAdj *int `json:"oom_score_adj,omitempty"` - // UidMappings is an array of User ID mappings for User Namespaces - UidMappings []IDMap `json:"uid_mappings"` + // UIDMappings is an array of User ID mappings for User Namespaces + UIDMappings []IDMap `json:"uid_mappings"` - // GidMappings is an array of Group ID mappings for User Namespaces - GidMappings []IDMap `json:"gid_mappings"` + // GIDMappings is an array of Group ID mappings for User Namespaces + GIDMappings []IDMap `json:"gid_mappings"` // MaskPaths specifies paths within the container's rootfs to mask over with a bind // mount pointing to /dev/null as to prevent reads of the file. @@ -211,8 +213,87 @@ type Config struct { // RootlessCgroups is set when unlikely to have the full access to cgroups. // When RootlessCgroups is set, cgroups errors are ignored. RootlessCgroups bool `json:"rootless_cgroups,omitempty"` + + // TimeOffsets specifies the offset for supporting time namespaces. + TimeOffsets map[string]specs.LinuxTimeOffset `json:"time_offsets,omitempty"` + + // Scheduler represents the scheduling attributes for a process. + Scheduler *Scheduler `json:"scheduler,omitempty"` + + // Personality contains configuration for the Linux personality syscall. + Personality *LinuxPersonality `json:"personality,omitempty"` + + // IOPriority is the container's I/O priority. + IOPriority *IOPriority `json:"io_priority,omitempty"` } +// Scheduler is based on the Linux sched_setattr(2) syscall. +type Scheduler = specs.Scheduler + +// ToSchedAttr is to convert *configs.Scheduler to *unix.SchedAttr. +func ToSchedAttr(scheduler *Scheduler) (*unix.SchedAttr, error) { + var policy uint32 + switch scheduler.Policy { + case specs.SchedOther: + policy = 0 + case specs.SchedFIFO: + policy = 1 + case specs.SchedRR: + policy = 2 + case specs.SchedBatch: + policy = 3 + case specs.SchedISO: + policy = 4 + case specs.SchedIdle: + policy = 5 + case specs.SchedDeadline: + policy = 6 + default: + return nil, fmt.Errorf("invalid scheduler policy: %s", scheduler.Policy) + } + + var flags uint64 + for _, flag := range scheduler.Flags { + switch flag { + case specs.SchedFlagResetOnFork: + flags |= 0x01 + case specs.SchedFlagReclaim: + flags |= 0x02 + case specs.SchedFlagDLOverrun: + flags |= 0x04 + case specs.SchedFlagKeepPolicy: + flags |= 0x08 + case specs.SchedFlagKeepParams: + flags |= 0x10 + case specs.SchedFlagUtilClampMin: + flags |= 0x20 + case specs.SchedFlagUtilClampMax: + flags |= 0x40 + default: + return nil, fmt.Errorf("invalid scheduler flag: %s", flag) + } + } + + return &unix.SchedAttr{ + Size: unix.SizeofSchedAttr, + Policy: policy, + Flags: flags, + Nice: scheduler.Nice, + Priority: uint32(scheduler.Priority), + Runtime: scheduler.Runtime, + Deadline: scheduler.Deadline, + Period: scheduler.Period, + }, nil +} + +var IOPrioClassMapping = map[specs.IOPriorityClass]int{ + specs.IOPRIO_CLASS_RT: 1, + specs.IOPRIO_CLASS_BE: 2, + specs.IOPRIO_CLASS_IDLE: 3, +} + +type IOPriority = specs.LinuxIOPriority + type ( HookName string HookList []Hook @@ -277,6 +358,7 @@ type Capabilities struct { Ambient []string } +// Deprecated: use (Hooks).Run instead. func (hooks HookList) RunHooks(state *specs.State) error { for i, h := range hooks { if err := h.Run(state); err != nil { @@ -333,6 +415,18 @@ func (hooks *Hooks) MarshalJSON() ([]byte, error) { }) } +// Run executes all hooks for the given hook name. +func (hooks Hooks) Run(name HookName, state *specs.State) error { + list := hooks[name] + for i, h := range list { + if err := h.Run(state); err != nil { + return fmt.Errorf("error running %s hook #%d: %w", name, i, err) + } + } + + return nil +} + type Hook interface { // Run executes the hook with the provided state. Run(*specs.State) error @@ -393,7 +487,7 @@ func (c Command) Run(s *specs.State) error { go func() { err := cmd.Wait() if err != nil { - err = fmt.Errorf("error running hook: %w, stdout: %s, stderr: %s", err, stdout.String(), stderr.String()) + err = fmt.Errorf("%w, stdout: %s, stderr: %s", err, stdout.String(), stderr.String()) } errC <- err }() diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/config_linux.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/config_linux.go index 51fe9407..e401f533 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/config_linux.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/config_linux.go @@ -7,22 +7,33 @@ import ( ) 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.") + errNoUIDMap = errors.New("user namespaces enabled, but no uid mappings found") + errNoGIDMap = errors.New("user namespaces enabled, but no gid mappings found") ) +// Please check https://man7.org/linux/man-pages/man2/personality.2.html for const details. +// https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/personality.h +const ( + PerLinux = 0x0000 + PerLinux32 = 0x0008 +) + +type LinuxPersonality struct { + // Domain for the personality + // can only contain values "LINUX" and "LINUX32" + Domain int `json:"domain"` +} + // 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 { + if len(c.UIDMappings) == 0 { return -1, errNoUIDMap } - id, found := c.hostIDFromMapping(int64(containerId), c.UidMappings) + id, found := c.hostIDFromMapping(int64(containerId), c.UIDMappings) if !found { - return -1, errNoUserMap + return -1, fmt.Errorf("user namespaces enabled, but no mapping found for uid %d", containerId) } // If we are a 32-bit binary running on a 64-bit system, it's possible // the mapped user is too large to store in an int, which means we @@ -47,12 +58,12 @@ func (c Config) HostRootUID() (int, error) { // different when user namespaces are enabled. func (c Config) HostGID(containerId int) (int, error) { if c.Namespaces.Contains(NEWUSER) { - if c.GidMappings == nil { + if len(c.GIDMappings) == 0 { return -1, errNoGIDMap } - id, found := c.hostIDFromMapping(int64(containerId), c.GidMappings) + id, found := c.hostIDFromMapping(int64(containerId), c.GIDMappings) if !found { - return -1, errNoGroupMap + return -1, fmt.Errorf("user namespaces enabled, but no mapping found for gid %d", containerId) } // If we are a 32-bit binary running on a 64-bit system, it's possible // the mapped user is too large to store in an int, which means we diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/configs_fuzzer.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/configs_fuzzer.go index bce829e2..1fd87ce6 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/configs_fuzzer.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/configs_fuzzer.go @@ -1,5 +1,4 @@ //go:build gofuzz -// +build gofuzz package configs diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/mount.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/mount.go index 784c6182..bfd356e4 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/mount.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/mount.go @@ -1,48 +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. - EXT_COPYUP = 1 << iota //nolint:golint // ignore "don't use ALL_CAPS" warning + EXT_COPYUP = 1 << iota //nolint:golint,revive // ignore "don't use ALL_CAPS" warning ) - -type Mount struct { - // Source path for the mount. - Source string `json:"source"` - - // Destination path for the mount inside the container. - Destination string `json:"destination"` - - // Device the mount is for. - Device string `json:"device"` - - // Mount flags. - Flags int `json:"flags"` - - // Propagation Flags - PropagationFlags []int `json:"propagation_flags"` - - // Mount data applied to the mount. - Data string `json:"data"` - - // 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"` - - // Optional Command to be run before Source is mounted. - PremountCmds []Command `json:"premount_cmds"` - - // 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/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/mount_linux.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/mount_linux.go new file mode 100644 index 00000000..b69e9ab2 --- /dev/null +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/mount_linux.go @@ -0,0 +1,66 @@ +package configs + +import "golang.org/x/sys/unix" + +type MountIDMapping struct { + // Recursive indicates if the mapping needs to be recursive. + Recursive bool `json:"recursive"` + + // UserNSPath is a path to a user namespace that indicates the necessary + // id-mappings for MOUNT_ATTR_IDMAP. If set to non-"", UIDMappings and + // GIDMappings must be set to nil. + UserNSPath string `json:"userns_path,omitempty"` + + // UIDMappings is the uid mapping set for this mount, to be used with + // MOUNT_ATTR_IDMAP. + UIDMappings []IDMap `json:"uid_mappings,omitempty"` + + // GIDMappings is the gid mapping set for this mount, to be used with + // MOUNT_ATTR_IDMAP. + GIDMappings []IDMap `json:"gid_mappings,omitempty"` +} + +type Mount struct { + // Source path for the mount. + Source string `json:"source"` + + // Destination path for the mount inside the container. + Destination string `json:"destination"` + + // Device the mount is for. + Device string `json:"device"` + + // Mount flags. + Flags int `json:"flags"` + + // Mount flags that were explicitly cleared in the configuration (meaning + // the user explicitly requested that these flags *not* be set). + ClearedFlags int `json:"cleared_flags"` + + // Propagation Flags + PropagationFlags []int `json:"propagation_flags"` + + // Mount data applied to the mount. + Data string `json:"data"` + + // 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"` + + // Mapping is the MOUNT_ATTR_IDMAP configuration for the mount. If non-nil, + // the mount is configured to use MOUNT_ATTR_IDMAP-style id mappings. + IDMapping *MountIDMapping `json:"id_mapping,omitempty"` +} + +func (m *Mount) IsBind() bool { + return m.Flags&unix.MS_BIND != 0 +} + +func (m *Mount) IsIDMapped() bool { + return m.IDMapping != nil +} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/mount_unsupported.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/mount_unsupported.go new file mode 100644 index 00000000..1d4d9fe5 --- /dev/null +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/mount_unsupported.go @@ -0,0 +1,9 @@ +//go:build !linux + +package configs + +type Mount struct{} + +func (m *Mount) IsBind() bool { + return false +} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_linux.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_linux.go index d52d6fcd..898f96fd 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_linux.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_linux.go @@ -14,6 +14,7 @@ const ( NEWIPC NamespaceType = "NEWIPC" NEWUSER NamespaceType = "NEWUSER" NEWCGROUP NamespaceType = "NEWCGROUP" + NEWTIME NamespaceType = "NEWTIME" ) var ( @@ -38,6 +39,8 @@ func NsName(ns NamespaceType) string { return "uts" case NEWCGROUP: return "cgroup" + case NEWTIME: + return "time" } return "" } @@ -56,6 +59,9 @@ func IsNamespaceSupported(ns NamespaceType) bool { if nsFile == "" { return false } + // We don't need to use /proc/thread-self here because the list of + // namespace types is unrelated to the thread. This lets us avoid having to + // do runtime.LockOSThread. _, err := os.Stat("/proc/self/ns/" + nsFile) // a namespace is supported if it exists and we have permissions to read it supported = err == nil @@ -72,6 +78,7 @@ func NamespaceTypes() []NamespaceType { NEWPID, NEWNS, NEWCGROUP, + NEWTIME, } } diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall.go index 0516dba8..26b70b26 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall.go @@ -1,5 +1,4 @@ //go:build linux -// +build linux package configs @@ -17,6 +16,7 @@ var namespaceInfo = map[NamespaceType]int{ NEWUTS: unix.CLONE_NEWUTS, NEWPID: unix.CLONE_NEWPID, NEWCGROUP: unix.CLONE_NEWCGROUP, + NEWTIME: unix.CLONE_NEWTIME, } // CloneFlags parses the container's Namespaces options to set the correct @@ -31,3 +31,15 @@ func (n *Namespaces) CloneFlags() uintptr { } return uintptr(flag) } + +// IsPrivate tells whether the namespace of type t is configured as private +// (i.e. it exists and is not shared). +func (n Namespaces) IsPrivate(t NamespaceType) bool { + for _, v := range n { + if v.Type == t { + return v.Path == "" + } + } + // Not found, so implicitly sharing a parent namespace. + return false +} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall_unsupported.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall_unsupported.go index fbb0d490..10bf2436 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall_unsupported.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_syscall_unsupported.go @@ -1,5 +1,4 @@ //go:build !linux && !windows -// +build !linux,!windows package configs diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go index 946db30a..91468499 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go @@ -1,5 +1,4 @@ //go:build !linux -// +build !linux package configs diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go index 7d8e9fc3..d00775f5 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go @@ -1,5 +1,4 @@ //go:build !windows -// +build !windows package devices diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go deleted file mode 100644 index 32bab692..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go +++ /dev/null @@ -1,145 +0,0 @@ -//go:build linux -// +build linux - -package system - -import ( - "os" - "os/exec" - "runtime" - "strings" - "unsafe" - - "golang.org/x/sys/unix" -) - -type ParentDeathSignal int - -func (p ParentDeathSignal) Restore() error { - if p == 0 { - return nil - } - current, err := GetParentDeathSignal() - if err != nil { - return err - } - if p == current { - return nil - } - return p.Set() -} - -func (p ParentDeathSignal) Set() error { - return SetParentDeathSignal(uintptr(p)) -} - -// Deprecated: Execv is not used in runc anymore, it will be removed in v1.2.0. -func Execv(cmd string, args []string, env []string) error { - name, err := exec.LookPath(cmd) - if err != nil { - return err - } - return Exec(name, args, env) -} - -func Exec(cmd string, args []string, env []string) error { - for { - err := unix.Exec(cmd, args, env) - if err != unix.EINTR { //nolint:errorlint // unix errors are bare - return &os.PathError{Op: "exec", Path: cmd, Err: err} - } - } -} - -func SetParentDeathSignal(sig uintptr) error { - if err := unix.Prctl(unix.PR_SET_PDEATHSIG, sig, 0, 0, 0); err != nil { - return err - } - return nil -} - -func GetParentDeathSignal() (ParentDeathSignal, error) { - var sig int - if err := unix.Prctl(unix.PR_GET_PDEATHSIG, uintptr(unsafe.Pointer(&sig)), 0, 0, 0); err != nil { - return -1, err - } - return ParentDeathSignal(sig), nil -} - -func SetKeepCaps() error { - if err := unix.Prctl(unix.PR_SET_KEEPCAPS, 1, 0, 0, 0); err != nil { - return err - } - - return nil -} - -func ClearKeepCaps() error { - if err := unix.Prctl(unix.PR_SET_KEEPCAPS, 0, 0, 0, 0); err != nil { - return err - } - - return nil -} - -func Setctty() error { - if err := unix.IoctlSetInt(0, unix.TIOCSCTTY, 0); err != nil { - return err - } - return nil -} - -// SetSubreaper sets the value i as the subreaper setting for the calling process -func SetSubreaper(i int) error { - return unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0) -} - -// GetSubreaper returns the subreaper setting for the calling process -func GetSubreaper() (int, error) { - var i uintptr - - if err := unix.Prctl(unix.PR_GET_CHILD_SUBREAPER, uintptr(unsafe.Pointer(&i)), 0, 0, 0); err != nil { - return -1, err - } - - return int(i), nil -} - -func prepareAt(dir *os.File, path string) (int, string) { - if dir == nil { - return unix.AT_FDCWD, path - } - - // Rather than just filepath.Join-ing path here, do it manually so the - // error and handle correctly indicate cases like path=".." as being - // relative to the correct directory. The handle.Name() might end up being - // wrong but because this is (currently) only used in MkdirAllInRoot, that - // isn't a problem. - dirName := dir.Name() - if !strings.HasSuffix(dirName, "/") { - dirName += "/" - } - fullPath := dirName + path - - return int(dir.Fd()), fullPath -} - -func Openat(dir *os.File, path string, flags int, mode uint32) (*os.File, error) { - dirFd, fullPath := prepareAt(dir, path) - fd, err := unix.Openat(dirFd, path, flags, mode) - if err != nil { - return nil, &os.PathError{Op: "openat", Path: fullPath, Err: err} - } - runtime.KeepAlive(dir) - return os.NewFile(uintptr(fd), fullPath), nil -} - -func Mkdirat(dir *os.File, path string, mode uint32) error { - dirFd, fullPath := prepareAt(dir, path) - err := unix.Mkdirat(dirFd, path, mode) - if err != nil { - err = &os.PathError{Op: "mkdirat", Path: fullPath, Err: err} - } - runtime.KeepAlive(dir) - return err -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/proc.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/proc.go deleted file mode 100644 index 774443ec..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/proc.go +++ /dev/null @@ -1,127 +0,0 @@ -package system - -import ( - "fmt" - "os" - "path/filepath" - "strconv" - "strings" -) - -// State is the status of a process. -type State rune - -const ( // Only values for Linux 3.14 and later are listed here - Dead State = 'X' - DiskSleep State = 'D' - Running State = 'R' - Sleeping State = 'S' - Stopped State = 'T' - TracingStop State = 't' - Zombie State = 'Z' - Parked State = 'P' - Idle State = 'I' -) - -// String forms of the state from proc(5)'s documentation for -// /proc/[pid]/status' "State" field. -func (s State) String() string { - switch s { - case Dead: - return "dead" - case DiskSleep: - return "disk sleep" - case Running: - return "running" - case Sleeping: - return "sleeping" - case Stopped: - return "stopped" - case TracingStop: - return "tracing stop" - case Zombie: - return "zombie" - case Parked: - return "parked" - case Idle: - return "idle" // kernel thread - default: - return fmt.Sprintf("unknown (%c)", s) - } -} - -// Stat_t represents the information from /proc/[pid]/stat, as -// described in proc(5) with names based on the /proc/[pid]/status -// fields. -type Stat_t struct { - // Name is the command run by the process. - Name string - - // State is the state of the process. - State State - - // StartTime is the number of clock ticks after system boot (since - // Linux 2.6). - StartTime uint64 -} - -// Stat returns a Stat_t instance for the specified process. -func Stat(pid int) (stat Stat_t, err error) { - bytes, err := os.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "stat")) - if err != nil { - return stat, err - } - return parseStat(string(bytes)) -} - -func parseStat(data string) (stat Stat_t, err error) { - // Example: - // 89653 (gunicorn: maste) S 89630 89653 89653 0 -1 4194560 29689 28896 0 3 146 32 76 19 20 0 1 0 2971844 52965376 3920 18446744073709551615 1 1 0 0 0 0 0 16781312 137447943 0 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0 - // The fields are space-separated, see full description in proc(5). - // - // We are only interested in: - // * field 2: process name. It is the only field enclosed into - // parenthesis, as it can contain spaces (and parenthesis) inside. - // * field 3: process state, a single character (%c) - // * field 22: process start time, a long unsigned integer (%llu). - - // 1. Look for the first '(' and the last ')' first, what's in between is Name. - // We expect at least 20 fields and a space after the last one. - - const minAfterName = 20*2 + 1 // the min field is '0 '. - - first := strings.IndexByte(data, '(') - if first < 0 || first+minAfterName >= len(data) { - return stat, fmt.Errorf("invalid stat data (no comm or too short): %q", data) - } - - last := strings.LastIndexByte(data, ')') - if last <= first || last+minAfterName >= len(data) { - return stat, fmt.Errorf("invalid stat data (no comm or too short): %q", data) - } - - stat.Name = data[first+1 : last] - - // 2. Remove fields 1 and 2 and a space after. State is right after. - data = data[last+2:] - stat.State = State(data[0]) - - // 3. StartTime is field 22, data is at field 3 now, so we need to skip 19 spaces. - skipSpaces := 22 - 3 - for first = 0; skipSpaces > 0 && first < len(data); first++ { - if data[first] == ' ' { - skipSpaces-- - } - } - // Now first points to StartTime; look for space right after. - i := strings.IndexByte(data[first:], ' ') - if i < 0 { - return stat, fmt.Errorf("invalid stat data (too short): %q", data) - } - stat.StartTime, err = strconv.ParseUint(data[first:first+i], 10, 64) - if err != nil { - return stat, fmt.Errorf("invalid stat data (bad start time): %w", err) - } - - return stat, nil -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux.go deleted file mode 100644 index 4595fa82..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build go1.23 - -package system - -import ( - "syscall" -) - -// ClearRlimitNofileCache clears go runtime's nofile rlimit cache. The argument -// is process RLIMIT_NOFILE values. Relies on go.dev/cl/588076. -func ClearRlimitNofileCache(lim *syscall.Rlimit) { - // Ignore the return values since we only need to clean the cache, - // the limit is going to be set via unix.Prlimit elsewhere. - _ = syscall.Setrlimit(syscall.RLIMIT_NOFILE, lim) -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux_go122.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux_go122.go deleted file mode 100644 index 674e44bd..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_linux_go122.go +++ /dev/null @@ -1,27 +0,0 @@ -//go:build go1.19 && !go1.23 - -// TODO: remove this file once go 1.22 is no longer supported. - -package system - -import ( - "sync/atomic" - "syscall" - _ "unsafe" // Needed for go:linkname to work. -) - -//go:linkname syscallOrigRlimitNofile syscall.origRlimitNofile -var syscallOrigRlimitNofile atomic.Pointer[syscall.Rlimit] - -// ClearRlimitNofileCache clears go runtime's nofile rlimit cache. -// The argument is process RLIMIT_NOFILE values. -func ClearRlimitNofileCache(_ *syscall.Rlimit) { - // As reported in issue #4195, the new version of go runtime(since 1.19) - // will cache rlimit-nofile. Before executing execve, the rlimit-nofile - // of the process will be restored with the cache. In runc, this will - // cause the rlimit-nofile setting by the parent process for the container - // to become invalid. It can be solved by clearing this cache. But - // unfortunately, go stdlib doesn't provide such function, so we need to - // link to the private var `origRlimitNofile` in package syscall to hack. - syscallOrigRlimitNofile.Store(nil) -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_stub.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_stub.go deleted file mode 100644 index 96200df5..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/rlimit_stub.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !go1.19 - -package system - -import "syscall" - -func ClearRlimitNofileCache(_ *syscall.Rlimit) {} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_32.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_32.go deleted file mode 100644 index 1acc5cb0..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_32.go +++ /dev/null @@ -1,27 +0,0 @@ -//go:build linux && (386 || arm) -// +build linux -// +build 386 arm - -package system - -import ( - "golang.org/x/sys/unix" -) - -// Setuid sets the uid of the calling thread to the specified uid. -func Setuid(uid int) (err error) { - _, _, e1 := unix.RawSyscall(unix.SYS_SETUID32, uintptr(uid), 0, 0) - if e1 != 0 { - err = e1 - } - return -} - -// Setgid sets the gid of the calling thread to the specified gid. -func Setgid(gid int) (err error) { - _, _, e1 := unix.RawSyscall(unix.SYS_SETGID32, uintptr(gid), 0, 0) - if e1 != 0 { - err = e1 - } - return -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_64.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_64.go deleted file mode 100644 index 1ed0dba1..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/system/syscall_linux_64.go +++ /dev/null @@ -1,27 +0,0 @@ -//go:build linux && (arm64 || amd64 || mips || mipsle || mips64 || mips64le || ppc || ppc64 || ppc64le || riscv64 || s390x) -// +build linux -// +build arm64 amd64 mips mipsle mips64 mips64le ppc ppc64 ppc64le riscv64 s390x - -package system - -import ( - "golang.org/x/sys/unix" -) - -// Setuid sets the uid of the calling thread to the specified uid. -func Setuid(uid int) (err error) { - _, _, e1 := unix.RawSyscall(unix.SYS_SETUID, uintptr(uid), 0, 0) - if e1 != 0 { - err = e1 - } - return -} - -// Setgid sets the gid of the calling thread to the specified gid. -func Setgid(gid int) (err error) { - _, _, e1 := unix.RawSyscall(unix.SYS_SETGID, uintptr(gid), 0, 0) - if e1 != 0 { - err = e1 - } - return -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go deleted file mode 100644 index f95c1409..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go +++ /dev/null @@ -1,157 +0,0 @@ -//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd linux netbsd openbsd solaris - -package user - -import ( - "io" - "os" - "strconv" - - "golang.org/x/sys/unix" -) - -// Unix-specific path to the passwd and group formatted files. -const ( - unixPasswdPath = "/etc/passwd" - unixGroupPath = "/etc/group" -) - -// LookupUser looks up a user by their username in /etc/passwd. If the user -// cannot be found (or there is no /etc/passwd file on the filesystem), then -// LookupUser returns an error. -func LookupUser(username string) (User, error) { - return lookupUserFunc(func(u User) bool { - return u.Name == username - }) -} - -// LookupUid looks up a user by their user id in /etc/passwd. If the user cannot -// be found (or there is no /etc/passwd file on the filesystem), then LookupId -// returns an error. -func LookupUid(uid int) (User, error) { - return lookupUserFunc(func(u User) bool { - return u.Uid == uid - }) -} - -func lookupUserFunc(filter func(u User) bool) (User, error) { - // Get operating system-specific passwd reader-closer. - passwd, err := GetPasswd() - if err != nil { - return User{}, err - } - defer passwd.Close() - - // Get the users. - users, err := ParsePasswdFilter(passwd, filter) - if err != nil { - return User{}, err - } - - // No user entries found. - if len(users) == 0 { - return User{}, ErrNoPasswdEntries - } - - // Assume the first entry is the "correct" one. - return users[0], nil -} - -// LookupGroup looks up a group by its name in /etc/group. If the group cannot -// be found (or there is no /etc/group file on the filesystem), then LookupGroup -// returns an error. -func LookupGroup(groupname string) (Group, error) { - return lookupGroupFunc(func(g Group) bool { - return g.Name == groupname - }) -} - -// LookupGid looks up a group by its group id in /etc/group. If the group cannot -// be found (or there is no /etc/group file on the filesystem), then LookupGid -// returns an error. -func LookupGid(gid int) (Group, error) { - return lookupGroupFunc(func(g Group) bool { - return g.Gid == gid - }) -} - -func lookupGroupFunc(filter func(g Group) bool) (Group, error) { - // Get operating system-specific group reader-closer. - group, err := GetGroup() - if err != nil { - return Group{}, err - } - defer group.Close() - - // Get the users. - groups, err := ParseGroupFilter(group, filter) - if err != nil { - return Group{}, err - } - - // No user entries found. - if len(groups) == 0 { - return Group{}, ErrNoGroupEntries - } - - // Assume the first entry is the "correct" one. - return groups[0], nil -} - -func GetPasswdPath() (string, error) { - return unixPasswdPath, nil -} - -func GetPasswd() (io.ReadCloser, error) { - return os.Open(unixPasswdPath) -} - -func GetGroupPath() (string, error) { - return unixGroupPath, nil -} - -func GetGroup() (io.ReadCloser, error) { - return os.Open(unixGroupPath) -} - -// CurrentUser looks up the current user by their user id in /etc/passwd. If the -// user cannot be found (or there is no /etc/passwd file on the filesystem), -// then CurrentUser returns an error. -func CurrentUser() (User, error) { - return LookupUid(unix.Getuid()) -} - -// CurrentGroup looks up the current user's group by their primary group id's -// entry in /etc/passwd. If the group cannot be found (or there is no -// /etc/group file on the filesystem), then CurrentGroup returns an error. -func CurrentGroup() (Group, error) { - return LookupGid(unix.Getgid()) -} - -func currentUserSubIDs(fileName string) ([]SubID, error) { - u, err := CurrentUser() - if err != nil { - return nil, err - } - filter := func(entry SubID) bool { - return entry.Name == u.Name || entry.Name == strconv.Itoa(u.Uid) - } - return ParseSubIDFileFilter(fileName, filter) -} - -func CurrentUserSubUIDs() ([]SubID, error) { - return currentUserSubIDs("/etc/subuid") -} - -func CurrentUserSubGIDs() ([]SubID, error) { - return currentUserSubIDs("/etc/subgid") -} - -func CurrentProcessUIDMap() ([]IDMap, error) { - return ParseIDMapFile("/proc/self/uid_map") -} - -func CurrentProcessGIDMap() ([]IDMap, error) { - return ParseIDMapFile("/proc/self/gid_map") -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/user/user.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/user/user.go deleted file mode 100644 index 198c4936..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/user/user.go +++ /dev/null @@ -1,604 +0,0 @@ -package user - -import ( - "bufio" - "bytes" - "errors" - "fmt" - "io" - "os" - "strconv" - "strings" -) - -const ( - minID = 0 - maxID = 1<<31 - 1 // for 32-bit systems compatibility -) - -var ( - // ErrNoPasswdEntries is returned if no matching entries were found in /etc/group. - ErrNoPasswdEntries = errors.New("no matching entries in passwd file") - // ErrNoGroupEntries is returned if no matching entries were found in /etc/passwd. - ErrNoGroupEntries = errors.New("no matching entries in group file") - // ErrRange is returned if a UID or GID is outside of the valid range. - ErrRange = fmt.Errorf("uids and gids must be in range %d-%d", minID, maxID) -) - -type User struct { - Name string - Pass string - Uid int - Gid int - Gecos string - Home string - Shell string -} - -type Group struct { - Name string - Pass string - Gid int - List []string -} - -// SubID represents an entry in /etc/sub{u,g}id -type SubID struct { - Name string - SubID int64 - Count int64 -} - -// IDMap represents an entry in /proc/PID/{u,g}id_map -type IDMap struct { - ID int64 - ParentID int64 - Count int64 -} - -func parseLine(line []byte, v ...interface{}) { - parseParts(bytes.Split(line, []byte(":")), v...) -} - -func parseParts(parts [][]byte, v ...interface{}) { - if len(parts) == 0 { - return - } - - for i, p := range parts { - // Ignore cases where we don't have enough fields to populate the arguments. - // Some configuration files like to misbehave. - if len(v) <= i { - break - } - - // Use the type of the argument to figure out how to parse it, scanf() style. - // This is legit. - switch e := v[i].(type) { - case *string: - *e = string(p) - case *int: - // "numbers", with conversion errors ignored because of some misbehaving configuration files. - *e, _ = strconv.Atoi(string(p)) - case *int64: - *e, _ = strconv.ParseInt(string(p), 10, 64) - case *[]string: - // Comma-separated lists. - if len(p) != 0 { - *e = strings.Split(string(p), ",") - } else { - *e = []string{} - } - default: - // Someone goof'd when writing code using this function. Scream so they can hear us. - panic(fmt.Sprintf("parseLine only accepts {*string, *int, *int64, *[]string} as arguments! %#v is not a pointer!", e)) - } - } -} - -func ParsePasswdFile(path string) ([]User, error) { - passwd, err := os.Open(path) - if err != nil { - return nil, err - } - defer passwd.Close() - return ParsePasswd(passwd) -} - -func ParsePasswd(passwd io.Reader) ([]User, error) { - return ParsePasswdFilter(passwd, nil) -} - -func ParsePasswdFileFilter(path string, filter func(User) bool) ([]User, error) { - passwd, err := os.Open(path) - if err != nil { - return nil, err - } - defer passwd.Close() - return ParsePasswdFilter(passwd, filter) -} - -func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) { - if r == nil { - return nil, errors.New("nil source for passwd-formatted data") - } - - var ( - s = bufio.NewScanner(r) - out = []User{} - ) - - for s.Scan() { - line := bytes.TrimSpace(s.Bytes()) - if len(line) == 0 { - continue - } - - // see: man 5 passwd - // name:password:UID:GID:GECOS:directory:shell - // Name:Pass:Uid:Gid:Gecos:Home:Shell - // root:x:0:0:root:/root:/bin/bash - // adm:x:3:4:adm:/var/adm:/bin/false - p := User{} - parseLine(line, &p.Name, &p.Pass, &p.Uid, &p.Gid, &p.Gecos, &p.Home, &p.Shell) - - if filter == nil || filter(p) { - out = append(out, p) - } - } - if err := s.Err(); err != nil { - return nil, err - } - - return out, nil -} - -func ParseGroupFile(path string) ([]Group, error) { - group, err := os.Open(path) - if err != nil { - return nil, err - } - - defer group.Close() - return ParseGroup(group) -} - -func ParseGroup(group io.Reader) ([]Group, error) { - return ParseGroupFilter(group, nil) -} - -func ParseGroupFileFilter(path string, filter func(Group) bool) ([]Group, error) { - group, err := os.Open(path) - if err != nil { - return nil, err - } - defer group.Close() - return ParseGroupFilter(group, filter) -} - -func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) { - if r == nil { - return nil, errors.New("nil source for group-formatted data") - } - rd := bufio.NewReader(r) - out := []Group{} - - // Read the file line-by-line. - for { - var ( - isPrefix bool - wholeLine []byte - err error - ) - - // Read the next line. We do so in chunks (as much as reader's - // buffer is able to keep), check if we read enough columns - // already on each step and store final result in wholeLine. - for { - var line []byte - line, isPrefix, err = rd.ReadLine() - if err != nil { - // We should return no error if EOF is reached - // without a match. - if err == io.EOF { - err = nil - } - return out, err - } - - // Simple common case: line is short enough to fit in a - // single reader's buffer. - if !isPrefix && len(wholeLine) == 0 { - wholeLine = line - break - } - - wholeLine = append(wholeLine, line...) - - // Check if we read the whole line already. - if !isPrefix { - break - } - } - - // There's no spec for /etc/passwd or /etc/group, but we try to follow - // the same rules as the glibc parser, which allows comments and blank - // space at the beginning of a line. - wholeLine = bytes.TrimSpace(wholeLine) - if len(wholeLine) == 0 || wholeLine[0] == '#' { - continue - } - - // see: man 5 group - // group_name:password:GID:user_list - // Name:Pass:Gid:List - // root:x:0:root - // adm:x:4:root,adm,daemon - p := Group{} - parseLine(wholeLine, &p.Name, &p.Pass, &p.Gid, &p.List) - - if filter == nil || filter(p) { - out = append(out, p) - } - } -} - -type ExecUser struct { - Uid int - Gid int - Sgids []int - Home string -} - -// GetExecUserPath is a wrapper for GetExecUser. It reads data from each of the -// given file paths and uses that data as the arguments to GetExecUser. If the -// files cannot be opened for any reason, the error is ignored and a nil -// io.Reader is passed instead. -func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) { - var passwd, group io.Reader - - if passwdFile, err := os.Open(passwdPath); err == nil { - passwd = passwdFile - defer passwdFile.Close() - } - - if groupFile, err := os.Open(groupPath); err == nil { - group = groupFile - defer groupFile.Close() - } - - return GetExecUser(userSpec, defaults, passwd, group) -} - -// GetExecUser parses a user specification string (using the passwd and group -// readers as sources for /etc/passwd and /etc/group data, respectively). In -// the case of blank fields or missing data from the sources, the values in -// defaults is used. -// -// GetExecUser will return an error if a user or group literal could not be -// found in any entry in passwd and group respectively. -// -// Examples of valid user specifications are: -// - "" -// - "user" -// - "uid" -// - "user:group" -// - "uid:gid -// - "user:gid" -// - "uid:group" -// -// It should be noted that if you specify a numeric user or group id, they will -// not be evaluated as usernames (only the metadata will be filled). So attempting -// to parse a user with user.Name = "1337" will produce the user with a UID of -// 1337. -func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (*ExecUser, error) { - if defaults == nil { - defaults = new(ExecUser) - } - - // Copy over defaults. - user := &ExecUser{ - Uid: defaults.Uid, - Gid: defaults.Gid, - Sgids: defaults.Sgids, - Home: defaults.Home, - } - - // Sgids slice *cannot* be nil. - if user.Sgids == nil { - user.Sgids = []int{} - } - - // Allow for userArg to have either "user" syntax, or optionally "user:group" syntax - var userArg, groupArg string - parseLine([]byte(userSpec), &userArg, &groupArg) - - // Convert userArg and groupArg to be numeric, so we don't have to execute - // Atoi *twice* for each iteration over lines. - uidArg, uidErr := strconv.Atoi(userArg) - gidArg, gidErr := strconv.Atoi(groupArg) - - // Find the matching user. - users, err := ParsePasswdFilter(passwd, func(u User) bool { - if userArg == "" { - // Default to current state of the user. - return u.Uid == user.Uid - } - - if uidErr == nil { - // If the userArg is numeric, always treat it as a UID. - return uidArg == u.Uid - } - - return u.Name == userArg - }) - - // If we can't find the user, we have to bail. - if err != nil && passwd != nil { - if userArg == "" { - userArg = strconv.Itoa(user.Uid) - } - return nil, fmt.Errorf("unable to find user %s: %w", userArg, err) - } - - var matchedUserName string - if len(users) > 0 { - // First match wins, even if there's more than one matching entry. - matchedUserName = users[0].Name - user.Uid = users[0].Uid - user.Gid = users[0].Gid - user.Home = users[0].Home - } else if userArg != "" { - // If we can't find a user with the given username, the only other valid - // option is if it's a numeric username with no associated entry in passwd. - - if uidErr != nil { - // Not numeric. - return nil, fmt.Errorf("unable to find user %s: %w", userArg, ErrNoPasswdEntries) - } - user.Uid = uidArg - - // Must be inside valid uid range. - if user.Uid < minID || user.Uid > maxID { - return nil, ErrRange - } - - // Okay, so it's numeric. We can just roll with this. - } - - // On to the groups. If we matched a username, we need to do this because of - // the supplementary group IDs. - if groupArg != "" || matchedUserName != "" { - groups, err := ParseGroupFilter(group, func(g Group) bool { - // If the group argument isn't explicit, we'll just search for it. - if groupArg == "" { - // Check if user is a member of this group. - for _, u := range g.List { - if u == matchedUserName { - return true - } - } - return false - } - - if gidErr == nil { - // If the groupArg is numeric, always treat it as a GID. - return gidArg == g.Gid - } - - return g.Name == groupArg - }) - if err != nil && group != nil { - 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. - if groupArg != "" { - if len(groups) > 0 { - // First match wins, even if there's more than one matching entry. - user.Gid = groups[0].Gid - } else { - // If we can't find a group with the given name, the only other valid - // option is if it's a numeric group name with no associated entry in group. - - if gidErr != nil { - // Not numeric. - return nil, fmt.Errorf("unable to find group %s: %w", groupArg, ErrNoGroupEntries) - } - user.Gid = gidArg - - // Must be inside valid gid range. - if user.Gid < minID || user.Gid > maxID { - return nil, ErrRange - } - - // Okay, so it's numeric. We can just roll with this. - } - } else if len(groups) > 0 { - // Supplementary group ids only make sense if in the implicit form. - user.Sgids = make([]int, len(groups)) - for i, group := range groups { - user.Sgids[i] = group.Gid - } - } - } - - return user, nil -} - -// GetAdditionalGroups looks up a list of groups by name or group id -// against the given /etc/group formatted data. If a group name cannot -// be found, an error will be returned. If a group id cannot be found, -// or the given group data is nil, the id will be returned as-is -// provided it is in the legal range. -func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, error) { - groups := []Group{} - if group != nil { - var err error - groups, err = ParseGroupFilter(group, func(g Group) bool { - for _, ag := range additionalGroups { - if g.Name == ag || strconv.Itoa(g.Gid) == ag { - return true - } - } - return false - }) - if err != nil { - return nil, fmt.Errorf("Unable to find additional groups %v: %w", additionalGroups, err) - } - } - - gidMap := make(map[int]struct{}) - for _, ag := range additionalGroups { - var found bool - for _, g := range groups { - // if we found a matched group either by name or gid, take the - // first matched as correct - if g.Name == ag || strconv.Itoa(g.Gid) == ag { - if _, ok := gidMap[g.Gid]; !ok { - gidMap[g.Gid] = struct{}{} - found = true - break - } - } - } - // we asked for a group but didn't find it. let's check to see - // if we wanted a numeric group - if !found { - gid, err := strconv.ParseInt(ag, 10, 64) - if err != nil { - // 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 { - return nil, ErrRange - } - gidMap[int(gid)] = struct{}{} - } - } - gids := []int{} - for gid := range gidMap { - gids = append(gids, gid) - } - return gids, nil -} - -// GetAdditionalGroupsPath is a wrapper around GetAdditionalGroups -// that opens the groupPath given and gives it as an argument to -// GetAdditionalGroups. -func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int, error) { - var group io.Reader - - if groupFile, err := os.Open(groupPath); err == nil { - group = groupFile - defer groupFile.Close() - } - return GetAdditionalGroups(additionalGroups, group) -} - -func ParseSubIDFile(path string) ([]SubID, error) { - subid, err := os.Open(path) - if err != nil { - return nil, err - } - defer subid.Close() - return ParseSubID(subid) -} - -func ParseSubID(subid io.Reader) ([]SubID, error) { - return ParseSubIDFilter(subid, nil) -} - -func ParseSubIDFileFilter(path string, filter func(SubID) bool) ([]SubID, error) { - subid, err := os.Open(path) - if err != nil { - return nil, err - } - defer subid.Close() - return ParseSubIDFilter(subid, filter) -} - -func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) { - if r == nil { - return nil, errors.New("nil source for subid-formatted data") - } - - var ( - s = bufio.NewScanner(r) - out = []SubID{} - ) - - for s.Scan() { - line := bytes.TrimSpace(s.Bytes()) - if len(line) == 0 { - continue - } - - // see: man 5 subuid - p := SubID{} - parseLine(line, &p.Name, &p.SubID, &p.Count) - - if filter == nil || filter(p) { - out = append(out, p) - } - } - if err := s.Err(); err != nil { - return nil, err - } - - return out, nil -} - -func ParseIDMapFile(path string) ([]IDMap, error) { - r, err := os.Open(path) - if err != nil { - return nil, err - } - defer r.Close() - return ParseIDMap(r) -} - -func ParseIDMap(r io.Reader) ([]IDMap, error) { - return ParseIDMapFilter(r, nil) -} - -func ParseIDMapFileFilter(path string, filter func(IDMap) bool) ([]IDMap, error) { - r, err := os.Open(path) - if err != nil { - return nil, err - } - defer r.Close() - return ParseIDMapFilter(r, filter) -} - -func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) { - if r == nil { - return nil, errors.New("nil source for idmap-formatted data") - } - - var ( - s = bufio.NewScanner(r) - out = []IDMap{} - ) - - for s.Scan() { - line := bytes.TrimSpace(s.Bytes()) - if len(line) == 0 { - continue - } - - // see: man 7 user_namespaces - p := IDMap{} - parseParts(bytes.Fields(line), &p.ID, &p.ParentID, &p.Count) - - if filter == nil || filter(p) { - out = append(out, p) - } - } - if err := s.Err(); err != nil { - return nil, err - } - - return out, nil -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/user/user_fuzzer.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/user/user_fuzzer.go deleted file mode 100644 index e018eae6..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/user/user_fuzzer.go +++ /dev/null @@ -1,43 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -package user - -import ( - "io" - "strings" -) - -func IsDivisbleBy(n int, divisibleby int) bool { - return (n % divisibleby) == 0 -} - -func FuzzUser(data []byte) int { - if len(data) == 0 { - return -1 - } - if !IsDivisbleBy(len(data), 5) { - return -1 - } - - var divided [][]byte - - chunkSize := len(data) / 5 - - for i := 0; i < len(data); i += chunkSize { - end := i + chunkSize - - divided = append(divided, data[i:end]) - } - - _, _ = ParsePasswdFilter(strings.NewReader(string(divided[0])), nil) - - var passwd, group io.Reader - - group = strings.NewReader(string(divided[1])) - _, _ = GetAdditionalGroups([]string{string(divided[2])}, group) - - passwd = strings.NewReader(string(divided[3])) - _, _ = GetExecUser(string(divided[4]), nil, passwd, group) - return 1 -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns.go deleted file mode 100644 index f6cb98e5..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns.go +++ /dev/null @@ -1,5 +0,0 @@ -package userns - -// RunningInUserNS detects whether we are currently running in a user namespace. -// Originally copied from github.com/lxc/lxd/shared/util.go -var RunningInUserNS = runningInUserNS diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_fuzzer.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_fuzzer.go deleted file mode 100644 index 1e00ab8b..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_fuzzer.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -package userns - -import ( - "strings" - - "github.com/opencontainers/runc/libcontainer/user" -) - -func FuzzUIDMap(data []byte) int { - uidmap, _ := user.ParseIDMap(strings.NewReader(string(data))) - _ = uidMapInUserNS(uidmap) - return 1 -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_linux.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_linux.go deleted file mode 100644 index 724e6df0..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_linux.go +++ /dev/null @@ -1,37 +0,0 @@ -package userns - -import ( - "sync" - - "github.com/opencontainers/runc/libcontainer/user" -) - -var ( - inUserNS bool - nsOnce sync.Once -) - -// runningInUserNS detects whether we are currently running in a user namespace. -// Originally copied from github.com/lxc/lxd/shared/util.go -func runningInUserNS() bool { - nsOnce.Do(func() { - uidmap, err := user.CurrentProcessUIDMap() - if err != nil { - // This kernel-provided file only exists if user namespaces are supported - return - } - inUserNS = uidMapInUserNS(uidmap) - }) - return inUserNS -} - -func uidMapInUserNS(uidmap []user.IDMap) bool { - /* - * We assume we are in the initial user namespace if we have a full - * range - 4294967295 uids starting at uid 0. - */ - if len(uidmap) == 1 && uidmap[0].ID == 0 && uidmap[0].ParentID == 0 && uidmap[0].Count == 4294967295 { - return false - } - return true -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_maps.c b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_maps.c deleted file mode 100644 index 84f2c618..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_maps.c +++ /dev/null @@ -1,79 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include - -/* - * All of the code here is run inside an aync-signal-safe context, so we need - * to be careful to not call any functions that could cause issues. In theory, - * since we are a Go program, there are fewer restrictions in practice, it's - * better to be safe than sorry. - * - * The only exception is exit, which we need to call to make sure we don't - * return into runc. - */ - -void bail(int pipefd, const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - vdprintf(pipefd, fmt, args); - va_end(args); - - exit(1); -} - -int spawn_userns_cat(char *userns_path, char *path, int outfd, int errfd) -{ - char buffer[4096] = { 0 }; - - pid_t child = fork(); - if (child != 0) - return child; - /* in child */ - - /* Join the target userns. */ - int nsfd = open(userns_path, O_RDONLY); - if (nsfd < 0) - bail(errfd, "open userns path %s failed: %m", userns_path); - - int err = setns(nsfd, CLONE_NEWUSER); - if (err < 0) - bail(errfd, "setns %s failed: %m", userns_path); - - close(nsfd); - - /* Pipe the requested file contents. */ - int fd = open(path, O_RDONLY); - if (fd < 0) - bail(errfd, "open %s in userns %s failed: %m", path, userns_path); - - int nread, ntotal = 0; - while ((nread = read(fd, buffer, sizeof(buffer))) != 0) { - if (nread < 0) - bail(errfd, "read bytes from %s failed (after %d total bytes read): %m", path, ntotal); - ntotal += nread; - - int nwritten = 0; - while (nwritten < nread) { - int n = write(outfd, buffer, nread - nwritten); - if (n < 0) - bail(errfd, "write %d bytes from %s failed (after %d bytes written): %m", - nread - nwritten, path, nwritten); - nwritten += n; - } - if (nread != nwritten) - bail(errfd, "mismatch for bytes read and written: %d read != %d written", nread, nwritten); - } - - close(fd); - close(outfd); - close(errfd); - - /* We must exit here, otherwise we would return into a forked runc. */ - exit(0); -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_maps_linux.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_maps_linux.go deleted file mode 100644 index 7a8c2b02..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_maps_linux.go +++ /dev/null @@ -1,186 +0,0 @@ -//go:build linux - -package userns - -import ( - "bufio" - "bytes" - "fmt" - "io" - "os" - "unsafe" - - "github.com/opencontainers/runc/libcontainer/configs" - "github.com/sirupsen/logrus" -) - -/* -#include -extern int spawn_userns_cat(char *userns_path, char *path, int outfd, int errfd); -*/ -import "C" - -func parseIdmapData(data []byte) (ms []configs.IDMap, err error) { - scanner := bufio.NewScanner(bytes.NewReader(data)) - for scanner.Scan() { - var m configs.IDMap - line := scanner.Text() - if _, err := fmt.Sscanf(line, "%d %d %d", &m.ContainerID, &m.HostID, &m.Size); err != nil { - return nil, fmt.Errorf("parsing id map failed: invalid format in line %q: %w", line, err) - } - ms = append(ms, m) - } - if err := scanner.Err(); err != nil { - return nil, fmt.Errorf("parsing id map failed: %w", err) - } - return ms, nil -} - -// Do something equivalent to nsenter --user= cat , but more -// efficiently. Returns the contents of the requested file from within the user -// namespace. -func spawnUserNamespaceCat(nsPath string, path string) ([]byte, error) { - rdr, wtr, err := os.Pipe() - if err != nil { - return nil, fmt.Errorf("create pipe for userns spawn failed: %w", err) - } - defer rdr.Close() - defer wtr.Close() - - errRdr, errWtr, err := os.Pipe() - if err != nil { - return nil, fmt.Errorf("create error pipe for userns spawn failed: %w", err) - } - defer errRdr.Close() - defer errWtr.Close() - - cNsPath := C.CString(nsPath) - defer C.free(unsafe.Pointer(cNsPath)) - cPath := C.CString(path) - defer C.free(unsafe.Pointer(cPath)) - - childPid := C.spawn_userns_cat(cNsPath, cPath, C.int(wtr.Fd()), C.int(errWtr.Fd())) - - if childPid < 0 { - return nil, fmt.Errorf("failed to spawn fork for userns") - } else if childPid == 0 { - // this should never happen - panic("runc executing inside fork child -- unsafe state!") - } - - // We are in the parent -- close the write end of the pipe before reading. - wtr.Close() - output, err := io.ReadAll(rdr) - rdr.Close() - if err != nil { - return nil, fmt.Errorf("reading from userns spawn failed: %w", err) - } - - // Ditto for the error pipe. - errWtr.Close() - errOutput, err := io.ReadAll(errRdr) - errRdr.Close() - if err != nil { - return nil, fmt.Errorf("reading from userns spawn error pipe failed: %w", err) - } - errOutput = bytes.TrimSpace(errOutput) - - // Clean up the child. - child, err := os.FindProcess(int(childPid)) - if err != nil { - return nil, fmt.Errorf("could not find userns spawn process: %w", err) - } - state, err := child.Wait() - if err != nil { - return nil, fmt.Errorf("failed to wait for userns spawn process: %w", err) - } - if !state.Success() { - errStr := string(errOutput) - if errStr == "" { - errStr = fmt.Sprintf("unknown error (status code %d)", state.ExitCode()) - } - return nil, fmt.Errorf("userns spawn: %s", errStr) - } else if len(errOutput) > 0 { - // We can just ignore weird output in the error pipe if the process - // didn't bail(), but for completeness output for debugging. - logrus.Debugf("userns spawn succeeded but unexpected error message found: %s", string(errOutput)) - } - // The subprocess succeeded, return whatever it wrote to the pipe. - return output, nil -} - -func GetUserNamespaceMappings(nsPath string) (uidMap, gidMap []configs.IDMap, err error) { - var ( - pid int - extra rune - tryFastPath bool - ) - - // nsPath is usually of the form /proc//ns/user, which means that we - // already have a pid that is part of the user namespace and thus we can - // just use the pid to read from /proc//*id_map. - // - // Note that Sscanf doesn't consume the whole input, so we check for any - // trailing data with %c. That way, we can be sure the pattern matched - // /proc/$pid/ns/user _exactly_ iff n === 1. - if n, _ := fmt.Sscanf(nsPath, "/proc/%d/ns/user%c", &pid, &extra); n == 1 { - tryFastPath = pid > 0 - } - - for _, mapType := range []struct { - name string - idMap *[]configs.IDMap - }{ - {"uid_map", &uidMap}, - {"gid_map", &gidMap}, - } { - var mapData []byte - - if tryFastPath { - path := fmt.Sprintf("/proc/%d/%s", pid, mapType.name) - data, err := os.ReadFile(path) - if err != nil { - // Do not error out here -- we need to try the slow path if the - // fast path failed. - logrus.Debugf("failed to use fast path to read %s from userns %s (error: %s), falling back to slow userns-join path", mapType.name, nsPath, err) - } else { - mapData = data - } - } else { - logrus.Debugf("cannot use fast path to read %s from userns %s, falling back to slow userns-join path", mapType.name, nsPath) - } - - if mapData == nil { - // We have to actually join the namespace if we cannot take the - // fast path. The path is resolved with respect to the child - // process, so just use /proc/self. - data, err := spawnUserNamespaceCat(nsPath, "/proc/self/"+mapType.name) - if err != nil { - return nil, nil, err - } - mapData = data - } - idMap, err := parseIdmapData(mapData) - if err != nil { - return nil, nil, fmt.Errorf("failed to parse %s of userns %s: %w", mapType.name, nsPath, err) - } - *mapType.idMap = idMap - } - - return uidMap, gidMap, nil -} - -// IsSameMapping returns whether or not the two id mappings are the same. Note -// that if the order of the mappings is different, or a mapping has been split, -// the mappings will be considered different. -func IsSameMapping(a, b []configs.IDMap) bool { - if len(a) != len(b) { - return false - } - for idx := range a { - if a[idx] != b[idx] { - return false - } - } - return true -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_unsupported.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_unsupported.go deleted file mode 100644 index f35c13a1..00000000 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/userns/userns_unsupported.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build !linux -// +build !linux - -package userns - -import "github.com/opencontainers/runc/libcontainer/user" - -// runningInUserNS is a stub for non-Linux systems -// Always returns false -func runningInUserNS() bool { - return false -} - -// uidMapInUserNS is a stub for non-Linux systems -// Always returns false -func uidMapInUserNS(uidmap []user.IDMap) bool { - return false -} diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go index 7ef9da21..2edd1417 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go @@ -19,13 +19,14 @@ package utils import ( "fmt" "os" + "runtime" "golang.org/x/sys/unix" ) -// MaxSendfdLen is the maximum length of the name of a file descriptor being -// sent using SendFd. The name of the file handle returned by RecvFd will never -// be larger than this value. +// MaxNameLen is the maximum length of the name of a file descriptor being sent +// using SendFile. The name of the file handle returned by RecvFile will never be +// larger than this value. const MaxNameLen = 4096 // oobSpace is the size of the oob slice required to store a single FD. Note @@ -33,26 +34,21 @@ const MaxNameLen = 4096 // so sizeof(fd) = 4. var oobSpace = unix.CmsgSpace(4) -// RecvFd waits for a file descriptor to be sent over the given AF_UNIX +// RecvFile waits for a file descriptor to be sent over the given AF_UNIX // socket. The file name of the remote file descriptor will be recreated // locally (it is sent as non-auxiliary data in the same payload). -func RecvFd(socket *os.File) (*os.File, error) { - // For some reason, unix.Recvmsg uses the length rather than the capacity - // when passing the msg_controllen and other attributes to recvmsg. So we - // have to actually set the length. +func RecvFile(socket *os.File) (_ *os.File, Err error) { name := make([]byte, MaxNameLen) oob := make([]byte, oobSpace) sockfd := socket.Fd() - n, oobn, _, _, err := unix.Recvmsg(int(sockfd), name, oob, 0) + n, oobn, _, _, err := unix.Recvmsg(int(sockfd), name, oob, unix.MSG_CMSG_CLOEXEC) if err != nil { return nil, err } - if n >= MaxNameLen || oobn != oobSpace { - return nil, fmt.Errorf("recvfd: incorrect number of bytes read (n=%d oobn=%d)", n, oobn) + return nil, fmt.Errorf("recvfile: incorrect number of bytes read (n=%d oobn=%d)", n, oobn) } - // Truncate. name = name[:n] oob = oob[:oobn] @@ -61,36 +57,63 @@ func RecvFd(socket *os.File) (*os.File, error) { if err != nil { return nil, err } - if len(scms) != 1 { - return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms)) + + // We cannot control how many SCM_RIGHTS we receive, and upon receiving + // them all of the descriptors are installed in our fd table, so we need to + // parse all of the SCM_RIGHTS we received in order to close all of the + // descriptors on error. + var fds []int + defer func() { + for i, fd := range fds { + if i == 0 && Err == nil { + // Only close the first one on error. + continue + } + // Always close extra ones. + _ = unix.Close(fd) + } + }() + var lastErr error + for _, scm := range scms { + if scm.Header.Type == unix.SCM_RIGHTS { + scmFds, err := unix.ParseUnixRights(&scm) + if err != nil { + lastErr = err + } else { + fds = append(fds, scmFds...) + } + } + } + if lastErr != nil { + return nil, lastErr } - scm := scms[0] - fds, err := unix.ParseUnixRights(&scm) - if err != nil { - return nil, err + // We do this after collecting the fds to make sure we close them all when + // returning an error here. + if len(scms) != 1 { + return nil, fmt.Errorf("recvfd: number of SCMs is not 1: %d", len(scms)) } if len(fds) != 1 { return nil, fmt.Errorf("recvfd: number of fds is not 1: %d", len(fds)) } - fd := uintptr(fds[0]) - - return os.NewFile(fd, string(name)), nil + return os.NewFile(uintptr(fds[0]), string(name)), nil } -// SendFd sends a file descriptor over the given AF_UNIX socket. In -// addition, the file.Name() of the given file will also be sent as -// non-auxiliary data in the same payload (allowing to send contextual -// information for a file descriptor). -func SendFd(socket *os.File, name string, fd uintptr) error { +// SendFile sends a file over the given AF_UNIX socket. file.Name() is also +// included so that if the other end uses RecvFile, the file will have the same +// name information. +func SendFile(socket *os.File, file *os.File) error { + name := file.Name() if len(name) >= MaxNameLen { return fmt.Errorf("sendfd: filename too long: %s", name) } - return SendFds(socket, []byte(name), int(fd)) + err := SendRawFd(socket, name, file.Fd()) + runtime.KeepAlive(file) + return err } -// 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) +// SendRawFd sends a specific file descriptor over the given AF_UNIX socket. +func SendRawFd(socket *os.File, msg string, fd uintptr) error { + oob := unix.UnixRights(int(fd)) + return unix.Sendmsg(int(socket.Fd()), []byte(msg), oob, nil, 0) } diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go index 6b9fc343..db420ea6 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go @@ -1,17 +1,12 @@ package utils import ( - "encoding/binary" "encoding/json" - "fmt" "io" "os" "path/filepath" - "strconv" "strings" - "unsafe" - securejoin "github.com/cyphar/filepath-securejoin" "golang.org/x/sys/unix" ) @@ -19,20 +14,6 @@ const ( exitSignalOffset = 128 ) -// NativeEndian is the native byte order of the host system. -var NativeEndian binary.ByteOrder - -func init() { - // Copied from . - i := uint32(1) - b := (*[4]byte)(unsafe.Pointer(&i)) - if b[0] == 1 { - NativeEndian = binary.LittleEndian - } else { - NativeEndian = binary.BigEndian - } -} - // ExitStatus returns the correct exit status for a process based on if it // was signaled or exited cleanly func ExitStatus(status unix.WaitStatus) int { @@ -43,6 +24,9 @@ func ExitStatus(status unix.WaitStatus) int { } // WriteJSON writes the provided struct v to w using standard json marshaling +// without a trailing newline. This is used instead of json.Encoder because +// there might be a problem in json decoder in some cases, see: +// https://github.com/docker/docker/issues/14203#issuecomment-174177790 func WriteJSON(w io.Writer, v interface{}) error { data, err := json.Marshal(v) if err != nil { @@ -99,52 +83,16 @@ func stripRoot(root, path string) string { return CleanPath("/" + path) } -// WithProcfd runs the passed closure with a procfd path (/proc/self/fd/...) -// corresponding to the unsafePath resolved within the root. Before passing the -// fd, this path is verified to have been inside the root -- so operating on it -// through the passed fdpath should be safe. Do not access this path through -// the original path strings, and do not attempt to use the pathname outside of -// the passed closure (the file handle will be freed once the closure returns). -func WithProcfd(root, unsafePath string, fn func(procfd string) error) error { - // Remove the root then forcefully resolve inside the root. - unsafePath = stripRoot(root, unsafePath) - path, err := securejoin.SecureJoin(root, unsafePath) - if err != nil { - return fmt.Errorf("resolving path inside rootfs failed: %w", err) - } - - // Open the target path. - fh, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC, 0) - if err != nil { - return fmt.Errorf("open o_path procfd: %w", err) - } - defer fh.Close() - - // Double-check the path is the one we expected. - procfd := "/proc/self/fd/" + strconv.Itoa(int(fh.Fd())) - if realpath, err := os.Readlink(procfd); err != nil { - return fmt.Errorf("procfd verification failed: %w", err) - } else if realpath != path { - return fmt.Errorf("possibly malicious path detected -- refusing to operate on %s", realpath) - } - - // Run the closure. - return fn(procfd) -} - -// SearchLabels searches a list of key-value pairs for the provided key and -// returns the corresponding value. The pairs must be separated with '='. -func SearchLabels(labels []string, query string) string { - for _, l := range labels { - parts := strings.SplitN(l, "=", 2) - if len(parts) < 2 { - continue - } - if parts[0] == query { - return parts[1] +// SearchLabels searches through a list of key=value pairs for a given key, +// returning its value, and the binary flag telling whether the key exist. +func SearchLabels(labels []string, key string) (string, bool) { + key += "=" + for _, s := range labels { + if strings.HasPrefix(s, key) { + return s[len(key):], true } } - return "" + return "", false } // Annotations returns the bundle path and user defined annotations from the @@ -153,14 +101,14 @@ func SearchLabels(labels []string, query string) string { func Annotations(labels []string) (bundle string, userAnnotations map[string]string) { userAnnotations = make(map[string]string) for _, l := range labels { - parts := strings.SplitN(l, "=", 2) - if len(parts) < 2 { + name, value, ok := strings.Cut(l, "=") + if !ok { continue } - if parts[0] == "bundle" { - bundle = parts[1] + if name == "bundle" { + bundle = value } else { - userAnnotations[parts[0]] = parts[1] + userAnnotations[name] = value } } return diff --git a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go index 460b94ce..c8ad559d 100644 --- a/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go +++ b/src/bpm/vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go @@ -1,20 +1,20 @@ //go:build !windows -// +build !windows package utils import ( - "errors" "fmt" + "math" "os" "path/filepath" + "runtime" "strconv" "strings" + "sync" _ "unsafe" // for go:linkname - "github.com/opencontainers/runc/libcontainer/system" - securejoin "github.com/cyphar/filepath-securejoin" + "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -30,12 +30,39 @@ func EnsureProcHandle(fh *os.File) error { return nil } +var ( + haveCloseRangeCloexecBool bool + haveCloseRangeCloexecOnce sync.Once +) + +func haveCloseRangeCloexec() bool { + haveCloseRangeCloexecOnce.Do(func() { + // Make sure we're not closing a random file descriptor. + tmpFd, err := unix.FcntlInt(0, unix.F_DUPFD_CLOEXEC, 0) + if err != nil { + return + } + defer unix.Close(tmpFd) + + err = unix.CloseRange(uint(tmpFd), uint(tmpFd), unix.CLOSE_RANGE_CLOEXEC) + // Any error means we cannot use close_range(CLOSE_RANGE_CLOEXEC). + // -ENOSYS and -EINVAL ultimately mean we don't have support, but any + // other potential error would imply that even the most basic close + // operation wouldn't work. + haveCloseRangeCloexecBool = err == nil + }) + return haveCloseRangeCloexecBool +} + type fdFunc func(fd int) // fdRangeFrom calls the passed fdFunc for each file descriptor that is open in // the current process. func fdRangeFrom(minFd int, fn fdFunc) error { - fdDir, err := os.Open("/proc/self/fd") + procSelfFd, closer := ProcThreadSelf("fd") + defer closer() + + fdDir, err := os.Open(procSelfFd) if err != nil { return err } @@ -73,6 +100,12 @@ func fdRangeFrom(minFd int, fn fdFunc) error { // CloseExecFrom sets the O_CLOEXEC flag on all file descriptors greater or // equal to minFd in the current process. func CloseExecFrom(minFd int) error { + // Use close_range(CLOSE_RANGE_CLOEXEC) if possible. + if haveCloseRangeCloexec() { + err := unix.CloseRange(uint(minFd), math.MaxUint, unix.CLOSE_RANGE_CLOEXEC) + return os.NewSyscallError("close_range", err) + } + // Otherwise, fall back to the standard loop. return fdRangeFrom(minFd, unix.CloseOnExec) } @@ -95,7 +128,8 @@ func runtime_IsPollDescriptor(fd uintptr) bool //nolint:revive // *os.File operations would apply to the wrong file). This function is only // intended to be called from the last stage of runc init. func UnsafeCloseFrom(minFd int) error { - // We must not close some file descriptors. + // We cannot use close_range(2) even if it is available, because we must + // not close some file descriptors. return fdRangeFrom(minFd, func(fd int) { if runtime_IsPollDescriptor(uintptr(fd)) { // These are the Go runtimes internal netpoll file descriptors. @@ -113,8 +147,8 @@ func UnsafeCloseFrom(minFd int) error { }) } -// NewSockPair returns a new unix socket pair -func NewSockPair(name string) (parent *os.File, child *os.File, err error) { +// NewSockPair returns a new SOCK_STREAM unix socket pair. +func NewSockPair(name string) (parent, child *os.File, err error) { fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0) if err != nil { return nil, nil, err @@ -122,6 +156,112 @@ func NewSockPair(name string) (parent *os.File, child *os.File, err error) { return os.NewFile(uintptr(fds[1]), name+"-p"), os.NewFile(uintptr(fds[0]), name+"-c"), nil } +// WithProcfd runs the passed closure with a procfd path (/proc/self/fd/...) +// corresponding to the unsafePath resolved within the root. Before passing the +// fd, this path is verified to have been inside the root -- so operating on it +// through the passed fdpath should be safe. Do not access this path through +// the original path strings, and do not attempt to use the pathname outside of +// the passed closure (the file handle will be freed once the closure returns). +func WithProcfd(root, unsafePath string, fn func(procfd string) error) error { + // Remove the root then forcefully resolve inside the root. + unsafePath = stripRoot(root, unsafePath) + path, err := securejoin.SecureJoin(root, unsafePath) + if err != nil { + return fmt.Errorf("resolving path inside rootfs failed: %w", err) + } + + procSelfFd, closer := ProcThreadSelf("fd/") + defer closer() + + // Open the target path. + fh, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC, 0) + if err != nil { + return fmt.Errorf("open o_path procfd: %w", err) + } + defer fh.Close() + + procfd := filepath.Join(procSelfFd, strconv.Itoa(int(fh.Fd()))) + // Double-check the path is the one we expected. + if realpath, err := os.Readlink(procfd); err != nil { + return fmt.Errorf("procfd verification failed: %w", err) + } else if realpath != path { + return fmt.Errorf("possibly malicious path detected -- refusing to operate on %s", realpath) + } + + return fn(procfd) +} + +type ProcThreadSelfCloser func() + +var ( + haveProcThreadSelf bool + haveProcThreadSelfOnce sync.Once +) + +// ProcThreadSelf returns a string that is equivalent to +// /proc/thread-self/, with a graceful fallback on older kernels where +// /proc/thread-self doesn't exist. This method DOES NOT use SecureJoin, +// meaning that the passed string needs to be trusted. The caller _must_ call +// the returned procThreadSelfCloser function (which is runtime.UnlockOSThread) +// *only once* after it has finished using the returned path string. +func ProcThreadSelf(subpath string) (string, ProcThreadSelfCloser) { + haveProcThreadSelfOnce.Do(func() { + if _, err := os.Stat("/proc/thread-self/"); err == nil { + haveProcThreadSelf = true + } else { + logrus.Debugf("cannot stat /proc/thread-self (%v), falling back to /proc/self/task/", err) + } + }) + + // We need to lock our thread until the caller is done with the path string + // because any non-atomic operation on the path (such as opening a file, + // then reading it) could be interrupted by the Go runtime where the + // underlying thread is swapped out and the original thread is killed, + // resulting in pull-your-hair-out-hard-to-debug issues in the caller. In + // addition, the pre-3.17 fallback makes everything non-atomic because the + // same thing could happen between unix.Gettid() and the path operations. + // + // In theory, we don't need to lock in the atomic user case when using + // /proc/thread-self/, but it's better to be safe than sorry (and there are + // only one or two truly atomic users of /proc/thread-self/). + runtime.LockOSThread() + + threadSelf := "/proc/thread-self/" + if !haveProcThreadSelf { + // Pre-3.17 kernels did not have /proc/thread-self, so do it manually. + threadSelf = "/proc/self/task/" + strconv.Itoa(unix.Gettid()) + "/" + if _, err := os.Stat(threadSelf); err != nil { + // Unfortunately, this code is called from rootfs_linux.go where we + // are running inside the pid namespace of the container but /proc + // is the host's procfs. Unfortunately there is no real way to get + // the correct tid to use here (the kernel age means we cannot do + // things like set up a private fsopen("proc") -- even scanning + // NSpid in all of the tasks in /proc/self/task/*/status requires + // Linux 4.1). + // + // So, we just have to assume that /proc/self is acceptable in this + // one specific case. + if os.Getpid() == 1 { + logrus.Debugf("/proc/thread-self (tid=%d) cannot be emulated inside the initial container setup -- using /proc/self instead: %v", unix.Gettid(), err) + } else { + // This should never happen, but the fallback should work in most cases... + logrus.Warnf("/proc/thread-self could not be emulated for pid=%d (tid=%d) -- using more buggy /proc/self fallback instead: %v", os.Getpid(), unix.Gettid(), err) + } + threadSelf = "/proc/self/" + } + } + return threadSelf + subpath, runtime.UnlockOSThread +} + +// ProcThreadSelfFd is small wrapper around ProcThreadSelf to make it easier to +// create a /proc/thread-self handle for given file descriptor. +// +// It is basically equivalent to ProcThreadSelf(fmt.Sprintf("fd/%d", fd)), but +// without using fmt.Sprintf to avoid unneeded overhead. +func ProcThreadSelfFd(fd uintptr) (string, ProcThreadSelfCloser) { + return ProcThreadSelf("fd/" + strconv.FormatUint(uint64(fd), 10)) +} + // IsLexicallyInRoot is shorthand for strings.HasPrefix(path+"/", root+"/"), // but properly handling the case where path or root are "/". // @@ -156,83 +296,45 @@ func IsLexicallyInRoot(root, path string) bool { // This means that the path also must not contain ".." elements, otherwise an // error will occur. // -// This is a somewhat less safe alternative to -// , but it should -// detect attempts to trick us into creating directories outside of the root. -// We should migrate to securejoin.MkdirAll once it is merged. +// This uses securejoin.MkdirAllHandle under the hood, but it has special +// handling if unsafePath has already been scoped within the rootfs (this is +// needed for a lot of runc callers and fixing this would require reworking a +// lot of path logic). func MkdirAllInRootOpen(root, unsafePath string, mode uint32) (_ *os.File, Err error) { - // If the path is already "within" the root, use it verbatim. - fullPath := unsafePath - if !IsLexicallyInRoot(root, unsafePath) { - var err error - fullPath, err = securejoin.SecureJoin(root, unsafePath) + // If the path is already "within" the root, get the path relative to the + // root and use that as the unsafe path. This is necessary because a lot of + // MkdirAllInRootOpen callers have already done SecureJoin, and refactoring + // all of them to stop using these SecureJoin'd paths would require a fair + // amount of work. + // TODO(cyphar): Do the refactor to libpathrs once it's ready. + if IsLexicallyInRoot(root, unsafePath) { + subPath, err := filepath.Rel(root, unsafePath) if err != nil { return nil, err } - } - subPath, err := filepath.Rel(root, fullPath) - if err != nil { - return nil, err + unsafePath = subPath } // Check for any silly mode bits. if mode&^0o7777 != 0 { return nil, fmt.Errorf("tried to include non-mode bits in MkdirAll mode: 0o%.3o", mode) } + // Linux (and thus os.MkdirAll) silently ignores the suid and sgid bits if + // passed. While it would make sense to return an error in that case (since + // the user has asked for a mode that won't be applied), for compatibility + // reasons we have to ignore these bits. + if ignoredBits := mode &^ 0o1777; ignoredBits != 0 { + logrus.Warnf("MkdirAll called with no-op mode bits that are ignored by Linux: 0o%.3o", ignoredBits) + mode &= 0o1777 + } - currentDir, err := os.OpenFile(root, unix.O_DIRECTORY|unix.O_CLOEXEC, 0) + rootDir, err := os.OpenFile(root, unix.O_DIRECTORY|unix.O_CLOEXEC, 0) if err != nil { return nil, fmt.Errorf("open root handle: %w", err) } - defer func() { - if Err != nil { - currentDir.Close() - } - }() - - for _, part := range strings.Split(subPath, string(filepath.Separator)) { - switch part { - case "", ".": - // Skip over no-op components. - continue - case "..": - return nil, fmt.Errorf("possible breakout detected: found %q component in SecureJoin subpath %s", part, subPath) - } + defer rootDir.Close() - nextDir, err := system.Openat(currentDir, part, unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) - switch { - case err == nil: - // Update the currentDir. - _ = currentDir.Close() - currentDir = nextDir - - case errors.Is(err, unix.ENOTDIR): - // This might be a symlink or some other random file. Either way, - // error out. - return nil, fmt.Errorf("cannot mkdir in %s/%s: %w", currentDir.Name(), part, unix.ENOTDIR) - - case errors.Is(err, os.ErrNotExist): - // Luckily, mkdirat will not follow trailing symlinks, so this is - // safe to do as-is. - if err := system.Mkdirat(currentDir, part, mode); err != nil { - return nil, err - } - // Open the new directory. There is a race here where an attacker - // could swap the directory with a different directory, but - // MkdirAll's fuzzy semantics mean we don't care about that. - nextDir, err := system.Openat(currentDir, part, unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0) - if err != nil { - return nil, fmt.Errorf("open newly created directory: %w", err) - } - // Update the currentDir. - _ = currentDir.Close() - currentDir = nextDir - - default: - return nil, err - } - } - return currentDir, nil + return securejoin.MkdirAllHandle(rootDir, unsafePath, int(mode)) } // MkdirAllInRoot is a wrapper around MkdirAllInRootOpen which closes the @@ -244,3 +346,18 @@ func MkdirAllInRoot(root, unsafePath string, mode uint32) error { } return err } + +// Openat is a Go-friendly openat(2) wrapper. +func Openat(dir *os.File, path string, flags int, mode uint32) (*os.File, error) { + dirFd := unix.AT_FDCWD + if dir != nil { + dirFd = int(dir.Fd()) + } + flags |= unix.O_CLOEXEC + + fd, err := unix.Openat(dirFd, path, flags, mode) + if err != nil { + return nil, &os.PathError{Op: "openat", Path: path, Err: err} + } + return os.NewFile(uintptr(fd), dir.Name()+"/"+path), nil +} diff --git a/src/bpm/vendor/modules.txt b/src/bpm/vendor/modules.txt index 78e08337..922aa084 100644 --- a/src/bpm/vendor/modules.txt +++ b/src/bpm/vendor/modules.txt @@ -1,11 +1,11 @@ -# code.cloudfoundry.org/bytefmt v0.13.0 +# code.cloudfoundry.org/bytefmt v0.14.0 ## explicit; go 1.22.0 code.cloudfoundry.org/bytefmt -# code.cloudfoundry.org/clock v1.16.0 +# code.cloudfoundry.org/clock v1.17.0 ## explicit; go 1.22.0 code.cloudfoundry.org/clock code.cloudfoundry.org/clock/fakeclock -# code.cloudfoundry.org/lager/v3 v3.10.0 +# code.cloudfoundry.org/lager/v3 v3.11.0 ## explicit; go 1.22.0 code.cloudfoundry.org/lager/v3 code.cloudfoundry.org/lager/v3/internal/truncate @@ -37,7 +37,7 @@ github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/function github.com/google/go-cmp/cmp/internal/value -# github.com/google/pprof v0.0.0-20241017200806-017d972448fc +# github.com/google/pprof v0.0.0-20241023014458-598669927662 ## explicit; go 1.22 github.com/google/pprof/profile # github.com/inconshreveable/mousetrap v1.1.0 @@ -49,6 +49,9 @@ github.com/kr/pty # github.com/moby/sys/mountinfo v0.7.2 ## explicit; go 1.17 github.com/moby/sys/mountinfo +# github.com/moby/sys/userns v0.1.0 +## explicit; go 1.21 +github.com/moby/sys/userns # github.com/onsi/ginkgo/v2 v2.20.2 ## explicit; go 1.22 github.com/onsi/ginkgo/v2 @@ -85,14 +88,11 @@ 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.1.15 -## explicit; go 1.18 +# github.com/opencontainers/runc v1.2.0 +## explicit; go 1.22 github.com/opencontainers/runc/libcontainer/cgroups github.com/opencontainers/runc/libcontainer/configs github.com/opencontainers/runc/libcontainer/devices -github.com/opencontainers/runc/libcontainer/system -github.com/opencontainers/runc/libcontainer/user -github.com/opencontainers/runc/libcontainer/userns github.com/opencontainers/runc/libcontainer/utils # github.com/opencontainers/runtime-spec v1.2.0 ## explicit