Subject: Re: bin/25395: sh(1) closes FD 0 for some pipe members
To: None <netbsd-bugs@netbsd.org>
From: David Laight <david@l8s.co.uk>
List: netbsd-bugs
Date: 04/29/2004 21:49:25
> >Number:         25395
> >Category:       bin
> >Synopsis:       sh(1) closes FD 0 for some pipe members

> 	I do not know if this bug is still present in newer releases

Still fails in current.

> >How-To-Repeat:
> 	$ sh -c 'cat /etc/motd | tr a-z A-Z | grep foo | sed s/zip/zap/g' <&-
> 	grep: fstat: Bad file descriptor

The problem arises because the shell isn't careful enough to use fd numbers
above 2 for the ends of pipes it is keeping internally as it builds the
command pipeline.

This diff fixes it, but is (maybe) a bit OTT since at least one end of
every pipe is closed very shortly after.

	David

Index: eval.c
===================================================================
RCS file: /cvsroot/src/bin/sh/eval.c,v
retrieving revision 1.75
diff -u -p -r1.75 eval.c
--- eval.c	14 Nov 2003 10:27:10 -0000	1.75
+++ eval.c	29 Apr 2004 20:36:57 -0000
@@ -45,6 +45,7 @@ __RCSID("$NetBSD: eval.c,v 1.75 2003/11/
 #include <signal.h>
 #include <stdio.h>
 #include <unistd.h>
+#include <sys/fcntl.h>
 #include <sys/times.h>
 #include <sys/param.h>
 #include <sys/types.h>
@@ -125,6 +126,31 @@ SHELLPROC {
 }
 #endif
 
+static int
+sh_pipe(int fds[2])
+{
+	int nfd;
+
+	if (pipe(fds))
+		return -1;
+
+	if (fds[0] < 10) {
+		nfd = fcntl(fds[0], F_DUPFD, 10);
+		if (nfd != -1) {
+			close(fds[0]);
+			fds[0] = nfd;
+		}
+	}
+
+	if (fds[1] < 10) {
+		nfd = fcntl(fds[1], F_DUPFD, 10);
+		if (nfd != -1) {
+			close(fds[1]);
+			fds[1] = nfd;
+		}
+	}
+	return 0;
+}
 
 
 /*
@@ -476,7 +502,7 @@ evalpipe(union node *n)
 		prehash(lp->n);
 		pip[1] = -1;
 		if (lp->next) {
-			if (pipe(pip) < 0) {
+			if (sh_pipe(pip) < 0) {
 				close(prevfd);
 				error("Pipe call failed");
 			}
@@ -548,7 +574,7 @@ evalbackcmd(union node *n, struct backcm
 #endif
 	{
 		INTOFF;
-		if (pipe(pip) < 0)
+		if (sh_pipe(pip) < 0)
 			error("Pipe call failed");
 		jp = makejob(n, 1);
 		if (forkshell(jp, n, FORK_NOJOB) == 0) {
@@ -800,7 +826,7 @@ evalcommand(union node *cmd, int flags, 
 		mode = cmd->ncmd.backgnd;
 		if (flags & EV_BACKCMD) {
 			mode = FORK_NOJOB;
-			if (pipe(pip) < 0)
+			if (sh_pipe(pip) < 0)
 				error("Pipe call failed");
 		}
 #ifdef DO_SHAREDVFORK

-- 
David Laight: david@l8s.co.uk