Of course, the fundamental puzzle here is that filesystem permission checks are based on the combination of (the effective UID and) the effective GID and the supplementary GIDs. So, from the point of view of file permissions checks, the effective GID is equivalent to the supplementary GIDs, which leads to the OP's question. (In passing: if we are talking about Linux, it is actually the filesystem UID/GID that are used in filesystem permission checks, rather than the effective UID and GID, but the former IDs almost always have the same values as the latter IDs.)
So, there must be some cases where the real/effective/saved-set GIDs are not equivalent to the supplementary GIDs. (I group the real/effective/saved-set GIDs together, because the normal set*gid() permission rules say that an unprivileged process can change any one of those GIDs to the same value as one of the other two.)
And indeed, there are a few such cases. access(2) makes its checks based on the process's real user ID and group ID. If an unprivileged user was able to change the real group ID to be the same as one of the supplementary GIDs that is not the effective or saved set GID, then the behavior of access(2) could be manipulated.
There are other such cases. See the Linux mkdir(2) man page, for an example. Depending on whether the set-GID mode bit is set on the parent directory, a new file created in the directory takes its group ownership from the creating process's effective GID. Again, if an unprivileged process could change its effective GID to be the same as one of its supplementary GIDs, it could manipulate the group ownership of new files in unexpected ways. Similar comments apply for mknod(2) and the System V IPC calls semget(2), shmget(2), and msgget(2).
There are also some Linux-specific cases where the real/effective/saved set GIDs are not equivalent to the supplementary GIDs. See process_vm_readv(2) and prlimit(2), for example.
/usr/bin/env
with setgid permission).newgrp/sg
refers to the account database, not the supplementary group list of the process.setgid()
would allow you to leave membership of a group (which would be a security concern), but then again you could also do it with the same setgid executable trick as above, and your gid usually also is in your supplementary list (initgroups(3)
takes a gid argument just for that).