https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=251227

            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: b...@freebsd.org
          Reporter: mqu...@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) operation.

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 = vfork();
    if (pid == 0) {
        // Child process
        char * const argv[] = { "env", "true", NULL };
        execv("/usr/bin/env", argv);
        assert(0 && "child returned after execv!");
    }

    // Parent process
    sleep(1);
    int result = setpgid(pid, pid);
    assert(result != 0);
    printf("error code: %d\n", errno);
    assert(errno == 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 target
pid does exist). Fundamentally, the behavior of the parent (and in particular,
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.)

-- 
You are receiving this mail because:
You are the assignee for the bug.
_______________________________________________
freebsd-bugs@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "freebsd-bugs-unsubscr...@freebsd.org"

Reply via email to