basic: port various pidfd/pidref helpers to PIDFD_GET_INFO and PIDFD_GET_*_NAMESPACE (#35242)
Supersedes #35308 (cherry-picked one commit and replaced the rest) (I left a few comments that's folded by GitHub. Please make sure to check them too.)
This commit is contained in:
@@ -35,6 +35,7 @@ REQUIREMENTS:
|
||||
≥ 4.9 for RENAME_NOREPLACE support in vfat
|
||||
≥ 4.10 for cgroup-bpf egress and ingress hooks
|
||||
≥ 4.11 for nsfs
|
||||
# FIXME: drop compat glue and remove entries above before v258
|
||||
≥ 4.15 for cgroup-bpf device hook and cpu controller in cgroup v2
|
||||
≥ 4.17 for cgroup-bpf socket address hooks and /sys/power/resume_offset
|
||||
≥ 4.20 for PSI (used by systemd-oomd)
|
||||
@@ -43,16 +44,17 @@ REQUIREMENTS:
|
||||
≥ 5.4 for pidfd, new mount API, and signed Verity images
|
||||
≥ 5.6 for getrandom() GRND_INSECURE
|
||||
≥ 5.7 for CLONE_INTO_CGROUP, BPF links and the BPF LSM hook
|
||||
≥ 5.9 for close_range()
|
||||
≥ 5.8 for LOOP_CONFIGURE and STATX_ATTR_MOUNT_ROOT
|
||||
≥ 5.9 for close_range()
|
||||
≥ 6.3 for MFD_EXEC/MFD_NOEXEC_SEAL and tmpfs noswap option
|
||||
≥ 6.5 for name_to_handle_at() AT_HANDLE_FID, SO_PEERPIDFD/SO_PASSPIDFD,
|
||||
and MOVE_MOUNT_BENEATH
|
||||
≥ 6.9 for pidfs
|
||||
|
||||
⛔ Kernel versions below 4.3 ("minimum baseline") are not supported at
|
||||
⛔ Kernel versions below 4.11 ("minimum baseline") are not supported at
|
||||
all, and are missing required functionality (e.g. CLOCK_BOOTTIME support
|
||||
for timerfd_create(), getrandom(), ambient capabilities, or memfd_create()).
|
||||
for timerfd_create(), getrandom(), ambient capabilities, memfd_create(),
|
||||
or nsfs (NS_GET_NSTYPE)).
|
||||
|
||||
⚠️ Kernel versions below 5.4 ("recommended baseline") have significant
|
||||
gaps in functionality and are not recommended for use with this version
|
||||
|
||||
@@ -144,9 +144,6 @@ Features:
|
||||
that terminal emulators can maybe connect a password manager or so, and
|
||||
highlight things specially.
|
||||
|
||||
* Port pidref_namespace_open() to use PIDFD_GET_MNT_NAMESPACE and related
|
||||
ioctls to get nsfds directly from pidfds.
|
||||
|
||||
* start using STATX_SUBVOL in btrfs_is_subvol(). Also, make use of it
|
||||
generically, so that image discovery recognizes bcachefs subvols too.
|
||||
|
||||
|
||||
@@ -68,6 +68,27 @@ int cg_cgroupid_open(int cgroupfs_fd, uint64_t id) {
|
||||
return RET_NERRNO(open_by_handle_at(cgroupfs_fd, &fh.file_handle, O_DIRECTORY|O_CLOEXEC));
|
||||
}
|
||||
|
||||
int cg_path_from_cgroupid(int cgroupfs_fd, uint64_t id, char **ret) {
|
||||
_cleanup_close_ int cgfd = -EBADF;
|
||||
int r;
|
||||
|
||||
cgfd = cg_cgroupid_open(cgroupfs_fd, id);
|
||||
if (cgfd < 0)
|
||||
return cgfd;
|
||||
|
||||
_cleanup_free_ char *path = NULL;
|
||||
r = fd_get_path(cgfd, &path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!path_startswith(path, "/sys/fs/cgroup/"))
|
||||
return -EXDEV; /* recognizable error */
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cg_get_cgroupid_at(int dfd, const char *path, uint64_t *ret) {
|
||||
cg_file_handle fh = CG_FILE_HANDLE_INIT;
|
||||
int mnt_id;
|
||||
@@ -842,6 +863,10 @@ int cg_pidref_get_path(const char *controller, const PidRef *pidref, char **ret_
|
||||
if (pidref_is_remote(pidref))
|
||||
return -EREMOTE;
|
||||
|
||||
// XXX: Ideally we'd use pidfd_get_cgroupid() + cg_path_from_cgroupid() here, to extract this
|
||||
// bit of information from pidfd directly. However, the latter requires privilege and it's
|
||||
// not entirely clear how to handle cgroups from outer namespace.
|
||||
|
||||
r = cg_pid_get_path(controller, pidref->pid, &path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -183,6 +183,7 @@ typedef enum CGroupUnified {
|
||||
int cg_path_open(const char *controller, const char *path);
|
||||
int cg_cgroupid_open(int fsfd, uint64_t id);
|
||||
|
||||
int cg_path_from_cgroupid(int cgroupfs_fd, uint64_t id, char **ret);
|
||||
int cg_get_cgroupid_at(int dfd, const char *path, uint64_t *ret);
|
||||
static inline int cg_path_get_cgroupid(const char *path, uint64_t *ret) {
|
||||
return cg_get_cgroupid_at(AT_FDCWD, path, ret);
|
||||
|
||||
@@ -103,7 +103,7 @@ int chattr_full(
|
||||
continue;
|
||||
|
||||
if (ioctl(fd, FS_IOC_SETFLAGS, &new_one) < 0) {
|
||||
if (errno != EINVAL && !ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
if (!ERRNO_IS_IOCTL_NOT_SUPPORTED(errno))
|
||||
return -errno;
|
||||
|
||||
log_full_errno(FLAGS_SET(flags, CHATTR_WARN_UNSUPPORTED_FLAGS) ? LOG_WARNING : LOG_DEBUG,
|
||||
|
||||
@@ -158,7 +158,7 @@ static inline bool ERRNO_IS_NEG_RESOURCE(intmax_t r) {
|
||||
}
|
||||
_DEFINE_ABS_WRAPPER(RESOURCE);
|
||||
|
||||
/* Seven different errors for "operation/system call/ioctl/socket feature not supported" */
|
||||
/* Seven different errors for "operation/system call/socket feature not supported" */
|
||||
static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
|
||||
return IN_SET(r,
|
||||
-EOPNOTSUPP,
|
||||
@@ -172,6 +172,12 @@ static inline bool ERRNO_IS_NEG_NOT_SUPPORTED(intmax_t r) {
|
||||
}
|
||||
_DEFINE_ABS_WRAPPER(NOT_SUPPORTED);
|
||||
|
||||
/* ioctl() with unsupported command/arg might additionally return EINVAL */
|
||||
static inline bool ERRNO_IS_NEG_IOCTL_NOT_SUPPORTED(intmax_t r) {
|
||||
return ERRNO_IS_NEG_NOT_SUPPORTED(r) || r == -EINVAL;
|
||||
}
|
||||
_DEFINE_ABS_WRAPPER(IOCTL_NOT_SUPPORTED);
|
||||
|
||||
/* Two different errors for access problems */
|
||||
static inline bool ERRNO_IS_NEG_PRIVILEGE(intmax_t r) {
|
||||
return IN_SET(r,
|
||||
|
||||
@@ -72,6 +72,7 @@ basic_sources = files(
|
||||
'parse-util.c',
|
||||
'path-util.c',
|
||||
'percent-util.c',
|
||||
'pidfd-util.c',
|
||||
'pidref.c',
|
||||
'prioq.c',
|
||||
'proc-cmdline.c',
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <linux/types.h>
|
||||
#if HAVE_PIDFD_OPEN
|
||||
#include <sys/pidfd.h>
|
||||
#endif
|
||||
|
||||
#ifndef PIDFS_IOCTL_MAGIC
|
||||
# define PIDFS_IOCTL_MAGIC 0xFF
|
||||
#endif
|
||||
|
||||
#ifndef PIDFD_GET_CGROUP_NAMESPACE
|
||||
# define PIDFD_GET_CGROUP_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 1)
|
||||
# define PIDFD_GET_IPC_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 2)
|
||||
# define PIDFD_GET_MNT_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 3)
|
||||
# define PIDFD_GET_NET_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 4)
|
||||
# define PIDFD_GET_PID_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 5)
|
||||
# define PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 6)
|
||||
# define PIDFD_GET_TIME_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 7)
|
||||
# define PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 8)
|
||||
# define PIDFD_GET_USER_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 9)
|
||||
# define PIDFD_GET_UTS_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 10)
|
||||
#endif
|
||||
|
||||
#ifndef PIDFD_GET_INFO
|
||||
struct pidfd_info {
|
||||
__u64 mask;
|
||||
__u64 cgroupid;
|
||||
__u32 pid;
|
||||
__u32 tgid;
|
||||
__u32 ppid;
|
||||
__u32 ruid;
|
||||
__u32 rgid;
|
||||
__u32 euid;
|
||||
__u32 egid;
|
||||
__u32 suid;
|
||||
__u32 sgid;
|
||||
__u32 fsuid;
|
||||
__u32 fsgid;
|
||||
__u32 spare0[1];
|
||||
};
|
||||
|
||||
#define PIDFD_GET_INFO _IOWR(PIDFS_IOCTL_MAGIC, 11, struct pidfd_info)
|
||||
#define PIDFD_INFO_PID (1UL << 0)
|
||||
#define PIDFD_INFO_CREDS (1UL << 1)
|
||||
#define PIDFD_INFO_CGROUPID (1UL << 2)
|
||||
#endif
|
||||
+203
-163
@@ -18,29 +18,30 @@
|
||||
#include "mountpoint-util.h"
|
||||
#include "namespace-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "process-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "user-util.h"
|
||||
|
||||
const struct namespace_info namespace_info[_NAMESPACE_TYPE_MAX + 1] = {
|
||||
[NAMESPACE_CGROUP] = { "cgroup", "ns/cgroup", CLONE_NEWCGROUP, PROC_CGROUP_INIT_INO },
|
||||
[NAMESPACE_IPC] = { "ipc", "ns/ipc", CLONE_NEWIPC, PROC_IPC_INIT_INO },
|
||||
[NAMESPACE_NET] = { "net", "ns/net", CLONE_NEWNET, 0 },
|
||||
[NAMESPACE_CGROUP] = { "cgroup", "ns/cgroup", CLONE_NEWCGROUP, PIDFD_GET_CGROUP_NAMESPACE, PROC_CGROUP_INIT_INO },
|
||||
[NAMESPACE_IPC] = { "ipc", "ns/ipc", CLONE_NEWIPC, PIDFD_GET_IPC_NAMESPACE, PROC_IPC_INIT_INO },
|
||||
[NAMESPACE_NET] = { "net", "ns/net", CLONE_NEWNET, PIDFD_GET_NET_NAMESPACE, 0 },
|
||||
/* So, the mount namespace flag is called CLONE_NEWNS for historical
|
||||
* reasons. Let's expose it here under a more explanatory name: "mnt".
|
||||
* This is in-line with how the kernel exposes namespaces in /proc/$PID/ns. */
|
||||
[NAMESPACE_MOUNT] = { "mnt", "ns/mnt", CLONE_NEWNS, 0 },
|
||||
[NAMESPACE_PID] = { "pid", "ns/pid", CLONE_NEWPID, PROC_PID_INIT_INO },
|
||||
[NAMESPACE_USER] = { "user", "ns/user", CLONE_NEWUSER, PROC_USER_INIT_INO },
|
||||
[NAMESPACE_UTS] = { "uts", "ns/uts", CLONE_NEWUTS, PROC_UTS_INIT_INO },
|
||||
[NAMESPACE_TIME] = { "time", "ns/time", CLONE_NEWTIME, PROC_TIME_INIT_INO },
|
||||
{ /* Allow callers to iterate over the array without using _NAMESPACE_TYPE_MAX. */ },
|
||||
[NAMESPACE_MOUNT] = { "mnt", "ns/mnt", CLONE_NEWNS, PIDFD_GET_MNT_NAMESPACE, 0 },
|
||||
[NAMESPACE_PID] = { "pid", "ns/pid", CLONE_NEWPID, PIDFD_GET_PID_NAMESPACE, PROC_PID_INIT_INO },
|
||||
[NAMESPACE_USER] = { "user", "ns/user", CLONE_NEWUSER, PIDFD_GET_USER_NAMESPACE, PROC_USER_INIT_INO },
|
||||
[NAMESPACE_UTS] = { "uts", "ns/uts", CLONE_NEWUTS, PIDFD_GET_UTS_NAMESPACE, PROC_UTS_INIT_INO },
|
||||
[NAMESPACE_TIME] = { "time", "ns/time", CLONE_NEWTIME, PIDFD_GET_TIME_NAMESPACE, PROC_TIME_INIT_INO },
|
||||
{}, /* Allow callers to iterate over the array without using _NAMESPACE_TYPE_MAX. */
|
||||
};
|
||||
|
||||
#define pid_namespace_path(pid, type) procfs_file_alloca(pid, namespace_info[type].proc_path)
|
||||
|
||||
static NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag) {
|
||||
NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag) {
|
||||
for (NamespaceType t = 0; t < _NAMESPACE_TYPE_MAX; t++)
|
||||
if (((namespace_info[t].clone_flag ^ clone_flag) & (CLONE_NEWCGROUP|CLONE_NEWIPC|CLONE_NEWNET|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUSER|CLONE_NEWUTS|CLONE_NEWTIME)) == 0)
|
||||
return t;
|
||||
@@ -48,6 +49,62 @@ static NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag) {
|
||||
return _NAMESPACE_TYPE_INVALID;
|
||||
}
|
||||
|
||||
static int pidref_namespace_open_by_type_internal(const PidRef *pidref, NamespaceType type, bool *need_verify) {
|
||||
int r;
|
||||
|
||||
assert(pidref_is_set(pidref));
|
||||
assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
if (pidref_is_remote(pidref))
|
||||
return -EREMOTE;
|
||||
|
||||
if (pidref->fd >= 0) {
|
||||
r = pidfd_get_namespace(pidref->fd, namespace_info[type].pidfd_get_ns_ioctl_cmd);
|
||||
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
return r;
|
||||
}
|
||||
|
||||
if (need_verify) /* The caller shall call pidref_verify() later */
|
||||
*need_verify = true;
|
||||
|
||||
_cleanup_close_ int nsfd = -EBADF;
|
||||
const char *p;
|
||||
|
||||
p = pid_namespace_path(pidref->pid, type);
|
||||
nsfd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
if (nsfd < 0) {
|
||||
if (errno == ENOENT && proc_mounted() == 0)
|
||||
return -ENOSYS;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (!need_verify) { /* Otherwise we verify on our own */
|
||||
r = pidref_verify(pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return TAKE_FD(nsfd);
|
||||
}
|
||||
|
||||
int pidref_namespace_open_by_type(const PidRef *pidref, NamespaceType type) {
|
||||
return pidref_namespace_open_by_type_internal(pidref, type, NULL);
|
||||
}
|
||||
|
||||
int namespace_open_by_type(NamespaceType type) {
|
||||
_cleanup_(pidref_done) PidRef self = PIDREF_NULL;
|
||||
int r;
|
||||
|
||||
assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
r = pidref_set_self(&self);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return pidref_namespace_open_by_type(&self, type);
|
||||
}
|
||||
|
||||
int pidref_namespace_open(
|
||||
const PidRef *pidref,
|
||||
int *ret_pidns_fd,
|
||||
@@ -58,58 +115,56 @@ int pidref_namespace_open(
|
||||
|
||||
_cleanup_close_ int pidns_fd = -EBADF, mntns_fd = -EBADF, netns_fd = -EBADF,
|
||||
userns_fd = -EBADF, root_fd = -EBADF;
|
||||
bool need_verify = false;
|
||||
int r;
|
||||
|
||||
assert(pidref_is_set(pidref));
|
||||
|
||||
if (ret_pidns_fd) {
|
||||
const char *pidns;
|
||||
if (pidref_is_remote(pidref))
|
||||
return -EREMOTE;
|
||||
|
||||
pidns = pid_namespace_path(pidref->pid, NAMESPACE_PID);
|
||||
pidns_fd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
if (ret_pidns_fd) {
|
||||
pidns_fd = pidref_namespace_open_by_type_internal(pidref, NAMESPACE_PID, &need_verify);
|
||||
if (pidns_fd < 0)
|
||||
return -errno;
|
||||
return pidns_fd;
|
||||
}
|
||||
|
||||
if (ret_mntns_fd) {
|
||||
const char *mntns;
|
||||
|
||||
mntns = pid_namespace_path(pidref->pid, NAMESPACE_MOUNT);
|
||||
mntns_fd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
mntns_fd = pidref_namespace_open_by_type_internal(pidref, NAMESPACE_MOUNT, &need_verify);
|
||||
if (mntns_fd < 0)
|
||||
return -errno;
|
||||
return mntns_fd;
|
||||
}
|
||||
|
||||
if (ret_netns_fd) {
|
||||
const char *netns;
|
||||
|
||||
netns = pid_namespace_path(pidref->pid, NAMESPACE_NET);
|
||||
netns_fd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
netns_fd = pidref_namespace_open_by_type_internal(pidref, NAMESPACE_NET, &need_verify);
|
||||
if (netns_fd < 0)
|
||||
return -errno;
|
||||
return netns_fd;
|
||||
}
|
||||
|
||||
if (ret_userns_fd) {
|
||||
const char *userns;
|
||||
|
||||
userns = pid_namespace_path(pidref->pid, NAMESPACE_USER);
|
||||
userns_fd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||
if (userns_fd < 0 && errno != ENOENT)
|
||||
return -errno;
|
||||
userns_fd = pidref_namespace_open_by_type_internal(pidref, NAMESPACE_USER, &need_verify);
|
||||
if (userns_fd < 0 && !IN_SET(userns_fd, -ENOENT, -ENOPKG))
|
||||
return userns_fd;
|
||||
}
|
||||
|
||||
if (ret_root_fd) {
|
||||
const char *root;
|
||||
|
||||
root = procfs_file_alloca(pidref->pid, "root");
|
||||
root_fd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
|
||||
root_fd = RET_NERRNO(open(root, O_CLOEXEC|O_DIRECTORY));
|
||||
if (root_fd == -ENOENT && proc_mounted() == 0)
|
||||
return -ENOSYS;
|
||||
if (root_fd < 0)
|
||||
return -errno;
|
||||
return root_fd;
|
||||
|
||||
need_verify = true;
|
||||
}
|
||||
|
||||
r = pidref_verify(pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (need_verify) {
|
||||
r = pidref_verify(pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ret_pidns_fd)
|
||||
*ret_pidns_fd = TAKE_FD(pidns_fd);
|
||||
@@ -154,10 +209,10 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
|
||||
/* Can't setns to your own userns, since then you could escalate from non-root to root in
|
||||
* your own namespace, so check if namespaces are equal before attempting to enter. */
|
||||
|
||||
r = inode_same_at(userns_fd, "", AT_FDCWD, "/proc/self/ns/user", AT_EMPTY_PATH);
|
||||
r = is_our_namespace(userns_fd, NAMESPACE_USER);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r)
|
||||
if (r > 0)
|
||||
userns_fd = -EBADF;
|
||||
}
|
||||
|
||||
@@ -188,50 +243,128 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
|
||||
return reset_uid_gid();
|
||||
}
|
||||
|
||||
int fd_is_ns(int fd, unsigned long nsflag) {
|
||||
struct statfs s;
|
||||
int fd_is_namespace(int fd, NamespaceType type) {
|
||||
int r;
|
||||
|
||||
/* Checks whether the specified file descriptor refers to a namespace created by specifying nsflag in clone().
|
||||
* On old kernels there's no nice way to detect that, hence on those we'll return a recognizable error (EUCLEAN),
|
||||
* so that callers can handle this somewhat nicely.
|
||||
*
|
||||
* This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not
|
||||
* refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */
|
||||
/* Checks whether the specified file descriptor refers to a namespace (of type if type != _NAMESPACE_INVALID). */
|
||||
|
||||
if (fstatfs(fd, &s) < 0)
|
||||
assert(fd >= 0);
|
||||
assert(type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
r = fd_is_fs_type(fd, NSFS_MAGIC);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (type < 0)
|
||||
return true;
|
||||
|
||||
int clone_flag = ioctl(fd, NS_GET_NSTYPE);
|
||||
if (clone_flag < 0)
|
||||
return -errno;
|
||||
|
||||
if (!is_fs_type(&s, NSFS_MAGIC)) {
|
||||
/* On really old kernels, there was no "nsfs", and network namespace sockets belonged to procfs
|
||||
* instead. Handle that in a somewhat smart way. */
|
||||
NamespaceType found_type = clone_flag_to_namespace_type(clone_flag);
|
||||
if (found_type < 0)
|
||||
return -EBADF; /* Uh? Unknown namespace type? */
|
||||
|
||||
if (is_fs_type(&s, PROC_SUPER_MAGIC)) {
|
||||
struct statfs t;
|
||||
return found_type == type;
|
||||
}
|
||||
|
||||
/* OK, so it is procfs. Let's see if our own network namespace is procfs, too. If so, then the
|
||||
* passed fd might refer to a network namespace, but we can't know for sure. In that case,
|
||||
* return a recognizable error. */
|
||||
int is_our_namespace(int fd, NamespaceType type) {
|
||||
int r;
|
||||
|
||||
if (statfs("/proc/self/ns/net", &t) < 0)
|
||||
return -errno;
|
||||
assert(fd >= 0);
|
||||
assert(type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
if (s.f_type == t.f_type)
|
||||
return -EUCLEAN; /* It's possible, we simply don't know */
|
||||
r = fd_is_namespace(fd, type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* Not a namespace or not of the right type? */
|
||||
return -EUCLEAN;
|
||||
|
||||
_cleanup_close_ int our_ns = namespace_open_by_type(type);
|
||||
if (our_ns < 0)
|
||||
return our_ns;
|
||||
|
||||
return fd_inode_same(fd, our_ns);
|
||||
}
|
||||
|
||||
int namespace_is_init(NamespaceType type) {
|
||||
int r;
|
||||
|
||||
assert(type >= 0);
|
||||
assert(type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
if (namespace_info[type].root_inode == 0)
|
||||
return -EBADR; /* Cannot answer this question */
|
||||
|
||||
const char *p = pid_namespace_path(0, type);
|
||||
|
||||
struct stat st;
|
||||
r = RET_NERRNO(stat(p, &st));
|
||||
if (r == -ENOENT)
|
||||
/* If the /proc/ns/<type> API is not around in /proc/ then ns is off in the kernel and we are in the init ns */
|
||||
return proc_mounted() == 0 ? -ENOSYS : true;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return st.st_ino == namespace_info[type].root_inode;
|
||||
}
|
||||
|
||||
int pidref_in_same_namespace(PidRef *pid1, PidRef *pid2, NamespaceType type) {
|
||||
_cleanup_close_ int ns1 = -EBADF, ns2 = -EBADF;
|
||||
|
||||
/* Accepts NULL to indicate our own process */
|
||||
|
||||
assert(!pid1 || pidref_is_set(pid1));
|
||||
assert(!pid2 || pidref_is_set(pid2));
|
||||
assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
if (pidref_equal(pid1, pid2))
|
||||
return true;
|
||||
|
||||
if (!pid1)
|
||||
ns1 = namespace_open_by_type(type);
|
||||
else
|
||||
ns1 = pidref_namespace_open_by_type(pid1, type);
|
||||
if (ns1 < 0)
|
||||
return ns1;
|
||||
|
||||
if (!pid2)
|
||||
ns2 = namespace_open_by_type(type);
|
||||
else
|
||||
ns2 = pidref_namespace_open_by_type(pid2, type);
|
||||
if (ns2 < 0)
|
||||
return ns2;
|
||||
|
||||
return fd_inode_same(ns1, ns2);
|
||||
}
|
||||
|
||||
int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(pid >= 0);
|
||||
assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
|
||||
assert(ret);
|
||||
|
||||
for (;;) {
|
||||
pid_t ppid;
|
||||
|
||||
r = pid_get_ppid(pid, &ppid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = in_same_namespace(pid, ppid, type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
/* If the parent and the child are not in the same namespace, then the child is
|
||||
* the leader we are looking for. */
|
||||
*ret = pid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0; /* No! */
|
||||
pid = ppid;
|
||||
}
|
||||
|
||||
r = ioctl(fd, NS_GET_NSTYPE);
|
||||
if (r < 0) {
|
||||
if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */
|
||||
return -EUCLEAN;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return (unsigned long) r == nsflag;
|
||||
}
|
||||
|
||||
int detach_mount_namespace(void) {
|
||||
@@ -402,30 +535,6 @@ int netns_acquire(void) {
|
||||
return TAKE_FD(netns_fd);
|
||||
}
|
||||
|
||||
int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type) {
|
||||
const char *ns_path;
|
||||
struct stat ns_st1, ns_st2;
|
||||
|
||||
if (pid1 == 0)
|
||||
pid1 = getpid_cached();
|
||||
|
||||
if (pid2 == 0)
|
||||
pid2 = getpid_cached();
|
||||
|
||||
if (pid1 == pid2)
|
||||
return 1;
|
||||
|
||||
ns_path = pid_namespace_path(pid1, type);
|
||||
if (stat(ns_path, &ns_st1) < 0)
|
||||
return -errno;
|
||||
|
||||
ns_path = pid_namespace_path(pid2, type);
|
||||
if (stat(ns_path, &ns_st2) < 0)
|
||||
return -errno;
|
||||
|
||||
return stat_inode_same(&ns_st1, &ns_st2);
|
||||
}
|
||||
|
||||
int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range) {
|
||||
_cleanup_free_ char *buffer = NULL;
|
||||
const char *range, *shift;
|
||||
@@ -464,75 +573,6 @@ int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_r
|
||||
return 0;
|
||||
}
|
||||
|
||||
int namespace_open_by_type(NamespaceType type) {
|
||||
const char *p;
|
||||
int fd;
|
||||
|
||||
assert(type >= 0);
|
||||
assert(type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
p = pid_namespace_path(0, type);
|
||||
|
||||
fd = RET_NERRNO(open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC));
|
||||
if (fd == -ENOENT && proc_mounted() == 0)
|
||||
return -ENOSYS;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int namespace_is_init(NamespaceType type) {
|
||||
int r;
|
||||
|
||||
assert(type >= 0);
|
||||
assert(type <= _NAMESPACE_TYPE_MAX);
|
||||
|
||||
if (namespace_info[type].root_inode == 0)
|
||||
return -EBADR; /* Cannot answer this question */
|
||||
|
||||
const char *p = pid_namespace_path(0, type);
|
||||
|
||||
struct stat st;
|
||||
r = RET_NERRNO(stat(p, &st));
|
||||
if (r == -ENOENT)
|
||||
/* If the /proc/ns/<type> API is not around in /proc/ then ns is off in the kernel and we are in the init ns */
|
||||
return proc_mounted() == 0 ? -ENOSYS : true;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return st.st_ino == namespace_info[type].root_inode;
|
||||
}
|
||||
|
||||
int is_our_namespace(int fd, NamespaceType request_type) {
|
||||
int clone_flag;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
clone_flag = ioctl(fd, NS_GET_NSTYPE);
|
||||
if (clone_flag < 0)
|
||||
return -errno;
|
||||
|
||||
NamespaceType found_type = clone_flag_to_namespace_type(clone_flag);
|
||||
if (found_type < 0)
|
||||
return -EBADF; /* Uh? Unknown namespace type? */
|
||||
|
||||
if (request_type >= 0 && request_type != found_type) /* It's a namespace, but not of the right type? */
|
||||
return -EUCLEAN;
|
||||
|
||||
struct stat st_fd, st_ours;
|
||||
if (fstat(fd, &st_fd) < 0)
|
||||
return -errno;
|
||||
|
||||
const char *p = pid_namespace_path(0, found_type);
|
||||
if (stat(p, &st_ours) < 0) {
|
||||
if (errno == ENOENT)
|
||||
return proc_mounted() == 0 ? -ENOSYS : -ENOENT;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return stat_inode_same(&st_ours, &st_fd);
|
||||
}
|
||||
|
||||
int is_idmapping_supported(const char *path) {
|
||||
_cleanup_close_ int mount_fd = -EBADF, userns_fd = -EBADF, dir_fd = -EBADF;
|
||||
_cleanup_free_ char *uid_map = NULL, *gid_map = NULL;
|
||||
|
||||
+25
-14
@@ -3,11 +3,9 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef enum NamespaceType NamespaceType;
|
||||
|
||||
#include "pidref.h"
|
||||
|
||||
enum NamespaceType {
|
||||
typedef enum NamespaceType {
|
||||
NAMESPACE_CGROUP,
|
||||
NAMESPACE_IPC,
|
||||
NAMESPACE_NET,
|
||||
@@ -18,15 +16,21 @@ enum NamespaceType {
|
||||
NAMESPACE_TIME,
|
||||
_NAMESPACE_TYPE_MAX,
|
||||
_NAMESPACE_TYPE_INVALID = -EINVAL,
|
||||
};
|
||||
} NamespaceType;
|
||||
|
||||
extern const struct namespace_info {
|
||||
const char *proc_name;
|
||||
const char *proc_path;
|
||||
unsigned int clone_flag;
|
||||
unsigned long clone_flag;
|
||||
unsigned long pidfd_get_ns_ioctl_cmd;
|
||||
ino_t root_inode;
|
||||
} namespace_info[_NAMESPACE_TYPE_MAX + 1];
|
||||
|
||||
NamespaceType clone_flag_to_namespace_type(unsigned long clone_flag);
|
||||
|
||||
int pidref_namespace_open_by_type(const PidRef *pidref, NamespaceType type);
|
||||
int namespace_open_by_type(NamespaceType type);
|
||||
|
||||
int pidref_namespace_open(
|
||||
const PidRef *pidref,
|
||||
int *ret_pidns_fd,
|
||||
@@ -41,9 +45,24 @@ int namespace_open(
|
||||
int *ret_netns_fd,
|
||||
int *ret_userns_fd,
|
||||
int *ret_root_fd);
|
||||
|
||||
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
|
||||
|
||||
int fd_is_ns(int fd, unsigned long nsflag);
|
||||
int fd_is_namespace(int fd, NamespaceType type);
|
||||
int is_our_namespace(int fd, NamespaceType type);
|
||||
|
||||
int namespace_is_init(NamespaceType type);
|
||||
|
||||
int pidref_in_same_namespace(PidRef *pid1, PidRef *pid2, NamespaceType type);
|
||||
static inline int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type) {
|
||||
assert(pid1 >= 0);
|
||||
assert(pid2 >= 0);
|
||||
return pidref_in_same_namespace(pid1 == 0 ? NULL : &PIDREF_MAKE_FROM_PID(pid1),
|
||||
pid2 == 0 ? NULL : &PIDREF_MAKE_FROM_PID(pid2),
|
||||
type);
|
||||
}
|
||||
|
||||
int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret);
|
||||
|
||||
int detach_mount_namespace(void);
|
||||
int detach_mount_namespace_harder(uid_t target_uid, gid_t target_gid);
|
||||
@@ -69,14 +88,6 @@ int userns_acquire(const char *uid_map, const char *gid_map);
|
||||
|
||||
int netns_acquire(void);
|
||||
|
||||
int in_same_namespace(pid_t pid1, pid_t pid2, NamespaceType type);
|
||||
|
||||
int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_range);
|
||||
|
||||
int namespace_open_by_type(NamespaceType type);
|
||||
|
||||
int namespace_is_init(NamespaceType type);
|
||||
|
||||
int is_our_namespace(int fd, NamespaceType type);
|
||||
|
||||
int is_idmapping_supported(const char *path);
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
#include "missing_magic.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "process-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
static int have_pidfs = -1;
|
||||
|
||||
static int pidfd_check_pidfs(void) {
|
||||
|
||||
if (have_pidfs >= 0)
|
||||
return have_pidfs;
|
||||
|
||||
_cleanup_close_ int fd = pidfd_open(getpid_cached(), 0);
|
||||
if (fd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
return (have_pidfs = false);
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return (have_pidfs = fd_is_fs_type(fd, PID_FS_MAGIC));
|
||||
}
|
||||
|
||||
int pidfd_get_namespace(int fd, unsigned long ns_type_cmd) {
|
||||
static bool cached_supported = true;
|
||||
|
||||
/* Obtain the namespace fd from pidfd directly through ioctl(PIDFD_GET_*_NAMESPACE).
|
||||
*
|
||||
* Returns -EOPNOTSUPP if ioctl on pidfds are not supported, -ENOPKG if the requested namespace
|
||||
* is disabled in kernel. (The errno used are different from what kernel returns via ioctl(),
|
||||
* see below) */
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
/* If we know ahead of time that pidfs is unavailable, shortcut things. But otherwise we don't
|
||||
* call pidfd_check_pidfs() here, which is kinda extraneous and our own cache is required
|
||||
* anyways (pidfs is introduced in kernel 6.9 while ioctl support there is added in 6.11). */
|
||||
if (have_pidfs == 0 || !cached_supported)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
int nsfd = ioctl(fd, ns_type_cmd);
|
||||
if (nsfd < 0) {
|
||||
/* Kernel returns EOPNOTSUPP if the ns type in question is disabled. Hence we need to look
|
||||
* at precise errno instead of generic ERRNO_IS_(IOCTL_)NOT_SUPPORTED. */
|
||||
if (IN_SET(errno, ENOTTY, EINVAL)) {
|
||||
cached_supported = false;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (errno == EOPNOTSUPP) /* Translate to something more recognizable */
|
||||
return -ENOPKG;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return nsfd;
|
||||
}
|
||||
|
||||
static int pidfd_get_info(int fd, struct pidfd_info *info) {
|
||||
static bool cached_supported = true;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(info);
|
||||
|
||||
if (have_pidfs == 0 || !cached_supported)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (ioctl(fd, PIDFD_GET_INFO, info) < 0) {
|
||||
if (ERRNO_IS_IOCTL_NOT_SUPPORTED(errno)) {
|
||||
cached_supported = false;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pidfd_get_pid_fdinfo(int fd, pid_t *ret) {
|
||||
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_free_ char *fdinfo = NULL;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
xsprintf(path, "/proc/self/fdinfo/%i", fd);
|
||||
|
||||
r = read_full_virtual_file(path, &fdinfo, NULL);
|
||||
if (r == -ENOENT)
|
||||
return proc_fd_enoent_errno();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
char *p = find_line_startswith(fdinfo, "Pid:");
|
||||
if (!p)
|
||||
return -ENOTTY; /* not a pidfd? */
|
||||
|
||||
p = skip_leading_chars(p, /* bad = */ NULL);
|
||||
p[strcspn(p, WHITESPACE)] = 0;
|
||||
|
||||
if (streq(p, "0"))
|
||||
return -EREMOTE; /* PID is in foreign PID namespace? */
|
||||
if (streq(p, "-1"))
|
||||
return -ESRCH; /* refers to reaped process? */
|
||||
|
||||
return parse_pid(p, ret);
|
||||
}
|
||||
|
||||
static int pidfd_get_pid_ioctl(int fd, pid_t *ret) {
|
||||
struct pidfd_info info = { .mask = PIDFD_INFO_PID };
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = pidfd_get_info(fd, &info);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
|
||||
|
||||
if (ret)
|
||||
*ret = info.pid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidfd_get_pid(int fd, pid_t *ret) {
|
||||
int r;
|
||||
|
||||
/* Converts a pidfd into a pid. We try ioctl(PIDFD_GET_INFO) (kernel 6.13+) first,
|
||||
* /proc/self/fdinfo/ as fallback. Well known errors:
|
||||
*
|
||||
* -EBADF → fd invalid
|
||||
* -ESRCH → fd valid, but process is already reaped
|
||||
*
|
||||
* pidfd_get_pid_fdinfo() might additionally fail for other reasons:
|
||||
*
|
||||
* -ENOSYS → /proc/ not mounted
|
||||
* -ENOTTY → fd valid, but not a pidfd
|
||||
* -EREMOTE → fd valid, but pid is in another namespace we cannot translate to the local one
|
||||
* (when using PIDFD_GET_INFO this is indistinguishable from -ESRCH)
|
||||
*/
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = pidfd_get_pid_ioctl(fd, ret);
|
||||
if (r != -EOPNOTSUPP)
|
||||
return r;
|
||||
|
||||
return pidfd_get_pid_fdinfo(fd, ret);
|
||||
}
|
||||
|
||||
int pidfd_verify_pid(int pidfd, pid_t pid) {
|
||||
pid_t current_pid;
|
||||
int r;
|
||||
|
||||
assert(pidfd >= 0);
|
||||
assert(pid > 0);
|
||||
|
||||
r = pidfd_get_pid(pidfd, ¤t_pid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return current_pid != pid ? -ESRCH : 0;
|
||||
}
|
||||
|
||||
int pidfd_get_ppid(int fd, pid_t *ret) {
|
||||
struct pidfd_info info = { .mask = PIDFD_INFO_PID };
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = pidfd_get_info(fd, &info);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
|
||||
|
||||
if (info.ppid == 0) /* See comments in pid_get_ppid() */
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
if (ret)
|
||||
*ret = info.ppid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidfd_get_uid(int fd, uid_t *ret) {
|
||||
struct pidfd_info info = { .mask = PIDFD_INFO_CREDS };
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = pidfd_get_info(fd, &info);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(FLAGS_SET(info.mask, PIDFD_INFO_CREDS));
|
||||
|
||||
if (ret)
|
||||
*ret = info.ruid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidfd_get_cgroupid(int fd, uint64_t *ret) {
|
||||
struct pidfd_info info = { .mask = PIDFD_INFO_CGROUPID };
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = pidfd_get_info(fd, &info);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(FLAGS_SET(info.mask, PIDFD_INFO_CGROUPID));
|
||||
|
||||
if (ret)
|
||||
*ret = info.cgroupid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidfd_get_inode_id(int fd, uint64_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
r = pidfd_check_pidfs();
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (ret)
|
||||
*ret = (uint64_t) st.st_ino;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "missing_pidfd.h"
|
||||
#include "missing_syscall.h"
|
||||
|
||||
int pidfd_get_namespace(int fd, unsigned long ns_type_cmd);
|
||||
|
||||
int pidfd_get_pid(int fd, pid_t *ret);
|
||||
int pidfd_verify_pid(int pidfd, pid_t pid);
|
||||
|
||||
int pidfd_get_ppid(int fd, pid_t *ret);
|
||||
int pidfd_get_uid(int fd, uid_t *ret);
|
||||
int pidfd_get_cgroupid(int fd, uint64_t *ret);
|
||||
|
||||
int pidfd_get_inode_id(int fd, uint64_t *ret);
|
||||
+7
-34
@@ -1,36 +1,14 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#if HAVE_PIDFD_OPEN
|
||||
#include <sys/pidfd.h>
|
||||
#endif
|
||||
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "missing_magic.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "missing_wait.h"
|
||||
#include "parse-util.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "pidref.h"
|
||||
#include "process-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "stat-util.h"
|
||||
|
||||
static int pidfd_inode_ids_supported(void) {
|
||||
static int cached = -1;
|
||||
|
||||
if (cached >= 0)
|
||||
return cached;
|
||||
|
||||
_cleanup_close_ int fd = pidfd_open(getpid_cached(), 0);
|
||||
if (fd < 0) {
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno))
|
||||
return (cached = false);
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return (cached = fd_is_fs_type(fd, PID_FS_MAGIC));
|
||||
}
|
||||
|
||||
int pidref_acquire_pidfd_id(PidRef *pidref) {
|
||||
int r;
|
||||
@@ -49,19 +27,14 @@ int pidref_acquire_pidfd_id(PidRef *pidref) {
|
||||
if (pidref->fd_id > 0)
|
||||
return 0;
|
||||
|
||||
r = pidfd_inode_ids_supported();
|
||||
if (r < 0)
|
||||
r = pidfd_get_inode_id(pidref->fd, &pidref->fd_id);
|
||||
if (r < 0) {
|
||||
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
log_debug_errno(r, "Failed to get inode number of pidfd for pid " PID_FMT ": %m",
|
||||
pidref->pid);
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
|
||||
if (fstat(pidref->fd, &st) < 0)
|
||||
return log_debug_errno(errno, "Failed to get inode number of pidfd for pid " PID_FMT ": %m",
|
||||
pidref->pid);
|
||||
|
||||
pidref->fd_id = (uint64_t) st.st_ino;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+58
-119
@@ -48,6 +48,7 @@
|
||||
#include "nulstr-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "process-util.h"
|
||||
#include "raw-clone.h"
|
||||
#include "rlimit-util.h"
|
||||
@@ -395,33 +396,6 @@ int container_get_leader(const char *machine, pid_t *pid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
for (;;) {
|
||||
pid_t ppid;
|
||||
|
||||
r = get_process_ppid(pid, &ppid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = in_same_namespace(pid, ppid, type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
/* If the parent and the child are not in the same
|
||||
* namespace, then the child is the leader we are
|
||||
* looking for. */
|
||||
*ret = pid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pid = ppid;
|
||||
}
|
||||
}
|
||||
|
||||
int pid_is_kernel_thread(pid_t pid) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
unsigned long long flags;
|
||||
@@ -584,7 +558,6 @@ int pid_get_uid(pid_t pid, uid_t *ret) {
|
||||
}
|
||||
|
||||
int pidref_get_uid(const PidRef *pid, uid_t *ret) {
|
||||
uid_t uid;
|
||||
int r;
|
||||
|
||||
if (!pidref_is_set(pid))
|
||||
@@ -593,6 +566,13 @@ int pidref_get_uid(const PidRef *pid, uid_t *ret) {
|
||||
if (pidref_is_remote(pid))
|
||||
return -EREMOTE;
|
||||
|
||||
if (pid->fd >= 0) {
|
||||
r = pidfd_get_uid(pid->fd, ret);
|
||||
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
return r;
|
||||
}
|
||||
|
||||
uid_t uid;
|
||||
r = pid_get_uid(pid->pid, &uid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -678,7 +658,7 @@ int get_process_environ(pid_t pid, char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_process_ppid(pid_t pid, pid_t *ret) {
|
||||
int pid_get_ppid(pid_t pid, pid_t *ret) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
unsigned long ppid;
|
||||
const char *p;
|
||||
@@ -733,6 +713,35 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidref_get_ppid(const PidRef *pidref, pid_t *ret) {
|
||||
int r;
|
||||
|
||||
if (!pidref_is_set(pidref))
|
||||
return -ESRCH;
|
||||
|
||||
if (pidref_is_remote(pidref))
|
||||
return -EREMOTE;
|
||||
|
||||
if (pidref->fd >= 0) {
|
||||
r = pidfd_get_ppid(pidref->fd, ret);
|
||||
if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
return r;
|
||||
}
|
||||
|
||||
pid_t ppid;
|
||||
r = pid_get_ppid(pidref->pid, ret ? &ppid : NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = pidref_verify(pidref);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = ppid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pid_get_start_time(pid_t pid, usec_t *ret) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
const char *p;
|
||||
@@ -1073,25 +1082,8 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pid_is_my_child(pid_t pid) {
|
||||
pid_t ppid;
|
||||
int r;
|
||||
|
||||
if (pid < 0)
|
||||
return -ESRCH;
|
||||
|
||||
if (pid <= 1)
|
||||
return false;
|
||||
|
||||
r = get_process_ppid(pid, &ppid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return ppid == getpid_cached();
|
||||
}
|
||||
|
||||
int pidref_is_my_child(const PidRef *pid) {
|
||||
int r, result;
|
||||
int r;
|
||||
|
||||
if (!pidref_is_set(pid))
|
||||
return -ESRCH;
|
||||
@@ -1099,38 +1091,30 @@ int pidref_is_my_child(const PidRef *pid) {
|
||||
if (pidref_is_remote(pid))
|
||||
return -EREMOTE;
|
||||
|
||||
result = pid_is_my_child(pid->pid);
|
||||
if (result < 0)
|
||||
return result;
|
||||
if (pid->pid == 1 || pidref_is_self(pid))
|
||||
return false;
|
||||
|
||||
r = pidref_verify(pid);
|
||||
pid_t ppid;
|
||||
r = pidref_get_ppid(pid, &ppid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return result;
|
||||
return ppid == getpid_cached();
|
||||
}
|
||||
|
||||
int pid_is_unwaited(pid_t pid) {
|
||||
/* Checks whether a PID is still valid at all, including a zombie */
|
||||
int pid_is_my_child(pid_t pid) {
|
||||
|
||||
if (pid < 0)
|
||||
return -ESRCH;
|
||||
if (pid == 0)
|
||||
return false;
|
||||
|
||||
if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */
|
||||
return true;
|
||||
|
||||
if (pid == getpid_cached())
|
||||
return true;
|
||||
|
||||
if (kill(pid, 0) >= 0)
|
||||
return true;
|
||||
|
||||
return errno != ESRCH;
|
||||
return pidref_is_my_child(&PIDREF_MAKE_FROM_PID(pid));
|
||||
}
|
||||
|
||||
int pidref_is_unwaited(const PidRef *pid) {
|
||||
int r;
|
||||
|
||||
/* Checks whether a PID is still valid at all, including a zombie */
|
||||
|
||||
if (!pidref_is_set(pid))
|
||||
return -ESRCH;
|
||||
|
||||
@@ -1149,6 +1133,14 @@ int pidref_is_unwaited(const PidRef *pid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int pid_is_unwaited(pid_t pid) {
|
||||
|
||||
if (pid == 0)
|
||||
return true;
|
||||
|
||||
return pidref_is_unwaited(&PIDREF_MAKE_FROM_PID(pid));
|
||||
}
|
||||
|
||||
int pid_is_alive(pid_t pid) {
|
||||
int r;
|
||||
|
||||
@@ -1859,59 +1851,6 @@ int get_oom_score_adjust(int *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pidfd_get_pid(int fd, pid_t *ret) {
|
||||
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_free_ char *fdinfo = NULL;
|
||||
int r;
|
||||
|
||||
/* Converts a pidfd into a pid. Well known errors:
|
||||
*
|
||||
* -EBADF → fd invalid
|
||||
* -ENOSYS → /proc/ not mounted
|
||||
* -ENOTTY → fd valid, but not a pidfd
|
||||
* -EREMOTE → fd valid, but pid is in another namespace we cannot translate to the local one
|
||||
* -ESRCH → fd valid, but process is already reaped
|
||||
*/
|
||||
|
||||
assert(fd >= 0);
|
||||
|
||||
xsprintf(path, "/proc/self/fdinfo/%i", fd);
|
||||
|
||||
r = read_full_virtual_file(path, &fdinfo, NULL);
|
||||
if (r == -ENOENT)
|
||||
return proc_fd_enoent_errno();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
char *p = find_line_startswith(fdinfo, "Pid:");
|
||||
if (!p)
|
||||
return -ENOTTY; /* not a pidfd? */
|
||||
|
||||
p = skip_leading_chars(p, /* bad = */ NULL);
|
||||
p[strcspn(p, WHITESPACE)] = 0;
|
||||
|
||||
if (streq(p, "0"))
|
||||
return -EREMOTE; /* PID is in foreign PID namespace? */
|
||||
if (streq(p, "-1"))
|
||||
return -ESRCH; /* refers to reaped process? */
|
||||
|
||||
return parse_pid(p, ret);
|
||||
}
|
||||
|
||||
int pidfd_verify_pid(int pidfd, pid_t pid) {
|
||||
pid_t current_pid;
|
||||
int r;
|
||||
|
||||
assert(pidfd >= 0);
|
||||
assert(pid > 0);
|
||||
|
||||
r = pidfd_get_pid(pidfd, ¤t_pid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return current_pid != pid ? -ESRCH : 0;
|
||||
}
|
||||
|
||||
static int rlimit_to_nice(rlim_t limit) {
|
||||
if (limit <= 1)
|
||||
return PRIO_MAX-1; /* i.e. 19 */
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "alloc-util.h"
|
||||
#include "format-util.h"
|
||||
#include "macro.h"
|
||||
#include "namespace-util.h"
|
||||
#include "pidref.h"
|
||||
#include "time-util.h"
|
||||
|
||||
@@ -53,15 +52,14 @@ int get_process_gid(pid_t pid, gid_t *ret);
|
||||
int get_process_cwd(pid_t pid, char **ret);
|
||||
int get_process_root(pid_t pid, char **ret);
|
||||
int get_process_environ(pid_t pid, char **ret);
|
||||
int get_process_ppid(pid_t pid, pid_t *ret);
|
||||
int pid_get_ppid(pid_t pid, pid_t *ret);
|
||||
int pidref_get_ppid(const PidRef *pidref, pid_t *ret);
|
||||
int pid_get_start_time(pid_t pid, usec_t *ret);
|
||||
int pidref_get_start_time(const PidRef* pid, usec_t *ret);
|
||||
int pidref_get_start_time(const PidRef *pid, usec_t *ret);
|
||||
int get_process_umask(pid_t pid, mode_t *ret);
|
||||
|
||||
int container_get_leader(const char *machine, pid_t *pid);
|
||||
|
||||
int namespace_get_leader(pid_t pid, NamespaceType type, pid_t *ret);
|
||||
|
||||
int wait_for_terminate(pid_t pid, siginfo_t *status);
|
||||
|
||||
typedef enum WaitFlags {
|
||||
@@ -250,9 +248,6 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
|
||||
/* Like TAKE_PTR() but for pid_t, resetting them to 0 */
|
||||
#define TAKE_PID(pid) TAKE_GENERIC(pid, pid_t, 0)
|
||||
|
||||
int pidfd_get_pid(int fd, pid_t *ret);
|
||||
int pidfd_verify_pid(int pidfd, pid_t pid);
|
||||
|
||||
int setpriority_closest(int priority);
|
||||
|
||||
_noreturn_ void freeze(void);
|
||||
|
||||
@@ -2334,7 +2334,7 @@ int pty_open_peer_racefree(int fd, int mode) {
|
||||
if (peer_fd >= 0)
|
||||
return peer_fd;
|
||||
|
||||
if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EINVAL) /* new ioctl() is not supported, return a clear error */
|
||||
if (ERRNO_IS_IOCTL_NOT_SUPPORTED(errno)) /* new ioctl() is not supported, return a clear error */
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (errno != EIO)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "fileio.h"
|
||||
#include "macro.h"
|
||||
#include "missing_threads.h"
|
||||
#include "namespace-util.h"
|
||||
#include "process-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-table.h"
|
||||
|
||||
@@ -3253,6 +3253,7 @@ int setup_shareable_ns(int ns_storage_socket[static 2], unsigned long nsflag) {
|
||||
|
||||
int open_shareable_ns_path(int ns_storage_socket[static 2], const char *path, unsigned long nsflag) {
|
||||
_cleanup_close_ int ns = -EBADF;
|
||||
NamespaceType type;
|
||||
int r;
|
||||
|
||||
assert(ns_storage_socket);
|
||||
@@ -3264,6 +3265,9 @@ int open_shareable_ns_path(int ns_storage_socket[static 2], const char *path, un
|
||||
* it. This is supposed to be called ahead of time, i.e. before setup_shareable_ns() which will
|
||||
* allocate a new anonymous ns if needed. */
|
||||
|
||||
type = clone_flag_to_namespace_type(nsflag);
|
||||
assert(type >= 0);
|
||||
|
||||
r = posix_lock(ns_storage_socket[0], LOCK_EX);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -3282,11 +3286,11 @@ int open_shareable_ns_path(int ns_storage_socket[static 2], const char *path, un
|
||||
if (ns < 0)
|
||||
return -errno;
|
||||
|
||||
r = fd_is_ns(ns, nsflag);
|
||||
r = fd_is_namespace(ns, type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EINVAL;
|
||||
if (r < 0 && r != -EUCLEAN) /* EUCLEAN: we don't know */
|
||||
return r;
|
||||
|
||||
r = send_one_fd(ns_storage_socket[1], ns, MSG_DONTWAIT);
|
||||
if (r < 0)
|
||||
|
||||
+35
-28
@@ -31,6 +31,7 @@
|
||||
#include "mkdir-label.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "process-util.h"
|
||||
#include "recurse-dir.h"
|
||||
#include "selinux-util.h"
|
||||
@@ -828,13 +829,13 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
cgroup_context_dump(UNIT(s), f, prefix);
|
||||
}
|
||||
|
||||
static int instance_from_socket(int fd, unsigned nr, char **instance) {
|
||||
socklen_t l;
|
||||
char *r;
|
||||
static int instance_from_socket(int fd, unsigned nr, char **ret) {
|
||||
union sockaddr_union local, remote;
|
||||
socklen_t l;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(instance);
|
||||
assert(ret);
|
||||
|
||||
l = sizeof(local);
|
||||
if (getsockname(fd, &local.sa, &l) < 0)
|
||||
@@ -844,6 +845,8 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
|
||||
if (getpeername(fd, &remote.sa, &l) < 0)
|
||||
return -errno;
|
||||
|
||||
char *s;
|
||||
|
||||
switch (local.sa.sa_family) {
|
||||
|
||||
case AF_INET: {
|
||||
@@ -851,7 +854,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
|
||||
a = be32toh(local.in.sin_addr.s_addr),
|
||||
b = be32toh(remote.in.sin_addr.s_addr);
|
||||
|
||||
if (asprintf(&r,
|
||||
if (asprintf(&s,
|
||||
"%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
|
||||
nr,
|
||||
a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
|
||||
@@ -874,7 +877,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
|
||||
*a = local.in6.sin6_addr.s6_addr+12,
|
||||
*b = remote.in6.sin6_addr.s6_addr+12;
|
||||
|
||||
if (asprintf(&r,
|
||||
if (asprintf(&s,
|
||||
"%u-%u.%u.%u.%u:%u-%u.%u.%u.%u:%u",
|
||||
nr,
|
||||
a[0], a[1], a[2], a[3],
|
||||
@@ -883,7 +886,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
|
||||
be16toh(remote.in6.sin6_port)) < 0)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
if (asprintf(&r,
|
||||
if (asprintf(&s,
|
||||
"%u-%s:%u-%s:%u",
|
||||
nr,
|
||||
IN6_ADDR_TO_STRING(&local.in6.sin6_addr),
|
||||
@@ -898,30 +901,35 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
|
||||
|
||||
case AF_UNIX: {
|
||||
struct ucred ucred;
|
||||
int k;
|
||||
|
||||
k = getpeercred(fd, &ucred);
|
||||
if (k >= 0) {
|
||||
if (asprintf(&r,
|
||||
"%u-"PID_FMT"-"UID_FMT,
|
||||
nr, ucred.pid, ucred.uid) < 0)
|
||||
r = getpeercred(fd, &ucred);
|
||||
if (r >= 0) {
|
||||
_cleanup_close_ int pidfd = getpeerpidfd(fd);
|
||||
uint64_t pidfd_id;
|
||||
|
||||
if (pidfd >= 0 && pidfd_get_inode_id(pidfd, &pidfd_id) >= 0)
|
||||
r = asprintf(&s, "%u-" PID_FMT "_%" PRIu64 "-" UID_FMT,
|
||||
nr, ucred.pid, pidfd_id, ucred.uid);
|
||||
else
|
||||
r = asprintf(&s, "%u-" PID_FMT "-" UID_FMT,
|
||||
nr, ucred.pid, ucred.uid);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
} else if (k == -ENODATA) {
|
||||
/* This handles the case where somebody is
|
||||
* connecting from another pid/uid namespace
|
||||
} else if (r == -ENODATA) {
|
||||
/* This handles the case where somebody is connecting from another pid/uid namespace
|
||||
* (e.g. from outside of our container). */
|
||||
if (asprintf(&r,
|
||||
if (asprintf(&s,
|
||||
"%u-unknown",
|
||||
nr) < 0)
|
||||
return -ENOMEM;
|
||||
} else
|
||||
return k;
|
||||
return r;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AF_VSOCK:
|
||||
if (asprintf(&r,
|
||||
if (asprintf(&s,
|
||||
"%u-%u:%u-%u:%u",
|
||||
nr,
|
||||
local.vm.svm_cid, local.vm.svm_port,
|
||||
@@ -934,7 +942,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
*instance = r;
|
||||
*ret = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1395,15 +1403,14 @@ int socket_load_service_unit(Socket *s, int cfd, Unit **ret) {
|
||||
|
||||
if (cfd >= 0) {
|
||||
r = instance_from_socket(cfd, s->n_accepted, &instance);
|
||||
if (r < 0) {
|
||||
if (ERRNO_IS_DISCONNECT(r))
|
||||
/* ENOTCONN is legitimate if TCP RST was received. Other socket families might return
|
||||
* different errors. This connection is over, but the socket unit lives on. */
|
||||
return log_unit_debug_errno(UNIT(s), r,
|
||||
"Got %s on incoming socket, assuming aborted connection attempt, ignoring.",
|
||||
errno_to_name(r));
|
||||
if (ERRNO_IS_NEG_DISCONNECT(r))
|
||||
/* ENOTCONN is legitimate if TCP RST was received. Other socket families might return
|
||||
* different errors. This connection is over, but the socket unit lives on. */
|
||||
return log_unit_debug_errno(UNIT(s), r,
|
||||
"Got %s on incoming socket, assuming aborted connection attempt, ignoring.",
|
||||
errno_to_name(r));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
/* For accepting sockets, we don't know how the instance will be called until we get a connection and
|
||||
|
||||
@@ -1761,7 +1761,7 @@ static int acquire_pid_mount_tree_fd(const Context *context, int *ret_fd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_kernel(int argc, char* argv[]) {
|
||||
static int process_kernel(int argc, char *argv[]) {
|
||||
_cleanup_(iovw_free_freep) struct iovec_wrapper *iovw = NULL;
|
||||
_cleanup_(context_done) Context context = CONTEXT_NULL;
|
||||
int r;
|
||||
@@ -1799,7 +1799,7 @@ static int process_kernel(int argc, char* argv[]) {
|
||||
context.meta[META_ARGV_UID], context.meta[META_ARGV_SIGNAL],
|
||||
signal_to_string(context.signo));
|
||||
|
||||
r = in_same_namespace(getpid_cached(), context.pidref.pid, NAMESPACE_PID);
|
||||
r = pidref_in_same_namespace(/* pid1 = */ NULL, &context.pidref, NAMESPACE_PID);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to check pidns of crashing process, ignoring: %m");
|
||||
if (r == 0) {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <sys/epoll.h>
|
||||
#if HAVE_PIDFD_OPEN
|
||||
#include <sys/pidfd.h>
|
||||
#endif
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
@@ -31,6 +28,7 @@
|
||||
#include "origin-id.h"
|
||||
#include "path-util.h"
|
||||
#include "prioq.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "process-util.h"
|
||||
#include "psi-util.h"
|
||||
#include "set.h"
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "macro.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pidfd-util.h"
|
||||
#include "process-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "stdio-util.h"
|
||||
|
||||
@@ -19,7 +19,7 @@ int varlink_get_peer_pidref(sd_varlink *v, PidRef *ret) {
|
||||
|
||||
int pidfd = sd_varlink_get_peer_pidfd(v);
|
||||
if (pidfd < 0) {
|
||||
if (!ERRNO_IS_NEG_NOT_SUPPORTED(pidfd) && pidfd != -EINVAL)
|
||||
if (!ERRNO_IS_NEG_IOCTL_NOT_SUPPORTED(pidfd))
|
||||
return pidfd;
|
||||
|
||||
pid_t pid;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "fileio.h"
|
||||
#include "iovec-util.h"
|
||||
#include "machined.h"
|
||||
#include "namespace-util.h"
|
||||
#include "process-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "strv.h"
|
||||
@@ -182,7 +183,7 @@ void manager_enqueue_gc(Manager *m) {
|
||||
(void) sd_event_source_set_description(m->deferred_gc_event_source, "deferred-gc");
|
||||
}
|
||||
|
||||
int machine_get_addresses(Machine* machine, struct local_address **ret_addresses) {
|
||||
int machine_get_addresses(Machine *machine, struct local_address **ret_addresses) {
|
||||
assert(machine);
|
||||
assert(ret_addresses);
|
||||
|
||||
@@ -206,7 +207,7 @@ int machine_get_addresses(Machine* machine, struct local_address **ret_addresses
|
||||
pid_t child;
|
||||
int r;
|
||||
|
||||
r = in_same_namespace(/* pid1 = */ 0, machine->leader.pid, NAMESPACE_NET);
|
||||
r = pidref_in_same_namespace(/* pid1 = */ NULL, &machine->leader, NAMESPACE_NET);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to check if container has private network: %m");
|
||||
if (r > 0)
|
||||
|
||||
@@ -227,7 +227,7 @@ static int validate_userns(sd_varlink *link, int *userns_fd) {
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "User namespace file descriptor has unsafe flags set: %m");
|
||||
|
||||
r = fd_is_ns(*userns_fd, CLONE_NEWUSER);
|
||||
r = fd_is_namespace(*userns_fd, NAMESPACE_USER);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
|
||||
+3
-5
@@ -5244,12 +5244,10 @@ static int run_container(
|
||||
if (child_netns_fd < 0)
|
||||
return log_error_errno(errno, "Cannot open file %s: %m", arg_network_namespace_path);
|
||||
|
||||
r = fd_is_ns(child_netns_fd, CLONE_NEWNET);
|
||||
if (r == -EUCLEAN)
|
||||
log_debug_errno(r, "Cannot determine if passed network namespace path '%s' really refers to a network namespace, assuming it does.", arg_network_namespace_path);
|
||||
else if (r < 0)
|
||||
r = fd_is_namespace(child_netns_fd, NAMESPACE_NET);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to check %s fs type: %m", arg_network_namespace_path);
|
||||
else if (r == 0)
|
||||
if (r == 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Path %s doesn't refer to a network namespace, refusing.", arg_network_namespace_path);
|
||||
}
|
||||
|
||||
@@ -666,7 +666,7 @@ static int validate_userns(sd_varlink *link, int userns_fd) {
|
||||
return log_debug_errno(r, "User namespace file descriptor has unsafe flags set: %m");
|
||||
|
||||
/* Validate this is actually a valid user namespace fd */
|
||||
r = fd_is_ns(userns_fd, CLONE_NEWUSER);
|
||||
r = fd_is_namespace(userns_fd, NAMESPACE_USER);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to check if user namespace fd is actually a user namespace: %m");
|
||||
if (r == 0)
|
||||
@@ -1455,7 +1455,7 @@ static int validate_netns(sd_varlink *link, int userns_fd, int netns_fd) {
|
||||
return log_debug_errno(r, "Network namespace file descriptor has unsafe flags set: %m");
|
||||
|
||||
/* Validate this is actually a valid network namespace fd */
|
||||
r = fd_is_ns(netns_fd, CLONE_NEWNET);
|
||||
r = fd_is_namespace(netns_fd, NAMESPACE_NET);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
|
||||
@@ -280,7 +280,7 @@ int userns_restrict_put_by_fd(
|
||||
assert(userns_fd >= 0);
|
||||
assert(n_mount_fds == 0 || mount_fds);
|
||||
|
||||
r = fd_is_ns(userns_fd, CLONE_NEWUSER);
|
||||
r = fd_is_namespace(userns_fd, NAMESPACE_USER);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to determine if file descriptor is user namespace: %m");
|
||||
if (r == 0)
|
||||
|
||||
@@ -44,8 +44,7 @@ static int fs_make_very_read_only(int fd) {
|
||||
r = btrfs_subvol_set_read_only_fd(fd, true);
|
||||
if (r >= 0)
|
||||
return 0;
|
||||
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(r) && r != -EINVAL)
|
||||
if (!ERRNO_IS_NEG_IOCTL_NOT_SUPPORTED(r))
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@@ -312,11 +312,8 @@ static int loop_configure(
|
||||
|
||||
if (!loop_configure_broken) {
|
||||
if (ioctl(fd, LOOP_CONFIGURE, c) < 0) {
|
||||
/* Do fallback only if LOOP_CONFIGURE is not supported, propagate all other
|
||||
* errors. Note that the kernel is weird: non-existing ioctls currently return EINVAL
|
||||
* rather than ENOTTY on loopback block devices. They should fix that in the kernel,
|
||||
* but in the meantime we accept both here. */
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EINVAL)
|
||||
/* Do fallback only if LOOP_CONFIGURE is not supported, propagate all other errors. */
|
||||
if (!ERRNO_IS_IOCTL_NOT_SUPPORTED(errno))
|
||||
return log_device_debug_errno(dev, errno, "ioctl(LOOP_CONFIGURE) failed: %m");
|
||||
|
||||
loop_configure_broken = true;
|
||||
|
||||
@@ -1097,7 +1097,7 @@ static int mount_in_namespace(
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to retrieve FDs of the target process' namespace: %m");
|
||||
|
||||
r = inode_same_at(mntns_fd, "", AT_FDCWD, "/proc/self/ns/mnt", AT_EMPTY_PATH);
|
||||
r = is_our_namespace(mntns_fd, NAMESPACE_MOUNT);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to determine if mount namespaces are equal: %m");
|
||||
/* We can't add new mounts at runtime if the process wasn't started in a namespace */
|
||||
|
||||
@@ -164,6 +164,31 @@ TEST(ipcns) {
|
||||
test_shareable_ns(CLONE_NEWIPC);
|
||||
}
|
||||
|
||||
TEST(fd_is_namespace) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
|
||||
ASSERT_OK_ZERO(fd_is_namespace(STDIN_FILENO, NAMESPACE_NET));
|
||||
ASSERT_OK_ZERO(fd_is_namespace(STDOUT_FILENO, NAMESPACE_NET));
|
||||
ASSERT_OK_ZERO(fd_is_namespace(STDERR_FILENO, NAMESPACE_NET));
|
||||
|
||||
fd = namespace_open_by_type(NAMESPACE_MOUNT);
|
||||
if (IN_SET(fd, -ENOSYS, -ENOENT)) {
|
||||
log_notice("Path %s not found, skipping test", "/proc/self/ns/mnt");
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(fd);
|
||||
ASSERT_OK_POSITIVE(fd_is_namespace(fd, NAMESPACE_MOUNT));
|
||||
ASSERT_OK_ZERO(fd_is_namespace(fd, NAMESPACE_NET));
|
||||
fd = safe_close(fd);
|
||||
|
||||
ASSERT_OK(fd = namespace_open_by_type(NAMESPACE_IPC));
|
||||
ASSERT_OK_POSITIVE(fd_is_namespace(fd, NAMESPACE_IPC));
|
||||
fd = safe_close(fd);
|
||||
|
||||
ASSERT_OK(fd = namespace_open_by_type(NAMESPACE_NET));
|
||||
ASSERT_OK_POSITIVE(fd_is_namespace(fd, NAMESPACE_NET));
|
||||
}
|
||||
|
||||
TEST(protect_kernel_logs) {
|
||||
static const NamespaceParameters p = {
|
||||
.runtime_scope = RUNTIME_SCOPE_SYSTEM,
|
||||
|
||||
@@ -69,7 +69,7 @@ static void test_pid_get_comm_one(pid_t pid) {
|
||||
ASSERT_OK(pid_get_cmdline(pid, 1, 0, &d));
|
||||
log_info("PID"PID_FMT" cmdline truncated to 1: '%s'", pid, d);
|
||||
|
||||
r = get_process_ppid(pid, &e);
|
||||
r = pid_get_ppid(pid, &e);
|
||||
if (pid == 1)
|
||||
ASSERT_ERROR(r, EADDRNOTAVAIL);
|
||||
else
|
||||
@@ -813,18 +813,18 @@ TEST(setpriority_closest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(get_process_ppid) {
|
||||
TEST(pid_get_ppid) {
|
||||
uint64_t limit;
|
||||
int r;
|
||||
|
||||
ASSERT_ERROR(get_process_ppid(1, NULL), EADDRNOTAVAIL);
|
||||
ASSERT_ERROR(pid_get_ppid(1, NULL), EADDRNOTAVAIL);
|
||||
|
||||
/* the process with the PID above the global limit definitely doesn't exist. Verify that */
|
||||
ASSERT_OK(procfs_get_pid_max(&limit));
|
||||
log_debug("kernel.pid_max = %"PRIu64, limit);
|
||||
|
||||
if (limit < INT_MAX) {
|
||||
r = get_process_ppid(limit + 1, NULL);
|
||||
r = pid_get_ppid(limit + 1, NULL);
|
||||
log_debug_errno(r, "get_process_limit(%"PRIu64") → %d/%m", limit + 1, r);
|
||||
assert(r == -ESRCH);
|
||||
}
|
||||
@@ -833,7 +833,7 @@ TEST(get_process_ppid) {
|
||||
_cleanup_free_ char *c1 = NULL, *c2 = NULL;
|
||||
pid_t ppid;
|
||||
|
||||
r = get_process_ppid(pid, &ppid);
|
||||
r = pid_get_ppid(pid, &ppid);
|
||||
if (r == -EADDRNOTAVAIL) {
|
||||
log_info("No further parent PID");
|
||||
break;
|
||||
|
||||
@@ -165,31 +165,6 @@ TEST(path_is_read_only_fs) {
|
||||
assert_se(path_is_read_only_fs("/i-dont-exist") == -ENOENT);
|
||||
}
|
||||
|
||||
TEST(fd_is_ns) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
|
||||
assert_se(fd_is_ns(STDIN_FILENO, CLONE_NEWNET) == 0);
|
||||
assert_se(fd_is_ns(STDERR_FILENO, CLONE_NEWNET) == 0);
|
||||
assert_se(fd_is_ns(STDOUT_FILENO, CLONE_NEWNET) == 0);
|
||||
|
||||
fd = open("/proc/self/ns/mnt", O_CLOEXEC|O_RDONLY);
|
||||
if (fd < 0) {
|
||||
assert_se(errno == ENOENT);
|
||||
log_notice("Path %s not found, skipping test", "/proc/self/ns/mnt");
|
||||
return;
|
||||
}
|
||||
assert_se(fd >= 0);
|
||||
assert_se(IN_SET(fd_is_ns(fd, CLONE_NEWNET), 0, -EUCLEAN));
|
||||
fd = safe_close(fd);
|
||||
|
||||
assert_se((fd = open("/proc/self/ns/ipc", O_CLOEXEC|O_RDONLY)) >= 0);
|
||||
assert_se(IN_SET(fd_is_ns(fd, CLONE_NEWIPC), 1, -EUCLEAN));
|
||||
fd = safe_close(fd);
|
||||
|
||||
assert_se((fd = open("/proc/self/ns/net", O_CLOEXEC|O_RDONLY)) >= 0);
|
||||
assert_se(IN_SET(fd_is_ns(fd, CLONE_NEWNET), 1, -EUCLEAN));
|
||||
}
|
||||
|
||||
TEST(dir_is_empty) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *empty_dir = NULL;
|
||||
_cleanup_free_ char *j = NULL, *jj = NULL, *jjj = NULL;
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "macro.h"
|
||||
#include "main-func.h"
|
||||
#include "mkdir.h"
|
||||
#include "namespace-util.h"
|
||||
#include "netif-util.h"
|
||||
#include "pager.h"
|
||||
#include "parse-argument.h"
|
||||
|
||||
Reference in New Issue
Block a user