aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c40
-rw-r--r--shell/ash_test/ash-redir/redir3.right2
-rw-r--r--shell/ash_test/ash-redir/redir_exec2.right4
-rwxr-xr-xshell/ash_test/ash-redir/redir_exec2.tests10
-rw-r--r--shell/ash_test/ash-redir/redir_to_bad_fd255.right2
-rw-r--r--shell/ash_test/ash-redir/redir_to_bad_fd3.right2
-rw-r--r--shell/hush.c75
-rw-r--r--shell/hush_test/hush-redir/redir3.right3
-rw-r--r--shell/hush_test/hush-redir/redir_exec2.right4
-rwxr-xr-xshell/hush_test/hush-redir/redir_exec2.tests10
-rw-r--r--shell/hush_test/hush-redir/redir_to_bad_fd.right3
-rw-r--r--shell/hush_test/hush-redir/redir_to_bad_fd255.right3
-rw-r--r--shell/hush_test/hush-redir/redir_to_bad_fd3.right3
13 files changed, 119 insertions, 42 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 719f722a2..7a9f20ec0 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -758,10 +758,9 @@ static void trace_vprintf(const char *fmt, va_list va);
758#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) 758#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
759 759
760static int 760static int
761isdigit_str9(const char *str) 761isdigit_str(const char *str)
762{ 762{
763 int maxlen = 9 + 1; /* max 9 digits: 999999999 */ 763 while (isdigit(*str))
764 while (--maxlen && isdigit(*str))
765 str++; 764 str++;
766 return (*str == '\0'); 765 return (*str == '\0');
767} 766}
@@ -6386,10 +6385,13 @@ dup2_or_raise(int from, int to)
6386 newfd = (from != to) ? dup2(from, to) : to; 6385 newfd = (from != to) ? dup2(from, to) : to;
6387 if (newfd < 0) { 6386 if (newfd < 0) {
6388 /* Happens when source fd is not open: try "echo >&99" */ 6387 /* Happens when source fd is not open: try "echo >&99" */
6389 ash_msg_and_raise_perror("%d", from); 6388 ash_msg_and_raise_perror("dup2(%d,%d)", from, to);
6390 } 6389 }
6391 return newfd; 6390 return newfd;
6392} 6391}
6392/* The only possible error return is EBADF (fd wasn't open).
6393 * Transient errors retry, other errors raise exception.
6394 */
6393static int 6395static int
6394dup_CLOEXEC(int fd, int avoid_fd) 6396dup_CLOEXEC(int fd, int avoid_fd)
6395{ 6397{
@@ -6404,6 +6406,16 @@ dup_CLOEXEC(int fd, int avoid_fd)
6404 goto repeat; 6406 goto repeat;
6405 if (errno == EINTR) 6407 if (errno == EINTR)
6406 goto repeat; 6408 goto repeat;
6409 if (errno != EBADF) {
6410 /* "echo >&9999" gets EINVAL trying to save fd 1 to above 9999.
6411 * We could try saving it _below_ 9999 instead (how?), but
6412 * this probably means that dup2(9999,1) to effectuate >&9999
6413 * would also not work: fd 9999 can't exist. Gracefully bail out.
6414 * (This differs from "echo >&99" where saving works, but
6415 * subsequent dup2(99,1) fails if fd 99 is not open).
6416 */
6417 ash_msg_and_raise_perror("fcntl(%d,F_DUPFD,%d)", fd, avoid_fd + 1);
6418 }
6407 } 6419 }
6408 return newfd; 6420 return newfd;
6409} 6421}
@@ -6519,7 +6531,7 @@ save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
6519 new_fd = dup_CLOEXEC(fd, avoid_fd); 6531 new_fd = dup_CLOEXEC(fd, avoid_fd);
6520 sq->two_fd[i].moved_to = new_fd; 6532 sq->two_fd[i].moved_to = new_fd;
6521 TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd)); 6533 TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd));
6522 if (new_fd < 0) /* what? */ 6534 if (new_fd < 0) /* EBADF? what? */
6523 xfunc_die(); 6535 xfunc_die();
6524 return 0; /* "we did not close fd" */ 6536 return 0; /* "we did not close fd" */
6525 } 6537 }
@@ -6534,8 +6546,7 @@ save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
6534 new_fd = dup_CLOEXEC(fd, avoid_fd); 6546 new_fd = dup_CLOEXEC(fd, avoid_fd);
6535 TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd)); 6547 TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd));
6536 if (new_fd < 0) { 6548 if (new_fd < 0) {
6537 if (errno != EBADF) 6549 /* EBADF (fd is not open) */
6538 xfunc_die();
6539 /* new_fd = CLOSED; - already is -1 */ 6550 /* new_fd = CLOSED; - already is -1 */
6540 } 6551 }
6541 sq->two_fd[i].moved_to = new_fd; 6552 sq->two_fd[i].moved_to = new_fd;
@@ -10767,7 +10778,7 @@ expredir(union node *n)
10767 if (fn.list == NULL) 10778 if (fn.list == NULL)
10768 ash_msg_and_raise_error("redir error"); 10779 ash_msg_and_raise_error("redir error");
10769#if BASH_REDIR_OUTPUT 10780#if BASH_REDIR_OUTPUT
10770 if (!isdigit_str9(fn.list->text)) { 10781 if (!isdigit_str(fn.list->text)) {
10771 /* >&file, not >&fd */ 10782 /* >&file, not >&fd */
10772 if (redir->nfile.fd != 1) /* 123>&file - BAD */ 10783 if (redir->nfile.fd != 1) /* 123>&file - BAD */
10773 ash_msg_and_raise_error("redir error"); 10784 ash_msg_and_raise_error("redir error");
@@ -13284,12 +13295,19 @@ fixredir(union node *n, const char *text, int err)
13284 if (!err) 13295 if (!err)
13285 n->ndup.vname = NULL; 13296 n->ndup.vname = NULL;
13286 13297
13298 if (LONE_DASH(text)) {
13299 n->ndup.dupfd = -1;
13300 return;
13301 }
13302
13287 fd = bb_strtou(text, NULL, 10); 13303 fd = bb_strtou(text, NULL, 10);
13288 if (!errno && fd >= 0) 13304 if (!errno && fd >= 0)
13289 n->ndup.dupfd = fd; 13305 n->ndup.dupfd = fd;
13290 else if (LONE_DASH(text))
13291 n->ndup.dupfd = -1;
13292 else { 13306 else {
13307 /* This also fails on very large numbers
13308 * which overflow "int" - bb_strtou() does not
13309 * silently truncate results to word width.
13310 */
13293 if (err) 13311 if (err)
13294 raise_error_syntax("bad fd number"); 13312 raise_error_syntax("bad fd number");
13295 n->ndup.vname = makename(); 13313 n->ndup.vname = makename();
@@ -13975,7 +13993,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
13975 if ((c == '>' || c == '<' IF_BASH_REDIR_OUTPUT( || c == 0x100 + '>')) 13993 if ((c == '>' || c == '<' IF_BASH_REDIR_OUTPUT( || c == 0x100 + '>'))
13976 && quotef == 0 13994 && quotef == 0
13977 ) { 13995 ) {
13978 if (isdigit_str9(out)) { 13996 if (isdigit_str(out)) {
13979 PARSEREDIR(); /* passed as params: out, c */ 13997 PARSEREDIR(); /* passed as params: out, c */
13980 lasttoken = TREDIR; 13998 lasttoken = TREDIR;
13981 return lasttoken; 13999 return lasttoken;
diff --git a/shell/ash_test/ash-redir/redir3.right b/shell/ash_test/ash-redir/redir3.right
index fd641a8ea..d9cf5dac5 100644
--- a/shell/ash_test/ash-redir/redir3.right
+++ b/shell/ash_test/ash-redir/redir3.right
@@ -1,3 +1,3 @@
1TEST 1TEST
2./redir3.tests: line 4: 9: Bad file descriptor 2./redir3.tests: line 4: dup2(9,1): Bad file descriptor
3Output to fd#9: 1 3Output to fd#9: 1
diff --git a/shell/ash_test/ash-redir/redir_exec2.right b/shell/ash_test/ash-redir/redir_exec2.right
new file mode 100644
index 000000000..2bdc093c9
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_exec2.right
@@ -0,0 +1,4 @@
1fd/5
2fd/4
3fd/3
4One:1
diff --git a/shell/ash_test/ash-redir/redir_exec2.tests b/shell/ash_test/ash-redir/redir_exec2.tests
new file mode 100755
index 000000000..0af767592
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_exec2.tests
@@ -0,0 +1,10 @@
1cd /proc/$$
2exec 5>/dev/null
3exec 4>/dev/null
4exec 3>/dev/null
5ls -1 fd/5
6ls -1 fd/4
7ls -1 fd/3
8exec 5>&-
9test -e fd/5 && echo BUG
10echo One:$?
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd255.right b/shell/ash_test/ash-redir/redir_to_bad_fd255.right
index 9c5e35b36..ca4df91ac 100644
--- a/shell/ash_test/ash-redir/redir_to_bad_fd255.right
+++ b/shell/ash_test/ash-redir/redir_to_bad_fd255.right
@@ -1,2 +1,2 @@
1./redir_to_bad_fd255.tests: line 2: 255: Bad file descriptor 1./redir_to_bad_fd255.tests: line 2: dup2(255,1): Bad file descriptor
2OK 2OK
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd3.right b/shell/ash_test/ash-redir/redir_to_bad_fd3.right
index 895a4a0a6..5120322a5 100644
--- a/shell/ash_test/ash-redir/redir_to_bad_fd3.right
+++ b/shell/ash_test/ash-redir/redir_to_bad_fd3.right
@@ -1,2 +1,2 @@
1./redir_to_bad_fd3.tests: line 2: 3: Bad file descriptor 1./redir_to_bad_fd3.tests: line 2: dup2(3,1): Bad file descriptor
2OK 2OK
diff --git a/shell/hush.c b/shell/hush.c
index 3e6a13b32..707b77c9c 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -1575,12 +1575,22 @@ static int dup_CLOEXEC(int fd, int avoid_fd)
1575 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1); 1575 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
1576 if (newfd >= 0) { 1576 if (newfd >= 0) {
1577 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */ 1577 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
1578 fcntl(newfd, F_SETFD, FD_CLOEXEC); 1578 close_on_exec_on(newfd);
1579 } else { /* newfd < 0 */ 1579 } else { /* newfd < 0 */
1580 if (errno == EBUSY) 1580 if (errno == EBUSY)
1581 goto repeat; 1581 goto repeat;
1582 if (errno == EINTR) 1582 if (errno == EINTR)
1583 goto repeat; 1583 goto repeat;
1584 if (errno != EBADF) {
1585 /* "echo >&9999" gets EINVAL trying to save fd 1 to above 9999.
1586 * We could try saving it _below_ 9999 instead (how?), but
1587 * this probably means that dup2(9999,1) to effectuate >&9999
1588 * would also not work: fd 9999 can't exist.
1589 * (This differs from "echo >&99" where saving works, but
1590 * subsequent dup2(99,1) fails if fd 99 is not open).
1591 */
1592 bb_perror_msg("fcntl(%d,F_DUPFD,%d)", fd, avoid_fd + 1);
1593 }
1584 } 1594 }
1585 return newfd; 1595 return newfd;
1586} 1596}
@@ -1601,7 +1611,7 @@ static int xdup_CLOEXEC_and_close(int fd, int avoid_fd)
1601 xfunc_die(); 1611 xfunc_die();
1602 } 1612 }
1603 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */ 1613 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
1604 fcntl(newfd, F_SETFD, FD_CLOEXEC); 1614 close_on_exec_on(newfd);
1605 close(fd); 1615 close(fd);
1606 return newfd; 1616 return newfd;
1607} 1617}
@@ -7893,10 +7903,16 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
7893 if (sq) for (; sq[i].orig_fd >= 0; i++) { 7903 if (sq) for (; sq[i].orig_fd >= 0; i++) {
7894 /* If we collide with an already moved fd... */ 7904 /* If we collide with an already moved fd... */
7895 if (fd == sq[i].moved_to) { 7905 if (fd == sq[i].moved_to) {
7896 sq[i].moved_to = dup_CLOEXEC(sq[i].moved_to, avoid_fd); 7906 moved_to = dup_CLOEXEC(sq[i].moved_to, avoid_fd);
7897 debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to); 7907 debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, moved_to);
7898 if (sq[i].moved_to < 0) /* what? */ 7908 if (moved_to < 0) {
7899 xfunc_die(); 7909 /* "echo 2>/dev/tty 10>&9999" testcase:
7910 * We move fd 2 to 10, then discover we need to move fd 10
7911 * (and not hit 9999) and the latter fails.
7912 */
7913 return NULL; /* fcntl failed */
7914 }
7915 sq[i].moved_to = moved_to;
7900 return sq; 7916 return sq;
7901 } 7917 }
7902 if (fd == sq[i].orig_fd) { 7918 if (fd == sq[i].orig_fd) {
@@ -7910,7 +7926,7 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
7910 moved_to = dup_CLOEXEC(fd, avoid_fd); 7926 moved_to = dup_CLOEXEC(fd, avoid_fd);
7911 debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to); 7927 debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to);
7912 if (moved_to < 0 && errno != EBADF) 7928 if (moved_to < 0 && errno != EBADF)
7913 xfunc_die(); 7929 return NULL; /* fcntl failed (not because fd is closed) */
7914 return append_squirrel(sq, i, fd, moved_to); 7930 return append_squirrel(sq, i, fd, moved_to);
7915} 7931}
7916 7932
@@ -7943,6 +7959,8 @@ static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd)
7943 */ 7959 */
7944static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp) 7960static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
7945{ 7961{
7962 struct squirrel *new_squirrel;
7963
7946 if (avoid_fd < 9) /* the important case here is that it can be -1 */ 7964 if (avoid_fd < 9) /* the important case here is that it can be -1 */
7947 avoid_fd = 9; 7965 avoid_fd = 9;
7948 7966
@@ -8006,7 +8024,10 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
8006 } 8024 }
8007 8025
8008 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */ 8026 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
8009 *sqp = add_squirrel(*sqp, fd, avoid_fd); 8027 new_squirrel = add_squirrel(*sqp, fd, avoid_fd);
8028 if (!new_squirrel)
8029 return -1; /* redirect error */
8030 *sqp = new_squirrel;
8010 return 0; /* "we did not close fd" */ 8031 return 0; /* "we did not close fd" */
8011} 8032}
8012 8033
@@ -8077,8 +8098,11 @@ static int internally_opened_fd(int fd, struct squirrel *sq)
8077 return 0; 8098 return 0;
8078} 8099}
8079 8100
8080/* squirrel != NULL means we squirrel away copies of stdin, stdout, 8101/* sqp != NULL means we squirrel away copies of stdin, stdout,
8081 * and stderr if they are redirected. */ 8102 * and stderr if they are redirected.
8103 * If redirection fails, return 1. This will make caller
8104 * skip command execution and restore already created redirect fds.
8105 */
8082static int setup_redirects(struct command *prog, struct squirrel **sqp) 8106static int setup_redirects(struct command *prog, struct squirrel **sqp)
8083{ 8107{
8084 struct redir_struct *redir; 8108 struct redir_struct *redir;
@@ -8089,7 +8113,8 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
8089 8113
8090 if (redir->rd_type == REDIRECT_HEREDOC2) { 8114 if (redir->rd_type == REDIRECT_HEREDOC2) {
8091 /* "rd_fd<<HERE" case */ 8115 /* "rd_fd<<HERE" case */
8092 save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp); 8116 if (save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp) < 0)
8117 return 1;
8093 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_ 8118 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_
8094 * of the heredoc */ 8119 * of the heredoc */
8095 debug_printf_redir("set heredoc '%s'\n", 8120 debug_printf_redir("set heredoc '%s'\n",
@@ -8109,7 +8134,7 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
8109 * "cmd > <file" (2nd redirect starts too early) 8134 * "cmd > <file" (2nd redirect starts too early)
8110 */ 8135 */
8111 syntax_error("invalid redirect"); 8136 syntax_error("invalid redirect");
8112 continue; 8137 return 1;
8113 } 8138 }
8114 mode = redir_table[redir->rd_type].mode; 8139 mode = redir_table[redir->rd_type].mode;
8115 p = expand_string_to_string(redir->rd_filename, 8140 p = expand_string_to_string(redir->rd_filename,
@@ -8124,7 +8149,9 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
8124 */ 8149 */
8125 return 1; 8150 return 1;
8126 } 8151 }
8127 if (newfd == redir->rd_fd && sqp) { 8152 if (newfd == redir->rd_fd && sqp
8153 && sqp != ERR_PTR /* not a redirect in "exec" */
8154 ) {
8128 /* open() gave us precisely the fd we wanted. 8155 /* open() gave us precisely the fd we wanted.
8129 * This means that this fd was not busy 8156 * This means that this fd was not busy
8130 * (not opened to anywhere). 8157 * (not opened to anywhere).
@@ -8146,6 +8173,8 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
8146 /* if "N>&-": close redir->rd_fd (newfd is REDIRFD_CLOSE) */ 8173 /* if "N>&-": close redir->rd_fd (newfd is REDIRFD_CLOSE) */
8147 8174
8148 closed = save_fd_on_redirect(redir->rd_fd, /*avoid:*/ newfd, sqp); 8175 closed = save_fd_on_redirect(redir->rd_fd, /*avoid:*/ newfd, sqp);
8176 if (closed < 0)
8177 return 1; /* error */
8149 if (newfd == REDIRFD_CLOSE) { 8178 if (newfd == REDIRFD_CLOSE) {
8150 /* "N>&-" means "close me" */ 8179 /* "N>&-" means "close me" */
8151 if (!closed) { 8180 if (!closed) {
@@ -8159,13 +8188,16 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
8159 * and second redirect closes 3! Restore code then closes 3 again. 8188 * and second redirect closes 3! Restore code then closes 3 again.
8160 */ 8189 */
8161 } else { 8190 } else {
8162 /* if newfd is a script fd or saved fd, simulate EBADF */ 8191 /* if newfd is a script fd or saved fd, do not allow to use it */
8163 if (internally_opened_fd(newfd, sqp && sqp != ERR_PTR ? *sqp : NULL)) { 8192 if (internally_opened_fd(newfd, sqp && sqp != ERR_PTR ? *sqp : NULL)) {
8164 //errno = EBADF; 8193 bb_error_msg("fd#%d is not open", newfd);
8165 //bb_perror_msg_and_die("can't duplicate file descriptor"); 8194 return 1;
8166 newfd = -1; /* same effect as code above */ 8195 }
8196 if (dup2(newfd, redir->rd_fd) < 0) {
8197 /* "echo >&99" testcase */
8198 bb_perror_msg("dup2(%d,%d)", newfd, redir->rd_fd);
8199 return 1;
8167 } 8200 }
8168 xdup2(newfd, redir->rd_fd);
8169 if (redir->rd_dup == REDIRFD_TO_FILE) 8201 if (redir->rd_dup == REDIRFD_TO_FILE)
8170 /* "rd_fd > FILE" */ 8202 /* "rd_fd > FILE" */
8171 close(newfd); 8203 close(newfd);
@@ -10710,7 +10742,7 @@ int hush_main(int argc, char **argv)
10710 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254); 10742 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
10711 if (G_interactive_fd < 0) { 10743 if (G_interactive_fd < 0) {
10712 /* try to dup to any fd */ 10744 /* try to dup to any fd */
10713 G_interactive_fd = dup(STDIN_FILENO); 10745 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
10714 if (G_interactive_fd < 0) { 10746 if (G_interactive_fd < 0) {
10715 /* give up */ 10747 /* give up */
10716 G_interactive_fd = 0; 10748 G_interactive_fd = 0;
@@ -10720,8 +10752,6 @@ int hush_main(int argc, char **argv)
10720 } 10752 }
10721 debug_printf("interactive_fd:%d\n", G_interactive_fd); 10753 debug_printf("interactive_fd:%d\n", G_interactive_fd);
10722 if (G_interactive_fd) { 10754 if (G_interactive_fd) {
10723 close_on_exec_on(G_interactive_fd);
10724
10725 if (G_saved_tty_pgrp) { 10755 if (G_saved_tty_pgrp) {
10726 /* If we were run as 'hush &', sleep until we are 10756 /* If we were run as 'hush &', sleep until we are
10727 * in the foreground (tty pgrp == our pgrp). 10757 * in the foreground (tty pgrp == our pgrp).
@@ -10796,9 +10826,6 @@ int hush_main(int argc, char **argv)
10796 G_interactive_fd = 0; 10826 G_interactive_fd = 0;
10797 } 10827 }
10798 } 10828 }
10799 if (G_interactive_fd) {
10800 close_on_exec_on(G_interactive_fd);
10801 }
10802 install_special_sighandlers(); 10829 install_special_sighandlers();
10803#else 10830#else
10804 /* We have interactiveness code disabled */ 10831 /* We have interactiveness code disabled */
diff --git a/shell/hush_test/hush-redir/redir3.right b/shell/hush_test/hush-redir/redir3.right
index e3c878b7d..d49237c42 100644
--- a/shell/hush_test/hush-redir/redir3.right
+++ b/shell/hush_test/hush-redir/redir3.right
@@ -1,2 +1,3 @@
1TEST 1TEST
2hush: can't duplicate file descriptor: Bad file descriptor 2hush: dup2(9,1): Bad file descriptor
3Output to fd#9: 1
diff --git a/shell/hush_test/hush-redir/redir_exec2.right b/shell/hush_test/hush-redir/redir_exec2.right
new file mode 100644
index 000000000..2bdc093c9
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_exec2.right
@@ -0,0 +1,4 @@
1fd/5
2fd/4
3fd/3
4One:1
diff --git a/shell/hush_test/hush-redir/redir_exec2.tests b/shell/hush_test/hush-redir/redir_exec2.tests
new file mode 100755
index 000000000..0af767592
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_exec2.tests
@@ -0,0 +1,10 @@
1cd /proc/$$
2exec 5>/dev/null
3exec 4>/dev/null
4exec 3>/dev/null
5ls -1 fd/5
6ls -1 fd/4
7ls -1 fd/3
8exec 5>&-
9test -e fd/5 && echo BUG
10echo One:$?
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd.right b/shell/hush_test/hush-redir/redir_to_bad_fd.right
index 936911ce5..4fc51cc93 100644
--- a/shell/hush_test/hush-redir/redir_to_bad_fd.right
+++ b/shell/hush_test/hush-redir/redir_to_bad_fd.right
@@ -1 +1,2 @@
1hush: can't duplicate file descriptor: Bad file descriptor 1hush: dup2(10,1): Bad file descriptor
2OK
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd255.right b/shell/hush_test/hush-redir/redir_to_bad_fd255.right
index 936911ce5..baa2959ca 100644
--- a/shell/hush_test/hush-redir/redir_to_bad_fd255.right
+++ b/shell/hush_test/hush-redir/redir_to_bad_fd255.right
@@ -1 +1,2 @@
1hush: can't duplicate file descriptor: Bad file descriptor 1hush: dup2(255,1): Bad file descriptor
2OK
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd3.right b/shell/hush_test/hush-redir/redir_to_bad_fd3.right
index 936911ce5..5e54abc0a 100644
--- a/shell/hush_test/hush-redir/redir_to_bad_fd3.right
+++ b/shell/hush_test/hush-redir/redir_to_bad_fd3.right
@@ -1 +1,2 @@
1hush: can't duplicate file descriptor: Bad file descriptor 1hush: fd#3 is not open
2OK