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:
Lennart Poettering
2025-01-06 11:23:08 +01:00
committed by GitHub
35 changed files with 748 additions and 433 deletions
+5 -3
View File
@@ -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
-3
View File
@@ -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.
+25
View File
@@ -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;
+1
View File
@@ -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);
+1 -1
View File
@@ -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,
+7 -1
View File
@@ -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,
+1
View File
@@ -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',
+48
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+251
View File
@@ -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, &current_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;
}
+19
View File
@@ -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
View File
@@ -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
View File
@@ -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, &current_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 */
+3 -8
View File
@@ -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);
+1 -1
View File
@@ -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)
+1
View File
@@ -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"
+7 -3
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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 -3
View File
@@ -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"
+1
View File
@@ -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"
+1 -1
View File
@@ -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;
+3 -2
View File
@@ -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)
+1 -1
View File
@@ -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
View File
@@ -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);
}
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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)
+1 -2
View File
@@ -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;
}
+2 -5
View File
@@ -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;
+1 -1
View File
@@ -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 */
+25
View File
@@ -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,
+5 -5
View File
@@ -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;
-25
View File
@@ -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;
+1
View File
@@ -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"