waitpid WNOHANG on debugged-process returns -ECHILD

Originator:rsesek
Number:rdar://47533738 Date Originated:2019-01-24
Status:Open Resolved:
Product:macOS + SDK Product Version:10.14.3 18D42
Classification:Bug Reproducible:Always
 
Area:
Something not on this list

Summary:
In order to implement a "waitpid with timeout" function, we have a function that calls waitpid(pid, &stat, WNOHANG) in a loop, with nanosleep() calls up to a certain timeout. We've found that if a debugger is attached to the child being waited on, waitpid() returns -1 with errno set to ECHILD. Before the debugger is attached, and after it is again detached, waitpid() returns 0. It is in the debugger-attached state that waitpid() returns unexpected results.

Steps to Reproduce:
1. Download waitpid-debugger.c and compile it
2. Run the program, output like this will be observed:

PARENT: created child 43439
PARENT: starting waitpid
PARENT: waitpid() returned 0, stat=0, errno=0 (Undefined error: 0)
CHILD: now running, pid=43439
CHILD: busy loop
PARENT: waitpid() returned 0, stat=0, errno=0 (Undefined error: 0)
PARENT: waitpid() returned 0, stat=0, errno=0 (Undefined error: 0)
CHILD: busy loop

3. In a new shell, do `lldb -p <pid-of-child>`
4. Observe the output change to:

PARENT: waitpid() returned -1, stat=0, errno=10 (No child processes)

5. In lldb, detach from the process
6. Observe the output return to:

PARENT: waitpid() returned 0, stat=0, errno=0 (Undefined error: 0)

Expected Results:
waitpid() should continue to return 0 when a debugger is attached to the process.

Actual Results:
waitpid() returns -1 with ECHILD errno.

Note that continuing in lldb to move the process out of the STOPPED state does not affect the results, nor does passing WUNTRACED in the options to waitpid().

Version/Build:
10.14.3 18D42
10.10.5 14F1713

===================================
% cat /Volumes/Build/tests/waitpid-debugger.c 
// clang -o waitpid-debugger waitpid-debugger.c

#include <stdio.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

void do_parent(pid_t child_pid) {
  printf("PARENT: created child %d\n", child_pid);
  printf("PARENT: starting waitpid\n");

  for (;;) {
    errno = 0;
    int statloc = 0;
    int rv = waitpid(child_pid, &statloc, WNOHANG|WUNTRACED);
    printf("PARENT: waitpid() returned %d, stat=%d, errno=%d (%s)\n", rv, statloc, errno, strerror(errno));
#if 0
    if (rv != 0)
      break;
#endif
    sleep(2);
  }

  printf("PARENT: exiting\n");
}

void do_child() {
  printf("CHILD: now running, pid=%d\n", getpid());

  for (;;) {
    printf("CHILD: busy loop\n");
    sleep(5);
  }
}

int main() {
  pid_t pid = fork();
  if (pid == -1) {
    perror("fork()");
    return 1;
  } else if (pid == 0) {
    do_child();
  } else {
    do_parent(pid);
  }

  return 0;
}

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!