This is the mail archive of the cygwin mailing list for the Cygwin project.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: popen bugs

Hash: SHA1

According to Christopher Faylor on 8/17/2009 5:02 PM:
> On Mon, Aug 17, 2009 at 03:45:23PM -0700, Eric Blake wrote:
>> popen misbehaves when various std fds are closed.
> And, who among us could have not seen that coming?

I see you fixed the fd access from within child processes in CVS; thanks.
 However, there is still a bug: POSIX requires that children of subsequent
popen's no longer see the fd of the first popen, without regards to
FD_CLOEXEC.  Testing on FreeBSD, Solaris, and Linux show that these OS's
leave the parent's fd as non-cloexec, but still obey this requirement.
So, they must do it by maintaining a list of fd's opened by popen, and
explicitly close everything in that list when calling popen again.  And at
least the newlib implementation (which probably stems from the BSD
implementation) already has to maintain this list for making pclose work

Cygwin is not compatible with Linux in this regard, because it is relying
on marking the fd as cloexec, and an explicit call to remove the cloexec
flag will leak the fd of the first child into the second popen contrary to
POSIX.  (And this is probably partly my fault, for implementing it wrong
in newlib, for which I will be submitting a patch today.)

There's also several extensions to consider: a recent addition to glibc

popen (cmd, "re");

to mark the parent's fd as cloexec (and even do so atomically once pipe2
is supported), and if cygwin ever gets bi-directional pipes, at least
FreeBSD supports:

popen (cmd, "r+");

to set the parent FILE as read/write and the child gets the pipe for both
stdin and stdout.

$ cat foo.c # program to sniff whether popen sets cloexec flag
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main()
  FILE *c = popen (":", "r");
  printf ("%x %x\n", FD_CLOEXEC, fcntl (fileno (c), F_GETFD));
  return 0;

$ cat bar.c # program to show that cygwin violates posix
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main (int argc, char **argv)
  switch (argc)
    case 1:
      /* driver - create two children */
	FILE *c1, *c2;
	char cmd1[80], cmd2[80];
	snprintf (cmd1, 80, "%s r", argv[0]);
	c1 = popen (cmd1, "r");
	fcntl (fileno (c1), F_SETFD,
	       fcntl (fileno (c1), F_GETFD) & ~FD_CLOEXEC);
	snprintf (cmd2, 80, "%s %d r", argv[0], fileno (c1));
	c2 = popen (cmd2, "r");
	fprintf (stderr, "read %c from child2\n", getc (c2));
	fprintf (stderr, "read %c from child1\n", getc (c1));
	pclose (c1);
	pclose (c2);
    case 2:
      /* first child, argv[1] is zero to read, else write */
      if (atoi (argv[1]))
	fprintf (stderr, "child1 read %c\n", getchar ());
	fprintf (stderr, "child1 putchar returned %c\n", putchar ('1'));
    case 3:
      /* second child, argv[1] is fd that should be closed (if not 0
	 or 1), argv[2] is zero to read, else write */
      if (dup2 (atoi (argv[1]), 6) == 6)
	fputs ("child2 saw leaked fd\n", stderr);
      if (atoi (argv[2]))
	fprintf (stderr, "child2 read %c\n", getchar ());
	fprintf (stderr, "child2 putchar returned %c\n", putchar ('2'));
      return 1;
  return 0;

- --
Don't work too hard, make some time for fun as well!

Eric Blake   
Version: GnuPG v1.4.9 (Cygwin)
Comment: Public key at
Comment: Using GnuPG with Mozilla -


Problem reports:
Unsubscribe info:

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]