Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 18 Nov 2020 01:18:09 +0000
From:      bugzilla-noreply@freebsd.org
To:        bugs@FreeBSD.org
Subject:   [Bug 251227] setpgid sometimes returns ESRCH instead of EACCES
Message-ID:  <bug-251227-227@https.bugs.freebsd.org/bugzilla/>

next in thread | raw e-mail | index | archive | help
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D251227

            Bug ID: 251227
           Summary: setpgid sometimes returns ESRCH instead of EACCES
           Product: Base System
           Version: 12.2-RELEASE
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Many People
          Priority: ---
         Component: kern
          Assignee: bugs@FreeBSD.org
          Reporter: mqudsi@neosmart.net

setpgid(2) says

>      [ESRCH]            The requested process does not exist.
>      [ESRCH]            The target process is not the calling process or a
>                         child of the calling process.
>      [EACCES]           The requested process is a child of the calling
>                         process, but it has performed an exec(3) operatio=
n.

However, as demonstrated by the following test, FreeBSD is incorrectly
returning ESRCH when setpgid(2) is called on a child that has called both
fork(2) and execv(3) by the time the parent calls setpgid(2).

```
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>

int main() {
    pid_t pid =3D vfork();
    if (pid =3D=3D 0) {
        // Child process
        char * const argv[] =3D { "env", "true", NULL };
        execv("/usr/bin/env", argv);
        assert(0 && "child returned after execv!");
    }

    // Parent process
    sleep(1);
    int result =3D setpgid(pid, pid);
    assert(result !=3D 0);
    printf("error code: %d\n", errno);
    assert(errno =3D=3D EACCES && "incorrect error code returned!");
    return 0;
}
```

The test above fails on FreeBSD 11.2 and FreeBSD 12.2; it passes on Linux.

The underlying issue appears to be some sort of race condition: the
implementation of sys_setpgid in sys/kern/kern_prot.c correctly checks for =
the
P_EXEC flag and sets errno to EACCES if present.

You'll notice the use of vfork(2) in the test above, which, though not
guaranteed, in practice blocks execution of the parent process until exec(3)
has been called in the child. If the sleep(1) call is omitted from the test,
the test passes (EACCES is returned because execv(3) has been called by the
child process). With or without the call to sleep(1), the unreaped child
process remains present until the parent terminates (i.e. the referenced ta=
rget
pid does exist). Fundamentally, the behavior of the parent (and in particul=
ar,
the return code from its call to setpgid) should be the same both with and
without the call to sleep(1), given the semantics of vfork(3).

(The behavior does not change if /usr/bin/true is spawned directly instead =
of
via /usr/bin/env, i.e. this is not an issue with child vs grandchild.)

--=20
You are receiving this mail because:
You are the assignee for the bug.=



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-251227-227>