Based on my understanding, when bash (or any process as long as I don't intentionally change this behavior) runs, it will have (along with other information) a list of the supplementary groups for the logged in user.

The following is the ps result for the supplementary groups of bash:

1409   bash       adm dialout cdrom plugdev lpadmin admin sambashare chris

chris is the primary group for the logged in user, so why is it listed as part of the supplementary groups?

  • 1
    See also the output of id.
    – Kusalananda
    Dec 12, 2017 at 13:41

3 Answers 3


initgroup(), the libc function that is typically called by login applications to set supplementary group list of the process it runs in your name does add the primary group id to that list.

If it didn't, that would be a security issue, as that would give you a way to relinquish access to that group by creating a setgid executable with one of your other groups, and execute that to lose your primary gid (that executable would have to call setgid() for the real gid to also change), which would give you access to resources that have been explicitly denied access to your primary group for instance.


$ ls -l file
-rw----r-- 1 root chazelas 7 Dec 12 15:33 file

Access has been denied to my primary gid:

$ cat file
cat: file: Permission denied

Now, let's start a shell as me where the supplementary group ids doesn't include that group to see what we could do if login didn't add that group to our supplementary group id list:

$ sudo perl -le '$( = $) =  "1000 2"; $< = $> = 1000; exec zsh'
$ ps -o rgroup,egroup,supgrp -p $$
chazelas chazelas bin
$ id -a
uid=1000(chazelas) gid=1000(chazelas) groups=1000(chazelas),2(bin)

1000 is in gid, egid, but not the supplementary group list. As I'm also a member of bin, I can create a setgid bin executable:

$ cp -f /usr/bin/env .
$ chgrp bin env
$ chmod g+s env
$ ./env perl -U -le '$( = $); exec qw(/usr/bin/id -a)'
uid=1000(chazelas) gid=2(bin) groups=2(bin)

I'm using perl to call setgid() ($( = $)). And then I've lost membership of the chazelas group. So:

$ ./env perl -U -le '$( = $); exec qw(cat file)'

By adding the group to the supplementary list, login makes sure you can't get out of it (as only root can change that list and it's not affected by the execution of setgid executables).

  • 1
    Excellent explanation, thank you :-) May 6, 2019 at 8:16

Because something put it there. (Not very satisfactory, I know.)

POSIX take on the getgroups() system call is that

It is implementation-defined whether getgroups() also returns the effective group ID in the grouplist array.

On Linux (4.9), getgroups() doesn't include the effective GID in the returned, and neither does /proc/$pid/status. ps on Debian also doesn't seem to add it while displaying, but id does.

If the same GID is explicitly added with setgroups(), then it's listed.



If you're looking for an explanation, man pages is always a good place to start. See man ps:

supgrp SUPGRP group names of supplementary groups, if any. See getgroups(2).

and then, naturally, [man getgroups][1]:

getgroups() returns the supplementary group IDs of the calling process in list. The argument size should be set to the maximum number of items that can be stored in the buffer pointed to by list. If the calling process is a member of more than size supplementary groups, then an error results. It is unspecified whether the effective group ID of the calling process is included in the returned list. (Thus, an application should also call getegid(2) and add or remove the resulting value.)


Therefore, you should ps -eo pid,user,args,sgroup,supgrp and possibly subtract the value in the SGROUP column from those in the SUPGRP column. This is the way to get only supplementary groups.


As a matter of fact, ps doesn't call getgroups(), as pointed out in the comments. See ilkkachu's and Stéphane Chazelas's answers which are both technically correct and well explained. This answer only wants to say that it is expected that supplementary groups may contain the primary group.

  • Well no. On Linux at least, ps -o supgrp does return the supplementary group list and does not include the egid there (unless it is also in the supplementary list). ps doesn't call getgroups(), the reference to getgroups(2) is to link to a description of the supplementary group list. If you remove the egid from the supgrp, you do not obtain the real supgrp list. Dec 12, 2017 at 15:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .