diff --git a/go.mod b/go.mod index a2668ea05384..5ae5f1487b43 100644 --- a/go.mod +++ b/go.mod @@ -106,7 +106,7 @@ require ( google.golang.org/grpc v1.68.1 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 google.golang.org/protobuf v1.35.2 - kernel.org/pub/linux/libs/security/libcap/cap v1.2.70 + kernel.org/pub/linux/libs/security/libcap/cap v1.2.73 ) require ( @@ -179,7 +179,7 @@ require ( golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - kernel.org/pub/linux/libs/security/libcap/psx v1.2.70 // indirect + kernel.org/pub/linux/libs/security/libcap/psx v1.2.73 // indirect sigs.k8s.io/yaml v1.4.0 // indirect tags.cncf.io/container-device-interface v0.8.0 // indirect tags.cncf.io/container-device-interface/specs-go v0.8.0 // indirect diff --git a/go.sum b/go.sum index 8f6f586e867d..11c5c236fd67 100644 --- a/go.sum +++ b/go.sum @@ -570,10 +570,10 @@ gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -kernel.org/pub/linux/libs/security/libcap/cap v1.2.70 h1:QnLPkuDWWbD5C+3DUA2IUXai5TK6w2zff+MAGccqdsw= -kernel.org/pub/linux/libs/security/libcap/cap v1.2.70/go.mod h1:/iBwcj9nbLejQitYvUm9caurITQ6WyNHibJk6Q9fiS4= -kernel.org/pub/linux/libs/security/libcap/psx v1.2.70 h1:HsB2G/rEQiYyo1bGoQqHZ/Bvd6x1rERQTNdPr1FyWjI= -kernel.org/pub/linux/libs/security/libcap/psx v1.2.70/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24= +kernel.org/pub/linux/libs/security/libcap/cap v1.2.73 h1:Th2b8jljYqkyZKS3aD3N9VpYsQpHuXLgea+SZUIfODA= +kernel.org/pub/linux/libs/security/libcap/cap v1.2.73/go.mod h1:hbeKwKcboEsxARYmcy/AdPVN11wmT/Wnpgv4k4ftyqY= +kernel.org/pub/linux/libs/security/libcap/psx v1.2.73 h1:SEAEUiPVylTD4vqqi+vtGkSnXeP2FcRO3FoZB1MklMw= +kernel.org/pub/linux/libs/security/libcap/psx v1.2.73/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/vendor/kernel.org/pub/linux/libs/security/libcap/cap/cap.go b/vendor/kernel.org/pub/linux/libs/security/libcap/cap/cap.go index df324362b35f..0bff389ba4f8 100644 --- a/vendor/kernel.org/pub/linux/libs/security/libcap/cap/cap.go +++ b/vendor/kernel.org/pub/linux/libs/security/libcap/cap/cap.go @@ -7,7 +7,7 @@ // from code. You can read more about how Capabilities are intended to // work here: // -// https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/33528.pdf +// https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/33528.pdf // // This package supports native Go bindings for all the features // described in that paper as well as supporting subsequent changes to @@ -15,23 +15,23 @@ // // Some simple things you can do with this package are: // -// // Read and display the capabilities of the running process -// c := cap.GetProc() -// iab := cap.IABGetProc() -// log.Printf("this process has these caps: %q [%v]", c, iab) +// // Read and display the capabilities of the running process +// c := cap.GetProc() +// iab := cap.IABGetProc() +// log.Printf("this process has these caps: %q [%v]", c, iab) // -// // Drop any privilege a process might have (including for root, -// // but note root 'owns' a lot of system files so a cap-limited -// // root can still do considerable damage to a running system). -// old := cap.GetProc() -// empty := cap.NewSet() -// if err := empty.SetProc(); err != nil { -// log.Fatalf("failed to drop privilege: %q -> %q: %v", old, empty, err) -// } -// now := cap.GetProc() -// if cf, _ := now.Cf(empty); cf != 0 { -// log.Fatalf("failed to fully drop privilege: have=%q, wanted=%q", now, empty) -// } +// // Drop any privilege a process might have (including for root, +// // but note root 'owns' a lot of system files so a cap-limited +// // root can still do considerable damage to a running system). +// old := cap.GetProc() +// empty := cap.NewSet() +// if err := empty.SetProc(); err != nil { +// log.Fatalf("failed to drop privilege: %q -> %q: %v", old, empty, err) +// } +// now := cap.GetProc() +// if cf, _ := now.Cf(empty); cf != 0 { +// log.Fatalf("failed to fully drop privilege: have=%q, wanted=%q", now, empty) +// } // // The "cap" package operates with POSIX semantics for security // state. That is all OS threads are kept in sync at all times. The @@ -195,6 +195,7 @@ type syscaller struct { // caprcall provides a pointer etc wrapper for the system calls // associated with getcap. +// //go:uintptrescapes func (sc *syscaller) caprcall(call uintptr, h *header, d []data) error { x := uintptr(0) @@ -210,6 +211,7 @@ func (sc *syscaller) caprcall(call uintptr, h *header, d []data) error { // capwcall provides a pointer etc wrapper for the system calls // associated with setcap. +// //go:uintptrescapes func (sc *syscaller) capwcall(call uintptr, h *header, d []data) error { x := uintptr(0) @@ -467,9 +469,10 @@ func (sc *syscaller) setAmbient(enable bool, val ...Value) error { // permission is available to perform this task. The settings are // performed in order and the function returns immediately an error is // detected. Use GetAmbient() to unravel where things went -// wrong. Note, the cap package manages an abstraction IAB that -// captures all three inheritable vectors in a single type. Consider -// using that. +// wrong. +// +// Note, the cap package manages an abstraction IAB that captures all +// three inheritable vectors in a single type. Consider using that. func SetAmbient(enable bool, val ...Value) error { state, sc := scwStateSC() defer scwSetState(launchBlocked, state, -1) diff --git a/vendor/kernel.org/pub/linux/libs/security/libcap/cap/iab.go b/vendor/kernel.org/pub/linux/libs/security/libcap/cap/iab.go index da189bef3dfc..663a339a72f7 100644 --- a/vendor/kernel.org/pub/linux/libs/security/libcap/cap/iab.go +++ b/vendor/kernel.org/pub/linux/libs/security/libcap/cap/iab.go @@ -109,11 +109,11 @@ func (iab *IAB) Dup() (*IAB, error) { // // Example, replace this: // -// iab := IABInit() +// iab := IABInit() // // with this: // -// iab := NewIAB() +// iab := NewIAB() func IABInit() *IAB { return NewIAB() } @@ -212,25 +212,44 @@ func (iab *IAB) String() string { // The iab is known to be locked by the caller. func (sc *syscaller) iabSetProc(iab *IAB) (err error) { temp := GetProc() - var raising uint32 + raising := false + bounder := false for i := 0; i < words; i++ { newI := iab.i[i] oldIP := temp.flat[i][Inheritable] | temp.flat[i][Permitted] - raising |= (newI & ^oldIP) | iab.a[i] | iab.nb[i] + raising = raising || (newI & ^oldIP != 0) + if iab.nb[i] != 0 { + bounder = true + } temp.flat[i][Inheritable] = newI } + if bounder { + bounder = false + for c := Value(maxValues); c > 0; { + c-- + offset, mask := omask(c) + if iab.nb[offset]&mask == 0 { + continue + } + if b, _ := GetBound(c); b { + bounder = true + raising = true + break + } + } + } working, err2 := temp.Dup() if err2 != nil { err = err2 return } - if raising != 0 { + if raising { if err = working.SetFlag(Effective, true, SETPCAP); err != nil { return } - if err = sc.setProc(working); err != nil { - return - } + } + if err = sc.setProc(working); err != nil { + return } defer func() { if err2 := sc.setProc(temp); err == nil { @@ -246,7 +265,7 @@ func (sc *syscaller) iabSetProc(iab *IAB) (err error) { if iab.a[offset]&mask != 0 { err = sc.setAmbient(true, c) } - if err == nil && iab.nb[offset]&mask != 0 { + if bounder && err == nil && iab.nb[offset]&mask != 0 { err = sc.dropBound(c) } if err != nil { @@ -260,7 +279,9 @@ func (sc *syscaller) iabSetProc(iab *IAB) (err error) { // capability vectors of the current process using the content, // iab. The Bounding vector strongly affects the potential for setting // other bits, so this function carefully performs the combined -// operation in the most flexible manner. +// operation in the most flexible manner. If the desired IAB value +// will change the Bounding value, cap.SETPCAP must be a Permitted +// value. func (iab *IAB) SetProc() error { if err := iab.good(); err != nil { return err diff --git a/vendor/kernel.org/pub/linux/libs/security/libcap/psx/doc.go b/vendor/kernel.org/pub/linux/libs/security/libcap/psx/doc.go index c4ba829a118a..7fe48527347a 100644 --- a/vendor/kernel.org/pub/linux/libs/security/libcap/psx/doc.go +++ b/vendor/kernel.org/pub/linux/libs/security/libcap/psx/doc.go @@ -1,11 +1,9 @@ // Package psx provides support for system calls that are run -// simultaneously on all threads under Linux. +// simultaneously on all threads under Linux. It supports tool chains +// after go1.16. Earlier toolchains had no reliable way to support +// this because of // -// This property can be used to work around a historical lack of -// native Go support for such a feature. Something that is the subject -// of: -// -// https://github.com/golang/go/issues/1435 +// https://bugzilla.kernel.org/show_bug.cgi?id=219478 // // The package works differently depending on whether or not // CGO_ENABLED is 0 or 1. @@ -13,47 +11,41 @@ // In the former case, psx is a low overhead wrapper for the two // native go calls: syscall.AllThreadsSyscall() and // syscall.AllThreadsSyscall6() introduced in go1.16. We provide this -// wrapping to minimize client source code changes when compiling with -// or without CGo enabled. +// package wrapping to minimize client source code changes when +// compiling with or without CGo enabled. // -// In the latter case, and toolchains prior to go1.16, it works via -// CGo wrappers for system call functions that call the C [lib]psx -// functions of these names. This ensures that the system calls -// execute simultaneously on all the pthreads of the Go (and CGo) -// combined runtime. +// In the latter case it works via CGo wrappers for system call +// functions that call the C [lib]psx functions of these names. This +// ensures that the system calls execute simultaneously on all the +// threads of the Go (and CGo) combined runtime. // -// With CGo, the psx support works in the following way: the pthread +// With CGo, the psx support works in the following way: the thread // that is first asked to execute the syscall does so, and determines // if it succeeds or fails. If it fails, it returns immediately -// without attempting the syscall on other pthreads. If the initial +// without attempting the syscall on other threads. If the initial // attempt succeeds, however, then the runtime is stopped in order for -// the same system call to be performed on all the remaining pthreads -// of the runtime. Once all pthreads have completed the syscall, the -// return codes are those obtained by the first pthread's invocation +// the same system call to be performed on all the remaining threads +// of the runtime. Once all threads have completed the syscall, the +// return codes are those obtained by the first thread's invocation // of the syscall. // // Note, there is no need to use this variant of syscall where the // syscalls only read state from the kernel. However, since Go's -// runtime freely migrates code execution between pthreads, support of +// runtime freely migrates code execution between threads, support of // this type is required for any successful attempt to fully drop or // modify the privilege of a running Go program under Linux. // // More info on how Linux privilege works and examples of using this // package can be found here: // -// https://sites.google.com/site/fullycapable -// -// WARNING: For older go toolchains (prior to go1.15), correct -// compilation of this package may require an extra workaround step: -// -// The workaround is to build with the following CGO_LDFLAGS_ALLOW in -// effect (here the syntax is that of bash for defining an environment -// variable): -// -// export CGO_LDFLAGS_ALLOW="-Wl,-?-wrap[=,][^-.@][^,]*" +// https://sites.google.com/site/fullycapable // +// WARNING: For older go toolchains (prior to go1.16), the code should +// mostly work as far back as go1.11. However, like support for +// C.setuid(), this support is fragile and may hang. See the above bug +// for details. // -// Copyright (c) 2019,20 Andrew G. Morgan +// Copyright (c) 2019,20,24 Andrew G. Morgan // // The psx package is licensed with a (you choose) BSD 3-clause or // GPL2. See LICENSE file for details. diff --git a/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx.c b/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx.c index 65eb2aaa4cd8..c4430d38b48c 100644 --- a/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx.c +++ b/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx.c @@ -1,31 +1,36 @@ /* - * Copyright (c) 2019-21 Andrew G Morgan + * Copyright (c) 2019-21,2024 Andrew G Morgan * * This file contains a collection of routines that perform thread * synchronization to ensure that a whole process is running as a - * single privilege entity - independent of the number of pthreads. + * single privilege entity - independent of the number of threads. * * The whole file would be unnecessary if glibc exported an explicit * psx_syscall()-like function that leveraged the nptl:setxid * mechanism to synchronize thread state over the whole process. */ -#undef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 199309L #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include -#include +#include +#include /* pthread_atfork() */ #include #include #include +#include #include +#include #include #include -#include #include +#include +#include + +/* Not reliably defined by *libc so, alias the direct syscall. */ +#define _psx_gettid() syscall(SYS_gettid) #include "psx_syscall.h" @@ -34,13 +39,13 @@ static void *_psx_calloc(const char *file, const int line, size_t nmemb, size_t size) { void *ptr = calloc(nmemb, size); - fprintf(stderr, "psx:%d:%s:%d: calloc(%ld, %ld) -> %p\n", gettid(), + fprintf(stderr, "psx:%d:%s:%d: calloc(%ld, %ld) -> %p\n", _psx_gettid(), file, line, (long int)nmemb, (long int)size, ptr); return ptr; } static void _psx_free(const char *file, const int line, void *ptr) { - fprintf(stderr, "psx:%d:%s:%d: free(%p)\n", gettid(), file, line, ptr); + fprintf(stderr, "psx:%d:%s:%d: free(%p)\n", _psx_gettid(), file, line, ptr); return free(ptr); } @@ -68,40 +73,55 @@ void psx_load_syscalls(long int (**syscall_fn)(long int, } /* - * type to keep track of registered threads. + * Since we no longer (libcap-2.72) operate at the pthreads + * abstraction, we need our own mutex etc implementation. */ -typedef struct registered_thread_s { - struct registered_thread_s *next, *prev; - pthread_t thread; - pthread_mutex_t mu; - int pending; - int gone; - long int retval; - pid_t tid; -} registered_thread_t; -static pthread_once_t psx_tracker_initialized = PTHREAD_ONCE_INIT; +typedef uint8_t psx_mutex_t; +#define _psx_mu_blocked(x) \ + __atomic_test_and_set((void *)(x), __ATOMIC_SEQ_CST) +#define _psx_mu_lock(x) \ + while (_psx_mu_blocked(x)) sched_yield() +#define _psx_mu_unlock(x) \ + __atomic_clear((void *)(x), __ATOMIC_SEQ_CST) +#define _psx_mu_unlock_return(x, y) \ + do { _psx_mu_unlock(x); return (y); } while (0) +#define _psx_mu_cond_wait(x) \ + do { \ + _psx_mu_unlock(x); \ + sched_yield(); \ + _psx_mu_lock(x); \ + } while (0) typedef enum { _PSX_IDLE = 0, _PSX_SETUP = 1, _PSX_SYSCALL = 2, - _PSX_CREATE = 3, - _PSX_INFORK = 4, - _PSX_EXITING = 5, + _PSX_EXITING = 3, } psx_tracker_state_t; +/* + * Tracking threads is done via a hash map of these objects. + */ +typedef struct psx_thread_ref_s { + long sweep; + long pending; + long tid; + long retval; +} psx_thread_ref_t; + /* * This global structure holds the global coordination state for - * libcap's psx_posix_syscall() support. + * libcap's psx_syscall() support. */ static struct psx_tracker_s { - int has_forked; + long pid; + char *pid_path; - pthread_mutex_t state_mu; - pthread_cond_t cond; /* this is only used to wait on 'state' changes */ + psx_mutex_t state_mu; psx_tracker_state_t state; int initialized; + int incomplete; int psx_sig; psx_sensitivity_t sensitivity; @@ -114,35 +134,126 @@ static struct psx_tracker_s { struct sigaction sig_action; struct sigaction chained_action; - registered_thread_t *root; + + int map_entries; + long map_mask; + psx_thread_ref_t *map; } psx_tracker; +/* psx_mix is our trivial hash mixing for the thread reference map */ +static long psx_mix(long value) { + return value ^ (value >> 7) ^ (value >> 13) ^ (value >> 23); +} + +static void psx_set_map(int size) +{ + psx_tracker.map_entries = size; + psx_tracker.map_mask = size - 1; + psx_tracker.map = calloc(psx_tracker.map_entries, sizeof(psx_thread_ref_t)); +} + /* - * psx_action_key is used for thread local storage of the thread's - * registration. + * Forward declaration */ -pthread_key_t psx_action_key; +static void _psx_cleanup(void); + +#define taskdir_fmt "/proc/%ld/task" /* - * psx_do_registration called locked and creates a tracker entry for - * the current thread with a TLS specific key pointing at the threads - * specific tracker. + * Every time we detect a new process, the first thread to recognize + * this resets some of the psx_tracker fields. */ -static void *psx_do_registration(void) { - registered_thread_t *node = calloc(1, sizeof(registered_thread_t)); - if (node == NULL) { - perror("unable to register psx handler"); - _exit(1); +static void _psx_proc_start(void) +{ + long pid = getpid(); + psx_tracker.pid = pid; + if (psx_tracker.pid_path == NULL) { + psx_tracker.pid_path = malloc(3*sizeof(pid) + 13 /* strlen(taskdir_fmt) */); } - pthread_mutex_init(&node->mu, NULL); - node->thread = pthread_self(); - pthread_setspecific(psx_action_key, node); - node->next = psx_tracker.root; - if (node->next) { - node->next->prev = node; + sprintf(psx_tracker.pid_path, taskdir_fmt, pid); + psx_tracker.state = _PSX_IDLE; + psx_tracker.cmd.active = 0; +} + +static void _psx_new_proc(void) +{ + _psx_mu_unlock(&psx_tracker.state_mu); + _psx_proc_start(); +} + +/* + * psx_syscall_start initializes the psx subsystem. It is called + * once and while locked. + */ +static void psx_syscall_start(void) +{ + /* + * All sorts of things are assumed by Linux and glibc and/or musl + * about signal handlers and which can be blocked. Go has its own + * idiosyncrasies too. We tried SIGRTMAX until + * + * https://bugzilla.kernel.org/show_bug.cgi?id=210533 + * + * Our current strategy is to aggressively intercept SIGSYS, + * something that is confirmed to be the case each time _PSX_SETUP + * state is entered. + */ + psx_tracker.psx_sig = SIGSYS; + psx_set_map(256); + atexit(_psx_cleanup); + pthread_atfork(NULL, NULL, _psx_new_proc); + psx_tracker.initialized = 1; +} + +/* + * This is the only way this library globally locks. Note, this is not + * to be confused with psx_sig (interrupt) blocking - which is + * performed when the signal handler is being confirmed. + */ +static void psx_lock(void) +{ + _psx_mu_lock(&psx_tracker.state_mu); + if (!psx_tracker.initialized) { + _psx_proc_start(); + psx_syscall_start(); + } +} + +/* + * This is the only way this library unlocks. + */ +static void psx_unlock(void) +{ + _psx_mu_unlock(&psx_tracker.state_mu); +} + +/* + * psx_cond_wait unlocks and waits to obtain the lock again, allowing + * other code to run that may require the lock. This is the only way + * the psx code waits like this. + */ +static void psx_cond_wait(void) +{ + _psx_mu_cond_wait(&psx_tracker.state_mu); +} + +/* + * _psx_cleanup its called when the program exits. It is used to free + * any memory used by the thread tracker. + */ +static void _psx_cleanup(void) { + /* + * We enter the exiting state and never exit that. This cleanup is + * only done at program exit. + */ + psx_lock(); + while (psx_tracker.state != _PSX_IDLE) { + psx_cond_wait(); } - psx_tracker.root = node; - return node; + psx_tracker.state = _PSX_EXITING; + free(psx_tracker.map); + free(psx_tracker.pid_path); + psx_unlock(); } /* @@ -150,9 +261,10 @@ static void *psx_do_registration(void) { * thread and signals it is no longer pending. */ static void psx_posix_syscall_actor(int signum, siginfo_t *info, void *ignore) { - /* bail early if this isn't something we recognize */ + /* bail early to the next in the chain if not something we recognize */ if (signum != psx_tracker.psx_sig || !psx_tracker.cmd.active || - info == NULL || info->si_code != SI_TKILL || info->si_pid != getpid()) { + info == NULL || info->si_code != SI_TKILL || + info->si_pid != psx_tracker.pid) { if (psx_tracker.chained_action.sa_sigaction != 0) { psx_tracker.chained_action.sa_sigaction(signum, info, ignore); } @@ -176,43 +288,34 @@ static void psx_posix_syscall_actor(int signum, siginfo_t *info, void *ignore) { } /* - * This handler can only be called on registered threads which - * have had this specific defined at start-up. (But see the - * subsequent test.) + * communicate the result of the thread's attempt to perform the + * syscall. */ - registered_thread_t *ref = pthread_getspecific(psx_action_key); - if (ref) { - pthread_mutex_lock(&ref->mu); - ref->pending = 0; - ref->retval = retval; - ref->tid = syscall(SYS_gettid); - pthread_mutex_unlock(&ref->mu); - } /* - * else thread must be dying and its psx_action_key has already - * been cleaned up. - */ -} + long tid = _psx_gettid(); -/* - * Some forward declarations for the initialization - * psx_syscall_start() routine. - */ -static void _psx_cleanup(void); -static void _psx_prepare_fork(void); -static void _psx_fork_completed(void); -static void _psx_forked_child(void); -int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr, - void *(*start_routine) (void *), void *arg); - -/* - * psx requires this function to be provided by the linkage wrapping. - */ -extern int __real_pthread_create(pthread_t *thread, const pthread_attr_t *attr, - void *(*start_routine) (void *), void *arg); + psx_lock(); + psx_thread_ref_t *ref = &psx_tracker.map[psx_mix(tid) & psx_tracker.map_mask]; + ref->retval = retval; + ref->pending = 0; + /* + * Block this thread until all threads have been interrupted. + * This prevents threads clone()ing after running the syscall and + * confusing the psx mechanism into thinking they need to also run + * the syscall. They wouldn't need to run it, because they would + * inherit the thread state of a syscall that has already + * happened. However, figuring that out for an unblocked thread is + * hard, so we prevent it from happening. + */ + while (psx_tracker.cmd.active) { + psx_cond_wait(); + } + psx_tracker.incomplete--; + psx_unlock(); +} /* - * psx_confirm_sigaction reconfirms that the psx handler is the first - * handler to respond to the psx signal. It assumes that + * psx_confirm_sigaction (re)confirms that the psx handler is the + * first handler to respond to the psx signal. It assumes that * psx_tracker.psx_sig has been set. */ static void psx_confirm_sigaction(void) { @@ -238,54 +341,6 @@ static void psx_confirm_sigaction(void) { sigprocmask(SIG_SETMASK, &orig, NULL); } -/* - * psx_syscall_start initializes the subsystem including initializing - * the mutex. - */ -static void psx_syscall_start(void) { - pthread_mutex_init(&psx_tracker.state_mu, NULL); - pthread_cond_init(&psx_tracker.cond, NULL); - pthread_key_create(&psx_action_key, NULL); - pthread_atfork(_psx_prepare_fork, _psx_fork_completed, _psx_forked_child); - - /* - * All sorts of things are assumed by Linux and glibc and/or musl - * about signal handlers and which can be blocked. Go has its own - * idiosyncrasies too. We tried SIGRTMAX until - * - * https://bugzilla.kernel.org/show_bug.cgi?id=210533 - * - * Our current strategy is to aggressively intercept SIGSYS. - */ - psx_tracker.psx_sig = SIGSYS; - - psx_confirm_sigaction(); - psx_do_registration(); /* register the main thread. */ - atexit(_psx_cleanup); - - psx_tracker.initialized = 1; -} - -/* - * This is the only way this library globally locks. Note, this is not - * to be confused with psx_sig (interrupt) blocking - which is - * performed around thread creation and when the signal handler is - * being confirmed. - */ -static void psx_lock(void) -{ - pthread_once(&psx_tracker_initialized, psx_syscall_start); - pthread_mutex_lock(&psx_tracker.state_mu); -} - -/* - * This is the only way this library unlocks. - */ -static void psx_unlock(void) -{ - pthread_mutex_unlock(&psx_tracker.state_mu); -} - /* * under lock perform a state transition. Changing state is generally * done via this function. However, there is a single exception in @@ -295,13 +350,9 @@ static void psx_new_state(psx_tracker_state_t was, psx_tracker_state_t is) { psx_lock(); while (psx_tracker.state != was) { - pthread_cond_wait(&psx_tracker.cond, &psx_tracker.state_mu); + psx_cond_wait(); } psx_tracker.state = is; - if (is == _PSX_IDLE) { - /* only announce newly idle states since that is all we wait for */ - pthread_cond_signal(&psx_tracker.cond); - } psx_unlock(); } @@ -316,218 +367,6 @@ long int psx_syscall6(long int syscall_nr, return psx_syscall(syscall_nr, arg1, arg2, arg3, arg4, arg5, arg6); } -static void _psx_prepare_fork(void) { - /* - * obtain global lock - we don't want any syscalls while the fork - * is occurring since it may interfere with the preparation for - * the fork. - */ - psx_new_state(_PSX_IDLE, _PSX_INFORK); -} - -static void _psx_fork_completed(void) { - /* - * The only way we can get here is if state is _PSX_INFORK and was - * previously _PSX_IDLE. Now that the fork has completed, the - * parent can continue as if it hadn't happened - the forked child - * does not tie its security state to that of the parent process - * and threads. - * - * We don't strictly need to change the psx_tracker.state since we - * hold the mutex over the fork, but we do to make deadlock - * debugging easier. - */ - psx_new_state(_PSX_INFORK, _PSX_IDLE); -} - -static void _psx_forked_child(void) { - /* - * The only way we can get here is if state is _PSX_INFORK and was - * previously _PSX_IDLE. However, none of the registered threads - * exist in this newly minted child process, so we have to reset - * the tracking structure to avoid any confusion. We also scuttle - * any chance of the PSX API working on more than one thread in - * the child by leaving the state as _PSX_INFORK. We do support - * all psx_syscall()s by reverting to them being direct in the - * fork()ed child. - * - * We do this because the glibc man page for fork() suggests that - * only a subset of things will work post fork(). Specifically, - * only a "async-signal-safe functions (see signal-safety(7)) - * until such time as it calls execve(2)" can be relied upon. That - * man page suggests that you can't expect mutexes to work: "not - * async-signal-safe because it uses pthread_mutex_lock(3) - * internally.". - */ - registered_thread_t *next, *old_root; - old_root = psx_tracker.root; - psx_tracker.root = NULL; - - psx_tracker.has_forked = 1; - - for (; old_root; old_root = next) { - next = old_root->next; - memset(old_root, 0, sizeof(*old_root)); - free(old_root); - } -} - -/* - * called locked to unregister a node from the tracker. - */ -static void psx_do_unregister(registered_thread_t *node) { - if (psx_tracker.root == node) { - psx_tracker.root = node->next; - } - if (node->next) { - node->next->prev = node->prev; - } - if (node->prev) { - node->prev->next = node->next; - } - pthread_mutex_destroy(&node->mu); - memset(node, 0, sizeof(*node)); - free(node); -} - -typedef struct { - void *(*fn)(void *); - void *arg; - sigset_t sigbits; -} psx_starter_t; - -/* - * _psx_exiting is used to cleanup the node for the thread on its exit - * path. This is needed for musl libc: - * - * https://bugzilla.kernel.org/show_bug.cgi?id=208477 - * - * and likely wise for glibc too: - * - * https://sourceware.org/bugzilla/show_bug.cgi?id=12889 - */ -static void _psx_exiting(void *node) { - /* - * Until we are in the _PSX_EXITING state, we must not block the - * psx_sig interrupt for this dying thread. That is, until this - * exiting thread can set ref->gone to 1, this dying thread is - * still participating in the psx syscall distribution. - * - * See https://github.com/golang/go/issues/42494 for a situation - * where this code is called with psx_tracker.psx_sig blocked. - */ - sigset_t sigbit, orig_sigbits; - sigemptyset(&sigbit); - pthread_sigmask(SIG_UNBLOCK, &sigbit, &orig_sigbits); - sigaddset(&sigbit, psx_tracker.psx_sig); - pthread_sigmask(SIG_UNBLOCK, &sigbit, NULL); - - /* - * With psx_tracker.psx_sig unblocked we can wait until this - * thread can enter the _PSX_EXITING state. - */ - psx_new_state(_PSX_IDLE, _PSX_EXITING); - - /* - * We now indicate that this thread is no longer participating in - * the psx mechanism. - */ - registered_thread_t *ref = node; - pthread_mutex_lock(&ref->mu); - ref->gone = 1; - pthread_mutex_unlock(&ref->mu); - - /* - * At this point, we can restore the calling sigmask to whatever - * the caller thought was appropriate for a dying thread to have. - */ - pthread_sigmask(SIG_SETMASK, &orig_sigbits, NULL); - - /* - * Allow the rest of the psx system to carry on as per normal. - */ - psx_new_state(_PSX_EXITING, _PSX_IDLE); -} - -/* - * _psx_start_fn is a trampoline for the intended start function, it - * is called blocked (_PSX_CREATE), but releases the block before - * calling starter->fn. Before releasing the block, the TLS specific - * attributes are initialized for use by the interrupt handler under - * the psx mutex, so it doesn't race with an interrupt received by - * this thread and the interrupt handler does not need to poll for - * that specific attribute to be present (which is problematic during - * thread shutdown). - */ -static void *_psx_start_fn(void *data) { - void *node = psx_do_registration(); - - psx_new_state(_PSX_CREATE, _PSX_IDLE); - - psx_starter_t *starter = data; - pthread_sigmask(SIG_SETMASK, &starter->sigbits, NULL); - void *(*fn)(void *) = starter->fn; - void *arg = starter->arg; - - memset(data, 0, sizeof(*starter)); - free(data); - - void *ret; - - pthread_cleanup_push(_psx_exiting, node); - ret = fn(arg); - pthread_cleanup_pop(1); - - return ret; -} - -/* - * __wrap_pthread_create is the wrapped destination of all regular - * pthread_create calls. - */ -int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr, - void *(*start_routine) (void *), void *arg) { - psx_starter_t *starter = calloc(1, sizeof(psx_starter_t)); - if (starter == NULL) { - perror("failed at thread creation"); - exit(1); - } - starter->fn = start_routine; - starter->arg = arg; - /* - * Until we are in the _PSX_IDLE state and locked, we must not - * block the psx_sig interrupt for this parent thread. Arrange - * that parent thread and newly created one can restore signal - * mask. - */ - sigset_t sigbit, orig_sigbits; - sigemptyset(&sigbit); - pthread_sigmask(SIG_UNBLOCK, &sigbit, &starter->sigbits); - sigaddset(&sigbit, psx_tracker.psx_sig); - pthread_sigmask(SIG_UNBLOCK, &sigbit, &orig_sigbits); - - psx_new_state(_PSX_IDLE, _PSX_CREATE); - - /* - * until the child thread has been blessed with its own TLS - * specific attribute(s) we prevent either the parent thread or - * the new one from experiencing a PSX interrupt. - */ - pthread_sigmask(SIG_BLOCK, &sigbit, NULL); - - int ret = __real_pthread_create(thread, attr, _psx_start_fn, starter); - if (ret > 0) { - psx_new_state(_PSX_CREATE, _PSX_IDLE); - memset(starter, 0, sizeof(*starter)); - free(starter); - } /* else unlock happens in _psx_start_fn */ - - /* the parent can once again receive psx interrupt signals */ - pthread_sigmask(SIG_SETMASK, &orig_sigbits, NULL); - - return ret; -} - /* * __psx_immediate_syscall does one syscall using the current * process. @@ -558,6 +397,31 @@ static long int __psx_immediate_syscall(long int syscall_nr, psx_tracker.cmd.arg2, psx_tracker.cmd.arg3); } +/* + * glibc diropen/readdir API uses malloc/free internally and + * empirically employ some sort of private mutex. The fact that psx + * interrupts threads in arbitrary places guarantees that occasionally + * the code in __psx_syscall() will interrupt functions in the middle + * of performing these calls from other threads. Thus (and observed + * with the libcap_psx_test) it's inevitable that this will interrupt + * those functions while they hold a private lock. The net effect is + * that we will fall into a deadlock condition if __psx_syscall() uses + * diropen/readdir. So, we have opted to use raw system calls to read + * directories instead. The whole of the psx functionality is really + * low level, and only aimed at supporting Linux with its non-POSIX + * LWP threading model, so we're OK with that. + */ + +#define BUF_SIZE 4096 + +struct psx_linux_dirent64 { + long long d_ino; + long long d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[]; +}; + /* * __psx_syscall performs the syscall on the current thread and if no * error is detected it ensures that the syscall is also performed on @@ -576,7 +440,7 @@ static long int __psx_immediate_syscall(long int syscall_nr, */ long int __psx_syscall(long int syscall_nr, ...) { long int arg[7]; - int i; + long i; va_list aptr; va_start(aptr, syscall_nr); @@ -591,90 +455,132 @@ long int __psx_syscall(long int syscall_nr, ...) { return -1; } - if (psx_tracker.has_forked) { - return __psx_immediate_syscall(syscall_nr, count, arg); - } - psx_new_state(_PSX_IDLE, _PSX_SETUP); psx_confirm_sigaction(); - long int ret; - - ret = __psx_immediate_syscall(syscall_nr, count, arg); - if (ret == -1 || !psx_tracker.initialized) { + long int ret = __psx_immediate_syscall(syscall_nr, count, arg); + if (ret == -1) { psx_new_state(_PSX_SETUP, _PSX_IDLE); goto defer; } int restore_errno = errno; - psx_new_state(_PSX_SETUP, _PSX_SYSCALL); psx_tracker.cmd.active = 1; - pthread_t self = pthread_self(); - registered_thread_t *next = NULL, *ref; - - psx_lock(); - for (ref = psx_tracker.root; ref; ref = next) { - next = ref->next; - if (ref->thread == self) { - continue; - } - pthread_mutex_lock(&ref->mu); - ref->pending = 1; - int gone = ref->gone; - if (!gone) { - gone = pthread_kill(ref->thread, psx_tracker.psx_sig) != 0; - } - pthread_mutex_unlock(&ref->mu); - if (!gone) { - continue; + /* + * cleaning up before we start helps a fork()ed child not inherit + * confusion from its parent. + */ + memset(psx_tracker.map, 0, + psx_tracker.map_entries*sizeof(psx_thread_ref_t)); + + long self = _psx_gettid(), sweep = 1; + int some, incomplete, mismatch = 0, verified = 0; + do { + incomplete = 0; /* count threads to return from signal handler */ + some = 0; /* count threads still pending */ + sweep++; + + int fd = open(psx_tracker.pid_path, O_RDONLY | O_DIRECTORY); + if (fd == -1) { + psx_lock(); + fprintf(stderr, "failed to read %s - aborting\n", psx_tracker.pid_path); + kill(psx_tracker.pid, SIGKILL); } - /* - * need to remove invalid thread id from linked list - */ - psx_do_unregister(ref); - } - psx_unlock(); - int mismatch = 0; - for (;;) { - int waiting = 0; - psx_lock(); - for (ref = psx_tracker.root; ref; ref = next) { - next = ref->next; - if (ref->thread == self) { - continue; + for (;;) { + char buf[BUF_SIZE]; + size_t nread = syscall(SYS_getdents64, fd, buf, BUF_SIZE); + if (nread == 0) { + break; + } else if (nread < 0) { + perror("getdents64 failed"); + kill(psx_tracker.pid, SIGKILL); } - pthread_mutex_lock(&ref->mu); - int pending = ref->pending; - int gone = ref->gone; - if (!gone) { - if (pending) { - gone = (pthread_kill(ref->thread, 0) != 0); - } else { - mismatch |= (ref->retval != ret); + size_t offset; + unsigned short reclen; + for (offset = 0; offset < nread; offset += reclen) { + /* deal with potential unaligned reads */ + memcpy(&reclen, buf + offset + + offsetof(struct psx_linux_dirent64, d_reclen), + sizeof(reclen)); + char *dir = (buf + offset + + offsetof(struct psx_linux_dirent64, d_name)); + long tid = atoi(dir); + if (tid == 0 || tid == self) { + continue; } + long mix = psx_mix(tid); + long hval = mix & psx_tracker.map_mask; + psx_thread_ref_t *x = &psx_tracker.map[hval]; + if (x->tid != tid) { + if (x->tid != 0) { + /* a collision */ + long entries = psx_tracker.map_entries; + long oval; + for (oval = psx_mix(x->tid); ; entries <<= 1) { + long mask = entries - 1; + if (((oval ^ hval) & mask) != 0) { + /* no more collisions */ + break; + } + } + psx_thread_ref_t *old = psx_tracker.map; + long old_entries = psx_tracker.map_entries; + psx_lock(); + psx_set_map(entries); + long ok_sweep = sweep - 1; + for (i = 0; i < old_entries; i++) { + psx_thread_ref_t *y = &old[i]; + if (y->sweep < ok_sweep) { + /* no longer care about this entry */ + continue; + } + psx_thread_ref_t *z = &psx_tracker.map[psx_mix(y->tid) & psx_tracker.map_mask]; + z->tid = y->tid; + z->pending = y->pending; + z->retval = y->retval; + z->sweep = y->sweep; + } + psx_unlock(); + free(old); + x = &psx_tracker.map[mix & psx_tracker.map_mask]; + } + /* a new entry */ + x->pending = 1; + x->tid = tid; + syscall(SYS_tkill, tid, psx_tracker.psx_sig); + } + psx_lock(); + x->sweep = sweep; + incomplete++; + if (x->pending) { + some++; + } else if (x->retval != ret) { + mismatch = 1; + } + psx_unlock(); } - pthread_mutex_unlock(&ref->mu); - if (!gone) { - waiting += pending; - continue; - } - /* - * need to remove invalid thread id from linked list - */ - psx_do_unregister(ref); } - psx_unlock(); - if (!waiting) { - break; + close(fd); + if (some) { + verified = 0; + sched_yield(); + } else { + verified++; } - sched_yield(); - } + } while (verified < 2); + psx_lock(); + psx_tracker.incomplete = incomplete; psx_tracker.cmd.active = 0; + while (psx_tracker.incomplete != 0) { + psx_cond_wait(); + } + psx_unlock(); + if (mismatch) { psx_lock(); switch (psx_tracker.sensitivity) { @@ -699,20 +605,20 @@ long int __psx_syscall(long int syscall_nr, ...) { psx_tracker.cmd.arg3); } fprintf(stderr, "results:"); - for (ref = psx_tracker.root; ref; ref = next) { - next = ref->next; - if (ref->thread == self) { + for (i=0; i < psx_tracker.map_entries; i++) { + psx_thread_ref_t *ref = &psx_tracker.map[i]; + if (ref->sweep != sweep) { continue; } if (ret != ref->retval) { - fprintf(stderr, " %d={%ld}", ref->tid, ref->retval); + fprintf(stderr, " %ld={%ld}", ref->tid, ref->retval); } } fprintf(stderr, " wanted={%ld}\n", ret); if (psx_tracker.sensitivity == PSX_WARNING) { break; } - pthread_kill(self, SIGSYS); + kill(psx_tracker.pid, SIGKILL); } psx_unlock(); } @@ -723,31 +629,6 @@ long int __psx_syscall(long int syscall_nr, ...) { return ret; } -/* - * _psx_cleanup its called when the program exits. It is used to free - * any memory used by the thread tracker. - */ -static void _psx_cleanup(void) { - registered_thread_t *ref, *next; - - /* - * We enter the exiting state. Unlike exiting a single thread we - * never leave this state since this cleanup is only done at - * program exit. - */ - psx_lock(); - while (psx_tracker.state != _PSX_IDLE && psx_tracker.state != _PSX_INFORK) { - pthread_cond_wait(&psx_tracker.cond, &psx_tracker.state_mu); - } - psx_tracker.state = _PSX_EXITING; - psx_unlock(); - - for (ref = psx_tracker.root; ref; ref = next) { - next = ref->next; - psx_do_unregister(ref); - } -} - /* * Change the PSX sensitivity level. If the threads appear to have * diverged in behavior, this can cause the library to notify the @@ -763,3 +644,30 @@ int psx_set_sensitivity(psx_sensitivity_t level) { psx_unlock(); return 0; } + +#ifdef _LIBPSX_PTHREAD_LINKAGE + +/* + * psx requires this function to be provided by the linkage wrapping. + */ +extern int __real_pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg); + +/* + * forward declaration to keep the compiler happy. + */ +int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg); + +/* + * __wrap_pthread_create is defined for legacy reasons, since whether + * or not you use this wrapper to reach the __real_ functionality or + * not isn't important to the psx mechanism any longer (since + * libpsx-2.72). + */ +int __wrap_pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine) (void *), void *arg) { + return __real_pthread_create(thread, attr, start_routine, arg); +} + +#endif /* _LIBPSX_PTHREAD_LINKAGE def */ diff --git a/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx.go b/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx.go index 130f0cb3195f..0e3747294709 100644 --- a/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx.go +++ b/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx.go @@ -1,5 +1,5 @@ -// +build linux,!cgo -// +build go1.16 +//go:build linux && !cgo && go1.16 +// +build linux,!cgo,go1.16 package psx // import "kernel.org/pub/linux/libs/security/libcap/psx" diff --git a/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx_cgo.go b/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx_cgo.go index 1f7513736eae..e612433844ae 100644 --- a/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx_cgo.go +++ b/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx_cgo.go @@ -1,3 +1,4 @@ +//go:build linux && cgo // +build linux,cgo package psx // import "kernel.org/pub/linux/libs/security/libcap/psx" @@ -8,8 +9,6 @@ import ( "syscall" ) -// #cgo LDFLAGS: -lpthread -Wl,-wrap,pthread_create -// // #include // #include "psx_syscall.h" // @@ -46,14 +45,21 @@ func forceFatal() { // Syscall3 performs a 3 argument syscall. Syscall3 differs from // syscall.[Raw]Syscall() insofar as it is simultaneously executed on -// every thread of the combined Go and CGo runtimes. It works -// differently depending on whether CGO_ENABLED is 1 or 0 at compile -// time. +// every thread of the combined Go and CGo runtimes. If any of the +// return values of these sumultaneous system calls differs, the +// runtime will cause the program to exit. +// +// Syscall3 works differently depending on whether CGO_ENABLED is 1 or +// 0 at compile time. // -// If CGO_ENABLED=1 it uses the libpsx function C.psx_syscall3(). +// If CGO_ENABLED=1 it uses the libpsx function C.psx_syscall3(), with +// libpsx:psx_set_sensitivity(PSX_ERROR) - which means the program +// will be SIGSYS killed when inconsistent return values are returned +// for any pthread. // // If CGO_ENABLED=0 it redirects to the go1.16+ -// syscall.AllThreadsSyscall() function. +// syscall.AllThreadsSyscall() function. This function panics if +// inconsistent return values are returned for any pthread. func Syscall3(syscallnr, arg1, arg2, arg3 uintptr) (uintptr, uintptr, syscall.Errno) { forceFatal() // We lock to the OSThread here because we may need errno to diff --git a/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx_syscall.h b/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx_syscall.h index 7a8c9a19ea21..610471798c5f 100644 --- a/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx_syscall.h +++ b/vendor/kernel.org/pub/linux/libs/security/libcap/psx/psx_syscall.h @@ -1,12 +1,32 @@ /* - * Copyright (c) 2019 Andrew G. Morgan + * Copyright (c) 2019,24 Andrew G. Morgan * * This header, and the -lpsx library, provide a number of things to * support POSIX semantics for syscalls associated with the pthread - * library. Linking this code is tricky and is done as follows: + * library. For any code that references the psx_syscall{3,6} + * functions, linking this code is as follows: * - * ld ... -lpsx -lpthread --wrap=pthread_create - * or, gcc ... -lpsx -lpthread -Wl,-wrap,pthread_create + * ld ... -lpsx -lpthread + * or, + * gcc ... -lpsx -lpthread + * + * NOTE: linking with -lcap to make it silently aware of the psx + * mechanism is tricky. You have to envelope the -lpsx part with + * the following linker flags: + * + * ld ... --no-as-needed --whole-archive -lpsx --no-whole-archive --as-needed -lpthread + * or, + * gcc ... -Wl,--no-as-needed -Wl,--whole-archive -lpsx -Wl,--no-whole-archive -Wl,--as-needed -lpthread + * + * These options are provided in the + * https://en.wikipedia.org/wiki/Pkg-config libpsx.pc file + * distributed with the library. + * + * FYI Earlier versions of libpsx relied on gcc + * ... -Wl,--wrap=pthread_create linkage in all cases, but since + * libpsx-2.72 the library can work with non pthread threads (LWP) + * under Linux and such wrapping is no longer needed. That being + * said, for compatibility reasons such linking is still supported. * * glibc provides a subset of this functionality natively through the * nptl:setxid mechanism and could implement psx_syscall() directly @@ -25,6 +45,12 @@ extern "C" { #include +/* + * Programmatic way to recognize feature set. + */ +#define LIBPSX_MAJOR 2 +#define LIBPSX_MINOR 73 + /* * psx_syscall performs the specified syscall on all psx registered * threads. The mechanism by which this occurs is much less efficient diff --git a/vendor/modules.txt b/vendor/modules.txt index c77a78ac1c82..231d0bf2ee9a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1121,10 +1121,10 @@ google.golang.org/protobuf/types/pluginpb # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 -# kernel.org/pub/linux/libs/security/libcap/cap v1.2.70 +# kernel.org/pub/linux/libs/security/libcap/cap v1.2.73 ## explicit; go 1.11 kernel.org/pub/linux/libs/security/libcap/cap -# kernel.org/pub/linux/libs/security/libcap/psx v1.2.70 +# kernel.org/pub/linux/libs/security/libcap/psx v1.2.73 ## explicit; go 1.11 kernel.org/pub/linux/libs/security/libcap/psx # sigs.k8s.io/yaml v1.4.0