Slash Boxes
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

dgl (5868)

  (email not shown publicly)

Journal of dgl (5868)

Wednesday May 02, 2007
08:12 PM

Perl's lack of setgroups

[ #33182 ]
(Hi, I thought I'd start a blog..)

I want a process (started as root) to put itself in some additional groups before it changes to an unprivileged user. perldoc perlvar states this isn't possible via setting $GID or $(:

"However, a value assigned to $( must be a single number used to set the real gid. So the value given by $( should not be assigned back to $( without being forced numeric, such as by adding zero."

Next stop is POSIX, it helpfully contradicts perlvar:

"setgid [..] Similar to assigning a value to the Perl’s builtin $) variable, see "$GID" in perlvar, except that the latter will change only the real user identifier, and that the setgid() uses only a single numeric argument, as opposed to a space-separated list of numbers."

POSIX also provides the function getgroups, but there's no sign of setgroups! Even more helpfully yet another part of the Perl documentation implies this feature was added in perl 5.004.

I gave up and just tried it (with Perl 5.8.8 on Ubuntu):

# perl -MEnglish -e'$EGID = $GID = "1000 10"; $EUID = $UID = 65534; system("id")'
uid=65534(nobody) gid=1000(dgl) groups=0(root)

It doesn't work. Also notice it keeps the additional group root around, which is a bit scary too.

Perl does provide some help though -- through syscall() it's possible to work around the lack of setgroups. Unfortantely syscall() depends on knowing the number of the system call, which varies according to the architecture (even x86-64 is different to x86) and operating system in use.

setgroups is simply (on Linux x86):

sub setgroups {
  syscall 206, scalar @_, pack("L" x @_, @_);

This has the desired effect:

# perl -MEnglish -MPOSIX -e'sub setgroups { syscall 206, scalar @_, pack("L" x @_, @_); } setgroups(1000,10); $EGID = $GID = 1000; $EUID = $UID = 65534; system("id")'
uid=65534(nobody) gid=1000(dgl) groups=10(uucp),1000(dgl)

Maybe it would be better written as a module for CPAN although personally I'd rather see this fixed in the Perl core. As it leaves additional groups around with no way to get rid of them, I wouldn't be surprised if it results in a security issue in Perl programs trying to drop privileges.

Note under Linux the syscall number can be found by looking in /usr/include/<arch>/syscall.h (where <arch> is your architecture, e.g. x86). It might be easier to grep /usr/include for __NR_setgroups32 (or __NR_setgroups if it's a newer architecture).

Update: Ignore this post, I realised it's very simple, $EGID is the thing to set first.. i.e. $GID = $EGID = "10 1000"; works fine..

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
More | Login | Reply
Loading... please wait.
  • Hint: if you put that sort of update at the top of the post, it would spare many of your readers the effort of fully reading the entire post in detail. (I would have switched to skimming mode if I knew you went down a garden path.)

    Thanks in advance. :-)

    • Good point.

      BTW, is there a way (or greasemonkey script..) to make this entry box bigger?
      • I use the Stylish [] extension to impose this site-specific style:

        @namespace url(;
        @-moz-document domain("") {
          textarea[name="postercomment"], textarea[name="article"] {
            width: 45em !important;
            height: 40em !important;

        Another option would be Resizable Textareas [], I guess.