Skip to contentSkip to conversation

proc: Add complete process group list

PATCH In review Mike McCormack 4 participants 6 messages Jun 22, 2010 – Jun 24, 2010 Proc filesystem

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 proc: Add complete process group list

If a process is in more than NGROUPS_SMALL (32) groups, it's not possible
for any other user space process to determine the list of groups it is in using /proc/<pid>/status.

Increasing the list of groups listed by /proc/<pid>/status would lead to
very long lines that file, and possible misbehavior of userspace programs that parse /proc/<pid>/status, so instead I have opted to create a new file /proc/<pid>/groups, which contains the list of supplementary groups for each pid.

The new file /proc/<pid>/groups consists of a single group id per line,
with each line being 11 characters long. This should be enough space for 16bit or 32bit group ids.

This feature might be useful for a server listening on a unix domain pipe
to determine the list of groups that a client process is in from its pid.

---
 fs/proc/base.c |   55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 55 insertions(+), 0 deletions(-)
fs/proc/base.c +55 -0
index acb7ef8..689362c 100644
@@ -82,6 +82,8 @@
 #include <linux/pid_namespace.h>
 #include <linux/fs_struct.h>
 #include <linux/slab.h>
+#include <linux/cred.h>
+
 #include "internal.h"
 
 /* NOTE:
@@ -1325,6 +1327,57 @@ static const struct file_operations proc_pid_set_comm_operations = {
 	.release	= single_release,
 };
 
+/* supplementary groups, one per line */
+static int groups_proc_show(struct seq_file *m, void *v)
+{
+	struct inode *inode = m->private;
+	struct group_info *group_info;
+	struct task_struct *task;
+	const struct cred *cred;
+	struct pid *pid;
+	unsigned int g;
+
+	pid = proc_pid(inode);
+	task = get_pid_task(pid, PIDTYPE_PID);
+	if (!task)
+		return -ESRCH;
+
+	cred = get_task_cred(task);
+	group_info = cred->group_info;
+
+	/*
+	 * Groups may be 16bit or 32bit.
+	 * Try to be easily machine and human readable.
+	 */
+	for (g = 0; g < group_info->ngroups; g++)
+		seq_printf(m, "%-10u\n", GROUP_AT(group_info, g));
+
+	put_cred(cred);
+	put_task_struct(task);
+
+	return 0;
+}
+
+static int groups_proc_open(struct inode *inode, struct file *filp)
+{
+	int ret;
+
+	ret = single_open(filp, groups_proc_show, NULL);
+	if (!ret) {
+		struct seq_file *m = filp->private_data;
+
+		m->private = inode;
+	}
+	return ret;
+}
+
+static const struct file_operations proc_groups_operations = {
+	.open		= groups_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
 /*
  * We added or removed a vma mapping the executable. The vmas are only mapped
  * during exec and are not mapped with the mmap system call.
@@ -2586,6 +2639,7 @@ static const struct pid_entry tgid_base_stuff[] = {
 	INF("cmdline",    S_IRUGO, proc_pid_cmdline),
 	ONE("stat",       S_IRUGO, proc_tgid_stat),
 	ONE("statm",      S_IRUGO, proc_pid_statm),
+	REG("groups",     S_IRUSR, proc_groups_operations),
 	REG("maps",       S_IRUGO, proc_maps_operations),
 #ifdef CONFIG_NUMA
 	REG("numa_maps",  S_IRUGO, proc_numa_maps_operations),
@@ -2921,6 +2975,7 @@ static const struct pid_entry tid_base_stuff[] = {
 	INF("cmdline",   S_IRUGO, proc_pid_cmdline),
 	ONE("stat",      S_IRUGO, proc_tid_stat),
 	ONE("statm",     S_IRUGO, proc_pid_statm),
+	REG("groups",    S_IRUSR, proc_groups_operations),
 	REG("maps",      S_IRUGO, proc_maps_operations),
 #ifdef CONFIG_NUMA
 	REG("numa_maps", S_IRUGO, proc_numa_maps_operations),
Signature · 1 line
1.5.6.5
#1 lore raw
Quoted 7 lines · Mike McCormack

The new file /proc/<pid>/groups consists of a single group id per line,
with each line being 11 characters long. This should be enough space for 16bit or 32bit group ids.

This feature might be useful for a server listening on a unix domain pipe
to determine the list of groups that a client process is in from its pid.

As usual, I can never comment whether we need this or not. Just a minor nit about the code,

fs/proc/base.c static const struct file_operations proc_pid_set_comm_operations = { · line 1341

Quoted 11 lines
fs/proc/base.c +11 -0
+static int groups_proc_show(struct seq_file *m, void *v)
+{
+	struct inode *inode = m->private;
+	struct group_info *group_info;
+	struct task_struct *task;
+	const struct cred *cred;
+	struct pid *pid;
+	unsigned int g;
+
+	pid = proc_pid(inode);
+	task = get_pid_task(pid, PIDTYPE_PID);

get_proc_task() ?

fs/proc/base.c static const struct file_operations proc_pid_set_comm_operations = { · line 1345

Quoted 4 lines
fs/proc/base.c +4 -0
+	if (!task)
+		return -ESRCH;
+
+	cred = get_task_cred(task);

OTOH. Not that I think this is terribly important, but I don't think this needs get_task_struct + get_cred,

	group_info = NULL;

	rcu_read_lock();
	task = pid_task(proc_pid(inode), PIDTYPE_PID);
	if (task) {
		group_info = _task_cred(task)->group_info;
		get_group_info(group_info);
	}
	rcu_read_unlock();

	if (!group_info)
		retrn ESRCH;

Feel free to ignore though.

Oleg.

#2 lore raw
In reply to Mike McCormack
Quoted 10 lines · On Wed, 23 Jun 2010 00:07:26 +0900 Mike McCormack <[email protected]> wrote:

If a process is in more than NGROUPS_SMALL (32) groups, it's not possible
for any other user space process to determine the list of groups it is in using /proc/<pid>/status.

Increasing the list of groups listed by /proc/<pid>/status would lead to
very long lines that file, and possible misbehavior of userspace programs that parse /proc/<pid>/status, so instead I have opted to create a new file /proc/<pid>/groups, which contains the list of supplementary groups for each pid.

Quoted 6 lines

The new file /proc/<pid>/groups consists of a single group id per line,
with each line being 11 characters long. This should be enough space for 16bit or 32bit group ids.

This feature might be useful for a server listening on a unix domain pipe
to determine the list of groups that a client process is in from its pid.

"might be"?

It would be useful to hear a bit more about usage scenarios, why this is needed, etc - some hard info which would justify permanent extension of the kernel->userspace API. How does this get used, why is it needed, what are the alternatives, etc.

I don't recall having heard of anyone using the groups fields in /proc/pid/status before.

#3 lore raw
Quoted 4 lines · Andrew Morton
Quoted 2 lines

This feature might be useful for a server listening on a unix domain pipe
to determine the list of groups that a client process is in from its pid.

"might be"?

Well, "would be" :-)

Quoted 4 lines

It would be useful to hear a bit more about usage scenarios, why this is needed, etc - some hard info which would justify permanent extension of the kernel->userspace API. How does this get used, why is it needed, what are the alternatives, etc.

This will be used in a device with groups permissions checked in userspace.

Say you have a process called "telephony-server", and it talks to a number of client processes with different privilege levels via a unix domain socket.

telephony-server might be able do things which should have different privilege levels, like send SMS messages, make phone calls, download firmware to a 3G modem, etc. The client processes would be members of groups reflecting each privilege. Depending on the number of similar servers in the system, and how fine-grained the privileges are, there might be lots of groups (>32).

telephony-server should be able to allow or deny requests depending on whether an application is a member of the correct group or not.

unix sockets can pass credentials, but currently I can only see struct ucred (pid, uid and gid) being passed. Using the pid, /proc/pid/status can be read for a list of groups, but it only lists up to 32 groups.

Ways I can see to get the groups for a unix socket peer from it's pid all mostly require some kernel modification:

* modify kernel to list all groups in /proc/<pid>/status
- very long lines become possible in status file
- no way to know whether you're using an old kernel with 32 group limit or new kernel and pid only has 32 groups

* modify kernel to add /proc/<pid>/groups
- more kernel-userland interface

* implement LOCAL_CREDS for unix domain sockets in Linux
- work

* limit number of groups to 32
- limit is imposed by /proc code

* create multiple unix domian sockets per privilege with group r/w only
- seems like trouble

What do you think?

thanks,

Mike

#4 lore raw
Quoted 4 lines

* modify kernel to list all groups in /proc/<pid>/status
- very long lines become possible in status file
- no way to know whether you're using an old kernel with 32 group limit or new kernel and pid only has 32 groups

Is this necessary? Why?
Who need 32 groups limitation?

Quoted 2 lines

* modify kernel to add /proc/<pid>/groups
- more kernel-userland interface

My personal opinion (aka my personal prefer) is,
- If fixing /proc/<pid>/status is zero downside, it should do.
- If fixing /proc/</pid>status is some downside (e.g. performance down), /proc/<pid>groups is better

because, 99% user don't use >32groups.

And, personally I dislike following three ;)

Quoted 8 lines

* implement LOCAL_CREDS for unix domain sockets in Linux
- work

* limit number of groups to 32
- limit is imposed by /proc code

* create multiple unix domian sockets per privilege with group r/w only
- seems like trouble

#5 lore raw
In reply to Mike McCormack
Quoted 10 lines

If a process is in more than NGROUPS_SMALL (32) groups, it's not possible
for any other user space process to determine the list of groups it is in using /proc/<pid>/status.

Increasing the list of groups listed by /proc/<pid>/status would lead to
very long lines that file, and possible misbehavior of userspace programs that parse /proc/<pid>/status, so instead I have opted to create a new file /proc/<pid>/groups, which contains the list of supplementary groups for each pid.

Quoted 8 lines

The new file /proc/<pid>/groups consists of a single group id per line,
with each line being 11 characters long. This should be enough space for 16bit or 32bit group ids.

This feature might be useful for a server listening on a unix domain pipe
to determine the list of groups that a client process is in from its pid.

Just dumb question.

Why don't you fix /proc/<pid>/status? Can we share your worry? I haven't review your patch carefully yet. but your groups_proc_show() seems don't have heavy weight lock.

note: I'm not against your plan. it's just curiosity.