If the user is doing a dup2 unsynchronized with the syspipe(), even if everything works, the results are wierd with both solutions. Both the pipe() and the dup2() can succeed, except the pipe() can be returning fd's that have nothing to do with a pipe. As you said, dup2 is an dangerous thing when the fd's are shared. However, you're right. Your solution yields a more explainable world in the unlikely event that newfd() fails in syspipe(). I put in a newfd2(). The exact code follows. I added the routine findfreefd() since I want to keep that piece of magic in one place. I noticed your snippet didn't check the need to growfd after getting i, perhaps a good reason for findfreefd(). /* * this assumes that the fgrp is locked */ int findfreefd(Fgrp *f, int start) { int fd; for(fd=start; fdnfd; fd++) if(f->fd[fd] == 0) break; if(fd >= f->nfd && growfd(f, fd) < 0) return -1; return fd; } int newfd(Chan *c) { int fd; Fgrp *f; f = up->fgrp; lock(f); fd = findfreefd(f, 0); if(fd < 0){ unlock(f); return -1; } if(fd > f->maxfd) f->maxfd = fd; f->fd[fd] = c; unlock(f); return fd; } int newfd2(int fd[2], Chan *c[2]) { Fgrp *f; f = up->fgrp; lock(f); fd[0] = findfreefd(f, 0); if(fd[0] < 0){ unlock(f); return -1; } fd[1] = findfreefd(f, fd[0]+1); if(fd[1] < 0){ unlock(f); return -1; } if(fd[1] > f->maxfd) f->maxfd = fd[1]; f->fd[fd[0]] = c[0]; f->fd[fd[1]] = c[1]; unlock(f); return 0; } ... long syspipe(ulong *arg) { int fd[2]; Chan *c[2]; Dev *d; validaddr(arg[0], 2*BY2WD, 1); evenaddr(arg[0]); d = devtab[devno('|', 0)]; c[0] = namec("#|", Atodir, 0, 0); c[1] = 0; fd[0] = -1; fd[1] = -1; if(waserror()){ cclose(c[0]); if(c[1]) cclose(c[1]); nexterror(); } c[1] = cclone(c[0], 0); if(walk(&c[0], "data", 1) < 0) error(Egreg); if(walk(&c[1], "data1", 1) < 0) error(Egreg); c[0] = d->open(c[0], ORDWR); c[1] = d->open(c[1], ORDWR); if(newfd2(fd, c) < 0) error(Enofd); poperror(); ((long*)arg[0])[0] = fd[0]; ((long*)arg[0])[1] = fd[1]; return 0; } As for create(), I'ld rather change the man page. Otherwise we'ld have to reserve an fd in some way before the open but not have that fd visible to other processes until it was assinged a channel or let it be step on-able by dup2...