From 6a0ad2506116f4ddc3f9f617a90ba04a57eeef88 Mon Sep 17 00:00:00 2001
From: Denis Vlasenko <vda.linux@googlemail.com>
Date: Fri, 25 Jul 2008 13:34:05 +0000
Subject: ash: dont allow e.g. exec <&10 to attach to stript's fd!

function                                             old     new   delta
is_hidden_fd                                           -      61     +61
redirect                                            1135    1164     +29
popstring                                            134     140      +6
printf_main                                          635     637      +2
evalvar                                             1374    1376      +2
echo_main                                            294     296      +2
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 5/0 up/down: 102/0)             Total: 102 bytes
---
 coreutils/echo.c   | 11 +++++++----
 coreutils/printf.c |  7 +++++--
 shell/ash.c        | 47 +++++++++++++++++++++++++++++++++++------------
 3 files changed, 47 insertions(+), 18 deletions(-)

diff --git a/coreutils/echo.c b/coreutils/echo.c
index 36cb6b3af..decca095f 100644
--- a/coreutils/echo.c
+++ b/coreutils/echo.c
@@ -46,8 +46,11 @@ int echo_main(int argc UNUSED_PARAM, char **argv)
 	 * even if libc receives EBADF on write attempts, it feels determined
 	 * to output data no matter what. So it will try later,
 	 * and possibly will clobber future output. Not good. */
-	if (dup2(1, 1) != 1)
-		return -1;
+// TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR?
+	if (fcntl(1, F_GETFL) == -1)
+		return 1; /* match coreutils 6.10 (sans error msg to stderr) */
+	//if (dup2(1, 1) != 1) - old way
+	//	return 1;
 
 	arg = *++argv;
 	if (!arg)
@@ -58,8 +61,8 @@ int echo_main(int argc UNUSED_PARAM, char **argv)
 	char eflag = 0;
 
 	/* We must check that stdout is not closed. */
-	if (dup2(1, 1) != 1)
-		return -1;
+	if (fcntl(1, F_GETFL) == -1)
+		return 1;
 
 	while (1) {
 		arg = *++argv;
diff --git a/coreutils/printf.c b/coreutils/printf.c
index 72acbc751..76524f706 100644
--- a/coreutils/printf.c
+++ b/coreutils/printf.c
@@ -348,8 +348,11 @@ int printf_main(int argc UNUSED_PARAM, char **argv)
 	 * even if libc receives EBADF on write attempts, it feels determined
 	 * to output data no matter what. So it will try later,
 	 * and possibly will clobber future output. Not good. */
-	if (dup2(1, 1) != 1)
-		return -1;
+// TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR?
+	if (fcntl(1, F_GETFL) == -1)
+		return 1; /* match coreutils 6.10 (sans error msg to stderr) */
+	//if (dup2(1, 1) != 1) - old way
+	//	return 1;
 
 	/* bash builtin errors out on "printf '-%s-\n' foo",
 	 * coreutils-6.9 works. Both work with "printf -- '-%s-\n' foo".
diff --git a/shell/ash.c b/shell/ash.c
index bd2433c88..11174683e 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -4889,7 +4889,7 @@ static int need_to_remember(struct redirtab *rp, int fd)
 {
 	int i;
 
-	if (!rp) /* remebering was not requested */
+	if (!rp) /* remembering was not requested */
 		return 0;
 
 	for (i = 0; i < rp->pair_count; i++) {
@@ -4901,6 +4901,28 @@ static int need_to_remember(struct redirtab *rp, int fd)
 	return 1;
 }
 
+/* "hidden" fd is a fd used to read scripts, or a copy of such */
+static int is_hidden_fd(struct redirtab *rp, int fd)
+{
+	int i;
+	struct parsefile *pf = g_parsefile;
+	while (pf) {
+		if (fd == pf->fd) {
+			return 1;
+		}
+		pf = pf->prev;
+	}
+	if (!rp)
+		return 0;
+	fd |= COPYFD_RESTORE;
+	for (i = 0; i < rp->pair_count; i++) {
+		if (rp->two_fd[i].copy == fd) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
 /*
  * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
  * old file descriptors are stashed away so that the redirection can be
@@ -4950,8 +4972,15 @@ redirect(union node *redir, int flags)
 	do {
 		fd = redir->nfile.fd;
 		if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
-			if (redir->ndup.dupfd == fd)
-				continue; /* redirect from/to same file descriptor */
+			int right_fd = redir->ndup.dupfd;
+			/* redirect from/to same file descriptor? */
+			if (right_fd == fd)
+				continue;
+			/* echo >&10 and 10 is a fd opened to the sh script? */
+			if (is_hidden_fd(sv, right_fd)) {
+				errno = EBADF; /* as if it is closed */
+				ash_msg_and_raise_error("%d: %m", right_fd);
+			}
 			newfd = -1;
 		} else {
 			newfd = openredirect(redir); /* always >= 0 */
@@ -4988,14 +5017,8 @@ redirect(union node *redir, int flags)
 				/* "exec fd>&-" should not close fds
 				 * which point to script file(s).
 				 * Force them to be restored afterwards */
-				struct parsefile *pf = g_parsefile;
-				while (pf) {
-					if (fd == pf->fd) {
-						i |= COPYFD_RESTORE;
-						break;
-					}
-					pf = pf->prev;
-				}
+				if (is_hidden_fd(sv, fd))
+					i |= COPYFD_RESTORE;
 			}
 			if (fd == 2)
 				copied_fd2 = i;
@@ -9026,7 +9049,7 @@ static int
 preadfd(void)
 {
 	int nr;
-	char *buf =  g_parsefile->buf;
+	char *buf = g_parsefile->buf;
 	parsenextc = buf;
 
 #if ENABLE_FEATURE_EDITING
-- 
cgit v1.2.3-55-g6feb