Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 21 Oct 2000 09:09:56 -0700 (PDT)
From:      grubba@roxen.com
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   misc/22190: A threaded read(2) from a socketpair(2) fd can sometimes fail with errno 19 (ENODEV)
Message-ID:  <20001021160956.9375F37B4C5@hub.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         22190
>Category:       misc
>Synopsis:       A threaded read(2) from a socketpair(2) fd can sometimes fail with errno 19 (ENODEV)
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat Oct 21 09:10:01 PDT 2000
>Closed-Date:
>Last-Modified:
>Originator:     Henrik Grubbström
>Release:        4.0-RELEASE #0
>Organization:
Roxen Internet Software
>Environment:
FreeBSD snok.idonex.se 4.0-RELEASE FreeBSD 4.0-RELEASE #0: Wed Mar 15 02:16:55 GMT 2000     jkh@monster.cdrom.com:/usr/src/sys/compile/GENERIC  i386

>Description:
In the testsuite for a threaded application, a process spawning test
that spawns 1000 /bin/cat /dev/null and waits for them sometimes fails
because read(2) returns -1 with errno set to 19 (ENODEV). ENODEV is
not a documented error code for read(2).

Down-stripped code that triggs the bug:
  {
    pid_t pid=-2;
    int control_pipe[2];	/* Used for communication with the child. */
    char buf[4];

    if (socketpair(AF_UNIX, SOCK_STREAM, 0, control_pipe) < 0) {
      error("Failed to create child communication pipe.\n");
    }

    {
      int loop_cnt = 0;
      sigset_t new_sig, old_sig;
      sigfillset(&new_sig);
      while(sigprocmask(SIG_BLOCK, &new_sig, &old_sig))
	;

      do {

	pid=fork();
	if (pid == -1) {
	  if (errno == EAGAIN) {
	    /* Process table full or similar.
	     * Try sleeping for a bit.
	     */
	    if (loop_cnt++ < 60) {
	      /* Don't sleep for too long... */
	      poll(NULL, 0, 100);

	      /* Try again */
	      continue;
	    }
	  } else if (errno == EINTR) {
	    /* Try again */
	    continue;
	  }
	}
	break;
      } while(1);

      while(sigprocmask(SIG_SETMASK, &old_sig, 0))
	;
    }

    if(pid == -1) {
      int e = errno;
      /*
       * fork() failed
       */

      while(close(control_pipe[0]) < 0 && errno==EINTR);
      while(close(control_pipe[1]) < 0 && errno==EINTR);

      error("Process.create_process(): fork() failed. errno:%d\n",
	    e);
    } else if(pid) {
      int olderrno;

      /*
       * The parent process
       */

      /* Close our child's end of the pipe. */
      while(close(control_pipe[1]) < 0 && errno==EINTR);

      /* Wake up the child. */
      buf[0] = 0;

      while (((e = write(control_pipe[0], buf, 1)) < 0) && (errno == EINTR))
	;
      if(e!=1) {
	/* Paranoia in case close() sets errno. */
	olderrno = errno;
	while(close(control_pipe[0]) < 0 && errno==EINTR)
          ;
	error("Child process died prematurely. (e=%d errno=%d)\n",
	      e, olderrno);
      }

      /* Wait for exec or error */
      while (((e = read(control_pipe[0], buf, 3)) < 0) && (errno == EINTR))
	;
      /* Paranoia in case close() sets errno. */
      olderrno = errno;

      while(close(control_pipe[0]) < 0 && errno==EINTR)
        ;

      if (!e) {
	/* OK! */
	pop_n_elems(args);
	push_int(0);
	return;
      } else {
	/* Something went wrong. */
	switch(buf[0]) {
	  /* ... */
	case 0:
	  /* read() probably failed. */
	default:
	  /******************************************************************
           * This point is reached with buf = {0, 4, 0}, e = -1, olderrno=19.
           *****************************************************************/
	  error("Process.create_process(): "
		"Child failed: %d, %d, %d, %d, %d!\n",
		buf[0], buf[1], buf[2], e, olderrno);
	  break;
	}
      }
    }else{
      /*
       * The child process
       */
      /* Close our parent's end of the pipe. */
      while(close(control_pipe[0]) < 0 && errno==EINTR);
      /* Ensure that the pipe will be closed when the child starts. */
      if(set_close_on_exec(control_pipe[1], 1) < 0)
	PROCERROR(PROCE_CLOEXEC, 0);

      /* Wait for parent to get ready... */
      while ((( e = read(control_pipe[1], buf, 1)) < 0) && (errno == EINTR))
	;

      /* ... */
      execvp(argv[0], argv);
      PROCERROR(PROCE_EXEC, 0);
      exit(99);
    }
  }

For the full source, please check src/signal_handler.c:f_create_process() in a Pike distribution.

Testsuite report:

testsuite: Test 9406 (shift 0) (CRNL) failed.
  1: mixed a() {  for(int x=0;x<10;x++) { for(int e=0;e<100;e++) if(Process.create_process(({"/bin/cat","/dev/null"}))->wait()) return e; __signal_watchdog(); } return -1;; }
  2: mixed b() { return -1; }
Error: Process.create_process(): Child failed: 0, 4, 0, -1, 19!
__builtin.create_process: create(({"/bin/cat","/dev/null"}))
__builtin: create_process()
testsuite: Test 9406 (shift 0) (CRNL):1: a()
/tmp/autobuild/pike7.1-20001021082826.tar/bin/test_pike.pike:572: main(3,({"/tmp/autobuild/pike7.1-20001021082826.tar/bin/test_pike.pike","modules/CommonLog/module_testsuite","modules/Gdbm/module_testsuite","modules/Gettext/module_testsuite","modules/Gmp/module_testsuite",,,34}))

>How-To-Repeat:
Unfortunately, the problem is intermittent.
It may be triggered by resource exhaustion.
>Fix:


>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




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