aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell/ash.c332
-rwxr-xr-xshell/ash_test/ash-redir/redir_script.tests9
-rw-r--r--shell/ash_test/ash-redir/redir_to_bad_fd255.right2
-rwxr-xr-xshell/ash_test/ash-redir/redir_to_bad_fd255.tests3
-rw-r--r--shell/ash_test/ash-redir/redir_to_bad_fd3.right2
-rwxr-xr-xshell/ash_test/ash-redir/redir_to_bad_fd3.tests3
-rwxr-xr-xshell/hush_test/hush-redir/redir_script.tests9
-rw-r--r--shell/hush_test/hush-redir/redir_to_bad_fd255.right1
-rwxr-xr-xshell/hush_test/hush-redir/redir_to_bad_fd255.tests3
-rw-r--r--shell/hush_test/hush-redir/redir_to_bad_fd3.right1
-rwxr-xr-xshell/hush_test/hush-redir/redir_to_bad_fd3.tests3
11 files changed, 225 insertions, 143 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 1e720aec4..ac676c2dc 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -5192,7 +5192,7 @@ stoppedjobs(void)
5192#undef EMPTY 5192#undef EMPTY
5193#undef CLOSED 5193#undef CLOSED
5194#define EMPTY -2 /* marks an unused slot in redirtab */ 5194#define EMPTY -2 /* marks an unused slot in redirtab */
5195#define CLOSED -3 /* marks a slot of previously-closed fd */ 5195#define CLOSED -1 /* marks a slot of previously-closed fd */
5196 5196
5197/* 5197/*
5198 * Handle here documents. Normally we fork off a process to write the 5198 * Handle here documents. Normally we fork off a process to write the
@@ -5349,74 +5349,158 @@ dup2_or_raise(int from, int to)
5349 } 5349 }
5350 return newfd; 5350 return newfd;
5351} 5351}
5352static int
5353fcntl_F_DUPFD(int fd, int avoid_fd)
5354{
5355 int newfd;
5356 repeat:
5357 newfd = fcntl(fd, F_DUPFD, avoid_fd + 1);
5358 if (newfd < 0) {
5359 if (errno == EBUSY)
5360 goto repeat;
5361 if (errno == EINTR)
5362 goto repeat;
5363 }
5364 return newfd;
5365}
5366static int
5367xdup_CLOEXEC_and_close(int fd, int avoid_fd)
5368{
5369 int newfd;
5370 repeat:
5371 newfd = fcntl(fd, F_DUPFD, avoid_fd + 1);
5372 if (newfd < 0) {
5373 if (errno == EBUSY)
5374 goto repeat;
5375 if (errno == EINTR)
5376 goto repeat;
5377 /* fd was not open? */
5378 if (errno == EBADF)
5379 return fd;
5380 ash_msg_and_raise_perror("%d", newfd);
5381 }
5382 fcntl(newfd, F_SETFD, FD_CLOEXEC);
5383 close(fd);
5384 return newfd;
5385}
5352 5386
5353/* Struct def and variable are moved down to the first usage site */ 5387/* Struct def and variable are moved down to the first usage site */
5354struct two_fd_t { 5388struct squirrel {
5355 int orig, copy; 5389 int orig_fd;
5390 int moved_to;
5356}; 5391};
5357struct redirtab { 5392struct redirtab {
5358 struct redirtab *next; 5393 struct redirtab *next;
5359 int pair_count; 5394 int pair_count;
5360 struct two_fd_t two_fd[]; 5395 struct squirrel two_fd[];
5361}; 5396};
5362#define redirlist (G_var.redirlist) 5397#define redirlist (G_var.redirlist)
5363enum {
5364 COPYFD_RESTORE = (int)~(INT_MAX),
5365};
5366 5398
5367static int 5399static void
5368need_to_remember(struct redirtab *rp, int fd) 5400add_squirrel_closed(struct redirtab *sq, int fd)
5369{ 5401{
5370 int i; 5402 int i;
5371 5403
5372 if (!rp) /* remembering was not requested */ 5404 if (!sq)
5373 return 0; 5405 return;
5374 5406
5375 for (i = 0; i < rp->pair_count; i++) { 5407 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
5376 if (rp->two_fd[i].orig == fd) { 5408 /* If we collide with an already moved fd... */
5377 /* already remembered */ 5409 if (fd == sq->two_fd[i].orig_fd) {
5378 return 0; 5410 /* Examples:
5411 * "echo 3>FILE 3>&- 3>FILE"
5412 * "echo 3>&- 3>FILE"
5413 * No need for last redirect to insert
5414 * another "need to close 3" indicator.
5415 */
5416 TRACE(("redirect_fd %d: already moved or closed\n", fd));
5417 return;
5379 } 5418 }
5380 } 5419 }
5381 return 1; 5420 TRACE(("redirect_fd %d: previous fd was closed\n", fd));
5421 sq->two_fd[i].orig_fd = fd;
5422 sq->two_fd[i].moved_to = CLOSED;
5382} 5423}
5383 5424
5384/* "hidden" fd is a fd used to read scripts, or a copy of such */
5385static int 5425static int
5386is_hidden_fd(struct redirtab *rp, int fd) 5426save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
5387{ 5427{
5388 int i; 5428 int i, new_fd;
5389 struct parsefile *pf; 5429
5430 if (avoid_fd < 9) /* the important case here is that it can be -1 */
5431 avoid_fd = 9;
5390 5432
5391 if (fd == -1) 5433#if JOBS
5434 if (fd == ttyfd) {
5435 /* Testcase: "ls -l /proc/$$/fd 10>&-" should work */
5436 ttyfd = xdup_CLOEXEC_and_close(ttyfd, avoid_fd);
5437 TRACE(("redirect_fd %d: matches ttyfd, moving it to %d\n", fd, ttyfd));
5438 return 1; /* "we closed fd" */
5439 }
5440#endif
5441 /* Are we called from redirect(0)? E.g. redirect
5442 * in a forked child. No need to save fds,
5443 * we aren't going to use them anymore, ok to trash.
5444 */
5445 if (!sq)
5392 return 0; 5446 return 0;
5393 /* Check open scripts' fds */ 5447
5394 pf = g_parsefile; 5448 /* If this one of script's fds? */
5395 while (pf) { 5449 if (fd != 0) {
5396 /* We skip pf_fd == 0 case because of the following case: 5450 struct parsefile *pf = g_parsefile;
5397 * $ ash # running ash interactively 5451 while (pf) {
5398 * $ . ./script.sh 5452 /* We skip fd == 0 case because of the following:
5399 * and in script.sh: "exec 9>&0". 5453 * $ ash # running ash interactively
5400 * Even though top-level pf_fd _is_ 0, 5454 * $ . ./script.sh
5401 * it's still ok to use it: "read" builtin uses it, 5455 * and in script.sh: "exec 9>&0".
5402 * why should we cripple "exec" builtin? 5456 * Even though top-level pf_fd _is_ 0,
5403 */ 5457 * it's still ok to use it: "read" builtin uses it,
5404 if (pf->pf_fd > 0 && fd == pf->pf_fd) { 5458 * why should we cripple "exec" builtin?
5405 return 1; 5459 */
5460 if (fd == pf->pf_fd) {
5461 pf->pf_fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
5462 return 1; /* "we closed fd" */
5463 }
5464 pf = pf->prev;
5406 } 5465 }
5407 pf = pf->prev;
5408 } 5466 }
5409 5467
5410 if (!rp) 5468 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
5411 return 0; 5469
5412 /* Check saved fds of redirects */ 5470 /* First: do we collide with some already moved fds? */
5413 fd |= COPYFD_RESTORE; 5471 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
5414 for (i = 0; i < rp->pair_count; i++) { 5472 /* If we collide with an already moved fd... */
5415 if (rp->two_fd[i].copy == fd) { 5473 if (fd == sq->two_fd[i].moved_to) {
5416 return 1; 5474 new_fd = fcntl_F_DUPFD(fd, avoid_fd);
5475 sq->two_fd[i].moved_to = new_fd;
5476 TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd));
5477 if (new_fd < 0) /* what? */
5478 xfunc_die();
5479 return 0; /* "we did not close fd" */
5480 }
5481 if (fd == sq->two_fd[i].orig_fd) {
5482 /* Example: echo Hello >/dev/null 1>&2 */
5483 TRACE(("redirect_fd %d: already moved\n", fd));
5484 return 0; /* "we did not close fd" */
5417 } 5485 }
5418 } 5486 }
5419 return 0; 5487
5488 /* If this fd is open, we move and remember it; if it's closed, new_fd = CLOSED (-1) */
5489 new_fd = fcntl_F_DUPFD(fd, avoid_fd);
5490 TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd));
5491 if (new_fd < 0) {
5492 if (errno != EBADF)
5493 xfunc_die();
5494 /* new_fd = CLOSED; - already is -1 */
5495 }
5496 sq->two_fd[i].moved_to = new_fd;
5497 sq->two_fd[i].orig_fd = fd;
5498
5499 /* if we move stderr, let "set -x" code know */
5500 if (fd == preverrout_fd)
5501 preverrout_fd = new_fd;
5502
5503 return 0; /* "we did not close fd" */
5420} 5504}
5421 5505
5422/* 5506/*
@@ -5431,109 +5515,80 @@ redirect(union node *redir, int flags)
5431{ 5515{
5432 struct redirtab *sv; 5516 struct redirtab *sv;
5433 int sv_pos; 5517 int sv_pos;
5434 int fd;
5435 int newfd;
5436 5518
5437 if (!redir) 5519 if (!redir)
5438 return; 5520 return;
5521
5522 sv_pos = 0;
5439 sv = NULL; 5523 sv = NULL;
5440 INT_OFF; 5524 INT_OFF;
5441 if (flags & REDIR_PUSH) 5525 if (flags & REDIR_PUSH)
5442 sv = redirlist; 5526 sv = redirlist;
5443 sv_pos = 0;
5444 do { 5527 do {
5445 int right_fd = -1; 5528 int fd;
5529 int newfd;
5530 int close_fd;
5531 int closed;
5532
5446 fd = redir->nfile.fd; 5533 fd = redir->nfile.fd;
5447 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { 5534 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
5448 right_fd = redir->ndup.dupfd; 5535 //bb_error_msg("doing %d > %d", fd, newfd);
5449 //bb_error_msg("doing %d > %d", fd, right_fd); 5536 newfd = redir->ndup.dupfd;
5450 /* redirect from/to same file descriptor? */ 5537 close_fd = -1;
5451 if (right_fd == fd)
5452 continue;
5453 /* "echo >&10" and 10 is a fd opened to a sh script? */
5454 if (is_hidden_fd(sv, right_fd)) {
5455 errno = EBADF; /* as if it is closed */
5456 ash_msg_and_raise_perror("%d", right_fd);
5457 }
5458 newfd = -1;
5459 } else { 5538 } else {
5460 newfd = openredirect(redir); /* always >= 0 */ 5539 newfd = openredirect(redir); /* always >= 0 */
5461 if (fd == newfd) { 5540 if (fd == newfd) {
5462 /* Descriptor wasn't open before redirect. 5541 /* open() gave us precisely the fd we wanted.
5463 * Mark it for close in the future */ 5542 * This means that this fd was not busy
5464 if (need_to_remember(sv, fd)) { 5543 * (not opened to anywhere).
5465 goto remember_to_close; 5544 * Remember to close it on restore:
5466 } 5545 */
5546 add_squirrel_closed(sv, fd);
5467 continue; 5547 continue;
5468 } 5548 }
5549 close_fd = newfd;
5469 } 5550 }
5470#if BASH_REDIR_OUTPUT 5551
5471 redirect_more: 5552 if (fd == newfd)
5472#endif 5553 continue;
5473 if (need_to_remember(sv, fd)) { 5554
5474 /* Copy old descriptor */ 5555 /* if "N>FILE": move newfd to fd */
5475 /* Careful to not accidentally "save" 5556 /* if "N>&M": dup newfd to fd */
5476 * to the same fd as right side fd in N>&M */ 5557 /* if "N>&-": close fd (newfd is -1) */
5477 int minfd = right_fd < 10 ? 10 : right_fd + 1; 5558
5478#if defined(F_DUPFD_CLOEXEC) 5559 IF_BASH_REDIR_OUTPUT(redirect_more:)
5479 int copy = fcntl(fd, F_DUPFD_CLOEXEC, minfd); 5560
5480#else 5561 closed = save_fd_on_redirect(fd, /*avoid:*/ newfd, sv);
5481 int copy = fcntl(fd, F_DUPFD, minfd); 5562 if (newfd == -1) {
5482#endif 5563 /* "N>&-" means "close me" */
5483 if (copy == -1) { 5564 if (!closed) {
5484 int e = errno; 5565 /* ^^^ optimization: saving may already
5485 if (e != EBADF) { 5566 * have closed it. If not... */
5486 /* Strange error (e.g. "too many files" EMFILE?) */ 5567 close(fd);
5487 if (newfd >= 0)
5488 close(newfd);
5489 errno = e;
5490 ash_msg_and_raise_perror("%d", fd);
5491 /* NOTREACHED */
5492 }
5493 /* EBADF: it is not open - good, remember to close it */
5494 remember_to_close:
5495 copy = CLOSED;
5496 } else { /* fd is open, save its copy */
5497#if !defined(F_DUPFD_CLOEXEC)
5498 fcntl(copy, F_SETFD, FD_CLOEXEC);
5499#endif
5500 /* "exec fd>&-" should not close fds
5501 * which point to script file(s).
5502 * Force them to be restored afterwards */
5503 if (is_hidden_fd(sv, fd))
5504 copy |= COPYFD_RESTORE;
5505 }
5506 /* if we move stderr, let "set -x" code know */
5507 if (fd == preverrout_fd)
5508 preverrout_fd = copy;
5509 sv->two_fd[sv_pos].orig = fd;
5510 sv->two_fd[sv_pos].copy = copy;
5511 sv_pos++;
5512 }
5513 if (newfd < 0) {
5514 /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
5515 if (redir->ndup.dupfd < 0) { /* "fd>&-" */
5516 /* Don't want to trigger debugging */
5517 if (fd != -1)
5518 close(fd);
5519 } else {
5520 dup2_or_raise(redir->ndup.dupfd, fd);
5521 } 5568 }
5522 } else if (fd != newfd) { /* move newfd to fd */ 5569 } else {
5570///TODO: if _newfd_ is a script fd or saved fd, then simulate EBADF!
5571//if (newfd == ttyfd) {
5572// errno = EBADF;
5573// ash_msg_and_raise_perror("A %d", newfd);
5574//}
5575//if (newfd == g_parsefile->pf_fd) {
5576// errno = EBADF;
5577// ash_msg_and_raise_perror("B %d", newfd);
5578//}
5523 dup2_or_raise(newfd, fd); 5579 dup2_or_raise(newfd, fd);
5580 if (close_fd >= 0) /* "N>FILE" or ">&FILE" or heredoc? */
5581 close(close_fd);
5524#if BASH_REDIR_OUTPUT 5582#if BASH_REDIR_OUTPUT
5525 if (!(redir->nfile.type == NTO2 && fd == 2)) 5583 if (redir->nfile.type == NTO2 && fd == 1) {
5584 /* ">&FILE". we already redirected to 1, now copy 1 to 2 */
5585 fd = 2;
5586 newfd = 1;
5587 close_fd = -1;
5588 goto redirect_more;
5589 }
5526#endif 5590#endif
5527 close(newfd);
5528 } 5591 }
5529#if BASH_REDIR_OUTPUT
5530 if (redir->nfile.type == NTO2 && fd == 1) {
5531 /* We already redirected it to fd 1, now copy it to 2 */
5532 newfd = 1;
5533 fd = 2;
5534 goto redirect_more;
5535 }
5536#endif
5537 } while ((redir = redir->nfile.next) != NULL); 5592 } while ((redir = redir->nfile.next) != NULL);
5538 INT_ON; 5593 INT_ON;
5539 5594
@@ -5542,7 +5597,7 @@ redirect(union node *redir, int flags)
5542 // dash has a bug: since REDIR_SAVEFD2=3 and REDIR_PUSH=1, this test 5597 // dash has a bug: since REDIR_SAVEFD2=3 and REDIR_PUSH=1, this test
5543 // triggers for pure REDIR_PUSH too. Thus, this is done almost always, 5598 // triggers for pure REDIR_PUSH too. Thus, this is done almost always,
5544 // not only for calls with flags containing REDIR_SAVEFD2. 5599 // not only for calls with flags containing REDIR_SAVEFD2.
5545 // We do this unconditionally (see code above). 5600 // We do this unconditionally (see save_fd_on_redirect()).
5546 //if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0) 5601 //if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5547 // preverrout_fd = copied_fd2; 5602 // preverrout_fd = copied_fd2;
5548} 5603}
@@ -5557,7 +5612,7 @@ redirectsafe(union node *redir, int flags)
5557 5612
5558 SAVE_INT(saveint); 5613 SAVE_INT(saveint);
5559 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */ 5614 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
5560 err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2; 5615 err = setjmp(jmploc.loc); /* was = setjmp(jmploc.loc) * 2; */
5561 if (!err) { 5616 if (!err) {
5562 exception_handler = &jmploc; 5617 exception_handler = &jmploc;
5563 redirect(redir, flags); 5618 redirect(redir, flags);
@@ -5591,7 +5646,7 @@ pushredir(union node *redir)
5591 sv = ckzalloc(sizeof(*sv) + i * sizeof(sv->two_fd[0])); 5646 sv = ckzalloc(sizeof(*sv) + i * sizeof(sv->two_fd[0]));
5592 sv->pair_count = i; 5647 sv->pair_count = i;
5593 while (--i >= 0) 5648 while (--i >= 0)
5594 sv->two_fd[i].orig = sv->two_fd[i].copy = EMPTY; 5649 sv->two_fd[i].orig_fd = sv->two_fd[i].moved_to = EMPTY;
5595 sv->next = redirlist; 5650 sv->next = redirlist;
5596 redirlist = sv; 5651 redirlist = sv;
5597 return sv->next; 5652 return sv->next;
@@ -5601,7 +5656,7 @@ pushredir(union node *redir)
5601 * Undo the effects of the last redirection. 5656 * Undo the effects of the last redirection.
5602 */ 5657 */
5603static void 5658static void
5604popredir(int drop, int restore) 5659popredir(int drop)
5605{ 5660{
5606 struct redirtab *rp; 5661 struct redirtab *rp;
5607 int i; 5662 int i;
@@ -5611,20 +5666,19 @@ popredir(int drop, int restore)
5611 INT_OFF; 5666 INT_OFF;
5612 rp = redirlist; 5667 rp = redirlist;
5613 for (i = 0; i < rp->pair_count; i++) { 5668 for (i = 0; i < rp->pair_count; i++) {
5614 int fd = rp->two_fd[i].orig; 5669 int fd = rp->two_fd[i].orig_fd;
5615 int copy = rp->two_fd[i].copy; 5670 int copy = rp->two_fd[i].moved_to;
5616 if (copy == CLOSED) { 5671 if (copy == CLOSED) {
5617 if (!drop) 5672 if (!drop)
5618 close(fd); 5673 close(fd);
5619 continue; 5674 continue;
5620 } 5675 }
5621 if (copy != EMPTY) { 5676 if (copy != EMPTY) {
5622 if (!drop || (restore && (copy & COPYFD_RESTORE))) { 5677 if (!drop) {
5623 copy &= ~COPYFD_RESTORE;
5624 /*close(fd);*/ 5678 /*close(fd);*/
5625 dup2_or_raise(copy, fd); 5679 dup2_or_raise(copy, fd);
5626 } 5680 }
5627 close(copy & ~COPYFD_RESTORE); 5681 close(copy);
5628 } 5682 }
5629 } 5683 }
5630 redirlist = rp->next; 5684 redirlist = rp->next;
@@ -5636,7 +5690,7 @@ static void
5636unwindredir(struct redirtab *stop) 5690unwindredir(struct redirtab *stop)
5637{ 5691{
5638 while (redirlist != stop) 5692 while (redirlist != stop)
5639 popredir(/*drop:*/ 0, /*restore:*/ 0); 5693 popredir(/*drop:*/ 0);
5640} 5694}
5641 5695
5642 5696
@@ -7715,7 +7769,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
7715 clearenv(); 7769 clearenv();
7716 while (*envp) 7770 while (*envp)
7717 putenv(*envp++); 7771 putenv(*envp++);
7718 popredir(/*drop:*/ 1, /*restore:*/ 0); 7772 popredir(/*drop:*/ 1);
7719 run_applet_no_and_exit(applet_no, cmd, argv); 7773 run_applet_no_and_exit(applet_no, cmd, argv);
7720 } 7774 }
7721 /* re-exec ourselves with the new arguments */ 7775 /* re-exec ourselves with the new arguments */
@@ -8754,7 +8808,7 @@ evaltree(union node *n, int flags)
8754 status = evaltree(n->nredir.n, flags & EV_TESTED); 8808 status = evaltree(n->nredir.n, flags & EV_TESTED);
8755 } 8809 }
8756 if (n->nredir.redirect) 8810 if (n->nredir.redirect)
8757 popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */); 8811 popredir(/*drop:*/ 0);
8758 goto setstatus; 8812 goto setstatus;
8759 case NCMD: 8813 case NCMD:
8760 evalfn = evalcommand; 8814 evalfn = evalcommand;
@@ -9902,7 +9956,7 @@ evalcommand(union node *cmd, int flags)
9902 9956
9903 out: 9957 out:
9904 if (cmd->ncmd.redirect) 9958 if (cmd->ncmd.redirect)
9905 popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec); 9959 popredir(/*drop:*/ cmd_is_exec);
9906 unwindredir(redir_stop); 9960 unwindredir(redir_stop);
9907 unwindlocalvars(localvar_stop); 9961 unwindlocalvars(localvar_stop);
9908 if (lastarg) { 9962 if (lastarg) {
@@ -13727,7 +13781,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
13727 * Testcase: ash -c 'exec 1>&0' must not complain. */ 13781 * Testcase: ash -c 'exec 1>&0' must not complain. */
13728 // if (!sflag) g_parsefile->pf_fd = -1; 13782 // if (!sflag) g_parsefile->pf_fd = -1;
13729 // ^^ not necessary since now we special-case fd 0 13783 // ^^ not necessary since now we special-case fd 0
13730 // in is_hidden_fd() to not be considered "hidden fd" 13784 // in save_fd_on_redirect()
13731 evalstring(minusc, sflag ? 0 : EV_EXIT); 13785 evalstring(minusc, sflag ? 0 : EV_EXIT);
13732 } 13786 }
13733 13787
diff --git a/shell/ash_test/ash-redir/redir_script.tests b/shell/ash_test/ash-redir/redir_script.tests
index ccc497d7b..740daa461 100755
--- a/shell/ash_test/ash-redir/redir_script.tests
+++ b/shell/ash_test/ash-redir/redir_script.tests
@@ -20,10 +20,15 @@ eval "find_fds $fds"
20 20
21# Shell should not lose that fd. Did it? 21# Shell should not lose that fd. Did it?
22find_fds 22find_fds
23test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; } 23test x"$fds1" = x"$fds" \
24&& { echo "Ok: script fd is not closed"; exit 0; }
25
26# One legit way to handle it is to move script fd. For example, if we see that fd 10 moved to fd 11:
27test x"$fds1" = x" 10>&- 3>&-" && \
28test x"$fds" = x" 11>&- 3>&-" \
29&& { echo "Ok: script fd is not closed"; exit 0; }
24 30
25echo "Bug: script fd is closed" 31echo "Bug: script fd is closed"
26echo "fds1:$fds1" 32echo "fds1:$fds1"
27echo "fds2:$fds" 33echo "fds2:$fds"
28exit 1 34exit 1
29
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd255.right b/shell/ash_test/ash-redir/redir_to_bad_fd255.right
new file mode 100644
index 000000000..9c5e35b36
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_to_bad_fd255.right
@@ -0,0 +1,2 @@
1./redir_to_bad_fd255.tests: line 2: 255: Bad file descriptor
2OK
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd255.tests b/shell/ash_test/ash-redir/redir_to_bad_fd255.tests
new file mode 100755
index 000000000..2266af6da
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_to_bad_fd255.tests
@@ -0,0 +1,3 @@
1# ash uses fd 10 (usually) for reading the script
2echo LOST >&255
3echo OK
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd3.right b/shell/ash_test/ash-redir/redir_to_bad_fd3.right
new file mode 100644
index 000000000..895a4a0a6
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_to_bad_fd3.right
@@ -0,0 +1,2 @@
1./redir_to_bad_fd3.tests: line 2: 3: Bad file descriptor
2OK
diff --git a/shell/ash_test/ash-redir/redir_to_bad_fd3.tests b/shell/ash_test/ash-redir/redir_to_bad_fd3.tests
new file mode 100755
index 000000000..98c54cfc6
--- /dev/null
+++ b/shell/ash_test/ash-redir/redir_to_bad_fd3.tests
@@ -0,0 +1,3 @@
1# ash uses fd 10 (usually) for reading the script
2echo LOST >&3
3echo OK
diff --git a/shell/hush_test/hush-redir/redir_script.tests b/shell/hush_test/hush-redir/redir_script.tests
index ccc497d7b..740daa461 100755
--- a/shell/hush_test/hush-redir/redir_script.tests
+++ b/shell/hush_test/hush-redir/redir_script.tests
@@ -20,10 +20,15 @@ eval "find_fds $fds"
20 20
21# Shell should not lose that fd. Did it? 21# Shell should not lose that fd. Did it?
22find_fds 22find_fds
23test x"$fds1" = x"$fds" && { echo "Ok: script fd is not closed"; exit 0; } 23test x"$fds1" = x"$fds" \
24&& { echo "Ok: script fd is not closed"; exit 0; }
25
26# One legit way to handle it is to move script fd. For example, if we see that fd 10 moved to fd 11:
27test x"$fds1" = x" 10>&- 3>&-" && \
28test x"$fds" = x" 11>&- 3>&-" \
29&& { echo "Ok: script fd is not closed"; exit 0; }
24 30
25echo "Bug: script fd is closed" 31echo "Bug: script fd is closed"
26echo "fds1:$fds1" 32echo "fds1:$fds1"
27echo "fds2:$fds" 33echo "fds2:$fds"
28exit 1 34exit 1
29
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd255.right b/shell/hush_test/hush-redir/redir_to_bad_fd255.right
new file mode 100644
index 000000000..936911ce5
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_to_bad_fd255.right
@@ -0,0 +1 @@
hush: can't duplicate file descriptor: Bad file descriptor
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd255.tests b/shell/hush_test/hush-redir/redir_to_bad_fd255.tests
new file mode 100755
index 000000000..2266af6da
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_to_bad_fd255.tests
@@ -0,0 +1,3 @@
1# ash uses fd 10 (usually) for reading the script
2echo LOST >&255
3echo OK
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd3.right b/shell/hush_test/hush-redir/redir_to_bad_fd3.right
new file mode 100644
index 000000000..936911ce5
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_to_bad_fd3.right
@@ -0,0 +1 @@
hush: can't duplicate file descriptor: Bad file descriptor
diff --git a/shell/hush_test/hush-redir/redir_to_bad_fd3.tests b/shell/hush_test/hush-redir/redir_to_bad_fd3.tests
new file mode 100755
index 000000000..98c54cfc6
--- /dev/null
+++ b/shell/hush_test/hush-redir/redir_to_bad_fd3.tests
@@ -0,0 +1,3 @@
1# ash uses fd 10 (usually) for reading the script
2echo LOST >&3
3echo OK