Unverified Commit f08d0c3a authored by Lorenzo Stoakes's avatar Lorenzo Stoakes Committed by Christian Brauner
Browse files

pidfd: add PIDFD_SELF* sentinels to refer to own thread/process



It is useful to be able to utilise the pidfd mechanism to reference the
current thread or process (from a userland point of view - thread group
leader from the kernel's point of view).

Therefore introduce PIDFD_SELF_THREAD to refer to the current thread, and
PIDFD_SELF_THREAD_GROUP to refer to the current thread group leader.

For convenience and to avoid confusion from userland's perspective we alias
these:

* PIDFD_SELF is an alias for PIDFD_SELF_THREAD - This is nearly always what
  the user will want to use, as they would find it surprising if for
  instance fd's were unshared()'d and they wanted to invoke pidfd_getfd()
  and that failed.

* PIDFD_SELF_PROCESS is an alias for PIDFD_SELF_THREAD_GROUP - Most users
  have no concept of thread groups or what a thread group leader is, and
  from userland's perspective and nomenclature this is what userland
  considers to be a process.

We adjust pidfd_get_task() and the pidfd_send_signal() system call with
specific handling for this, implementing this functionality for
process_madvise(), process_mrelease() (albeit, using it here wouldn't
really make sense) and pidfd_send_signal().

Signed-off-by: default avatarLorenzo Stoakes <lorenzo.stoakes@oracle.com>
Link: https://lore.kernel.org/r/24315a16a3d01a548dd45c7515f7d51c767e954e.1738268370.git.lorenzo.stoakes@oracle.com


Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 2014c95a
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -23,6 +23,30 @@

#define PIDFD_INFO_SIZE_VER0		64 /* sizeof first published struct */

/*
 * The concept of process and threads in userland and the kernel is a confusing
 * one - within the kernel every thread is a 'task' with its own individual PID,
 * however from userland's point of view threads are grouped by a single PID,
 * which is that of the 'thread group leader', typically the first thread
 * spawned.
 *
 * To cut the Gideon knot, for internal kernel usage, we refer to
 * PIDFD_SELF_THREAD to refer to the current thread (or task from a kernel
 * perspective), and PIDFD_SELF_THREAD_GROUP to refer to the current thread
 * group leader...
 */
#define PIDFD_SELF_THREAD		-10000 /* Current thread. */
#define PIDFD_SELF_THREAD_GROUP		-20000 /* Current thread group leader. */

/*
 * ...and for userland we make life simpler - PIDFD_SELF refers to the current
 * thread, PIDFD_SELF_PROCESS refers to the process thread group leader.
 *
 * For nearly all practical uses, a user will want to use PIDFD_SELF.
 */
#define PIDFD_SELF		PIDFD_SELF_THREAD
#define PIDFD_SELF_PROCESS	PIDFD_SELF_THREAD_GROUP

struct pidfd_info {
	/*
	 * This mask is similar to the request_mask in statx(2).
+19 −5
Original line number Diff line number Diff line
@@ -564,15 +564,29 @@ struct pid *pidfd_get_pid(unsigned int fd, unsigned int *flags)
 */
struct task_struct *pidfd_get_task(int pidfd, unsigned int *flags)
{
	unsigned int f_flags;
	unsigned int f_flags = 0;
	struct pid *pid;
	struct task_struct *task;
	enum pid_type type;

	switch (pidfd) {
	case  PIDFD_SELF_THREAD:
		type = PIDTYPE_PID;
		pid = get_task_pid(current, type);
		break;
	case  PIDFD_SELF_THREAD_GROUP:
		type = PIDTYPE_TGID;
		pid = get_task_pid(current, type);
		break;
	default:
		pid = pidfd_get_pid(pidfd, &f_flags);
		if (IS_ERR(pid))
			return ERR_CAST(pid);
		type = PIDTYPE_TGID;
		break;
	}

	task = get_pid_task(pid, PIDTYPE_TGID);
	task = get_pid_task(pid, type);
	put_pid(pid);
	if (!task)
		return ERR_PTR(-ESRCH);
+63 −42
Original line number Diff line number Diff line
@@ -4009,6 +4009,47 @@ static struct pid *pidfd_to_pid(const struct file *file)
	(PIDFD_SIGNAL_THREAD | PIDFD_SIGNAL_THREAD_GROUP | \
	 PIDFD_SIGNAL_PROCESS_GROUP)

static int do_pidfd_send_signal(struct pid *pid, int sig, enum pid_type type,
				siginfo_t __user *info, unsigned int flags)
{
	kernel_siginfo_t kinfo;

	switch (flags) {
	case PIDFD_SIGNAL_THREAD:
		type = PIDTYPE_PID;
		break;
	case PIDFD_SIGNAL_THREAD_GROUP:
		type = PIDTYPE_TGID;
		break;
	case PIDFD_SIGNAL_PROCESS_GROUP:
		type = PIDTYPE_PGID;
		break;
	}

	if (info) {
		int ret;

		ret = copy_siginfo_from_user_any(&kinfo, info);
		if (unlikely(ret))
			return ret;

		if (unlikely(sig != kinfo.si_signo))
			return -EINVAL;

		/* Only allow sending arbitrary signals to yourself. */
		if ((task_pid(current) != pid || type > PIDTYPE_TGID) &&
		    (kinfo.si_code >= 0 || kinfo.si_code == SI_TKILL))
			return -EPERM;
	} else {
		prepare_kill_siginfo(sig, &kinfo, type);
	}

	if (type == PIDTYPE_PGID)
		return kill_pgrp_info(sig, &kinfo, pid);

	return kill_pid_info_type(sig, &kinfo, pid, type);
}

/**
 * sys_pidfd_send_signal - Signal a process through a pidfd
 * @pidfd:  file descriptor of the process
@@ -4026,9 +4067,7 @@ static struct pid *pidfd_to_pid(const struct file *file)
SYSCALL_DEFINE4(pidfd_send_signal, int, pidfd, int, sig,
		siginfo_t __user *, info, unsigned int, flags)
{
	int ret;
	struct pid *pid;
	kernel_siginfo_t kinfo;
	enum pid_type type;

	/* Enforce flags be set to 0 until we add an extension. */
@@ -4039,6 +4078,16 @@ SYSCALL_DEFINE4(pidfd_send_signal, int, pidfd, int, sig,
	if (hweight32(flags & PIDFD_SEND_SIGNAL_FLAGS) > 1)
		return -EINVAL;

	switch (pidfd) {
	case PIDFD_SELF_THREAD:
		pid = get_task_pid(current, PIDTYPE_PID);
		type = PIDTYPE_PID;
		break;
	case PIDFD_SELF_THREAD_GROUP:
		pid = get_task_pid(current, PIDTYPE_TGID);
		type = PIDTYPE_TGID;
		break;
	default: {
		CLASS(fd, f)(pidfd);
		if (fd_empty(f))
			return -EBADF;
@@ -4051,45 +4100,17 @@ SYSCALL_DEFINE4(pidfd_send_signal, int, pidfd, int, sig,
		if (!access_pidfd_pidns(pid))
			return -EINVAL;

	switch (flags) {
	case 0:
		/* Infer scope from the type of pidfd. */
		if (fd_file(f)->f_flags & PIDFD_THREAD)
			type = PIDTYPE_PID;
		else
			type = PIDTYPE_TGID;
		break;
	case PIDFD_SIGNAL_THREAD:
		type = PIDTYPE_PID;
		break;
	case PIDFD_SIGNAL_THREAD_GROUP:
		type = PIDTYPE_TGID;
		break;
	case PIDFD_SIGNAL_PROCESS_GROUP:
		type = PIDTYPE_PGID;
		break;
	}

	if (info) {
		ret = copy_siginfo_from_user_any(&kinfo, info);
		if (unlikely(ret))
			return ret;

		if (unlikely(sig != kinfo.si_signo))
			return -EINVAL;

		/* Only allow sending arbitrary signals to yourself. */
		if ((task_pid(current) != pid || type > PIDTYPE_TGID) &&
		    (kinfo.si_code >= 0 || kinfo.si_code == SI_TKILL))
			return -EPERM;
	} else {
		prepare_kill_siginfo(sig, &kinfo, type);
		return do_pidfd_send_signal(pid, sig, type, info, flags);
	}
	}

	if (type == PIDTYPE_PGID)
		return kill_pgrp_info(sig, &kinfo, pid);
	else
		return kill_pid_info_type(sig, &kinfo, pid, type);
	return do_pidfd_send_signal(pid, sig, type, info, flags);
}

static int