ash,hush: ">&10" redirects to script/tty fds should not work
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 31 Jul 2017 02:32:06 +0000 (04:32 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 31 Jul 2017 02:35:18 +0000 (04:35 +0200)
The fact that shell has open fds to tty and/or scripts should be
unobservable, if possible. In particular, if redirect tries to dup
one of them via ">&script_fd", it's better to pretend that script_fd
is closed, and thus redirect fails with EBADF.

Fixes these two testcase failures:
ash-redir/redir_to_bad_fd.tests
hush-redir/redir_to_bad_fd3.tests

function                                             old     new   delta
redirect                                            1018    1129    +111
setup_redirects                                      250     359    +109
readtoken1                                          2651    2655      +4
cmdloop                                              185     187      +2
changepath                                           194     195      +1
save_fd_on_redirect                                  203     194      -9
evaltree                                             501     484     -17
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 5/2 up/down: 227/-26)           Total: 201 bytes
   text    data     bss     dec     hex filename
 914553     485    6848  921886   e111e busybox_old
 914754     485    6848  922087   e11e7 busybox_unstripped

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c
shell/hush.c

index ac676c2dc9c08ceec311e007699ac9841a8d5ccc..5c2e0659902aeb8384c3606a3ea9e950f72739dd 100644 (file)
@@ -5503,6 +5503,31 @@ save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
        return 0; /* "we did not close fd" */
 }
 
+static int
+internally_opened_fd(int fd, struct redirtab *sq)
+{
+       int i;
+#if JOBS
+       if (fd == ttyfd)
+               return 1;
+#endif
+       /* If this one of script's fds? */
+       if (fd != 0) {
+               struct parsefile *pf = g_parsefile;
+               while (pf) {
+                       if (fd == pf->pf_fd)
+                               return 1;
+                       pf = pf->prev;
+               }
+       }
+
+       if (sq) for (i = 0; i < sq->pair_count && sq->two_fd[i].orig_fd != EMPTY; i++) {
+               if (fd == sq->two_fd[i].moved_to)
+                       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
@@ -5567,15 +5592,11 @@ redirect(union node *redir, int flags)
                                close(fd);
                        }
                } else {
-///TODO: if _newfd_ is a script fd or saved fd, then simulate EBADF!
-//if (newfd == ttyfd) {
-//     errno = EBADF;
-//     ash_msg_and_raise_perror("A %d", newfd);
-//}
-//if (newfd == g_parsefile->pf_fd) {
-//     errno = EBADF;
-//     ash_msg_and_raise_perror("B %d", newfd);
-//}
+                       /* if newfd is a script fd or saved fd, simulate EBADF */
+                       if (internally_opened_fd(newfd, sv)) {
+                               errno = EBADF;
+                               ash_msg_and_raise_perror("%d", newfd);
+                       }
                        dup2_or_raise(newfd, fd);
                        if (close_fd >= 0) /* "N>FILE" or ">&FILE" or heredoc? */
                                close(close_fd);
index d4101d66fceb4bf5c5650b7e16e1bb95efb65c52..cc785d36b262706dd7a452cc3b721db35c04cb39 100644 (file)
@@ -1546,6 +1546,16 @@ static void close_all_FILE_list(void)
        }
 }
 #endif
+static int fd_in_FILEs(int fd)
+{
+       struct FILE_list *fl = G.FILE_list;
+       while (fl) {
+               if (fl->fd == fd)
+                       return 1;
+               fl = fl->next;
+       }
+       return 0;
+}
 
 
 /* Helpers for setting new $n and restoring them back
@@ -6686,9 +6696,9 @@ static struct squirrel *append_squirrel(struct squirrel *sq, int i, int orig, in
 static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
 {
        int moved_to;
-       int i = 0;
+       int i;
 
-       if (sq) while (sq[i].orig_fd >= 0) {
+       if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) {
                /* If we collide with an already moved fd... */
                if (fd == sq[i].moved_to) {
                        sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd);
@@ -6702,7 +6712,6 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
                        debug_printf_redir("redirect_fd %d: already moved\n", fd);
                        return sq;
                }
-               i++;
        }
 
        /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */
@@ -6717,8 +6726,7 @@ static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd)
 {
        int i;
 
-       i = 0;
-       if (sq) while (sq[i].orig_fd >= 0) {
+       if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) {
                /* If we collide with an already moved fd... */
                if (fd == sq[i].orig_fd) {
                        /* Examples:
@@ -6730,7 +6738,6 @@ static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd)
                        debug_printf_redir("redirect_fd %d: already moved or closed\n", fd);
                        return sq;
                }
-               i++;
        }
 
        debug_printf_redir("redirect_fd %d: previous fd was closed\n", fd);
@@ -6747,7 +6754,8 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
                avoid_fd = 9;
 
 #if ENABLE_HUSH_INTERACTIVE
-       if (fd != 0 && fd == G.interactive_fd) {
+       if (fd == G.interactive_fd) {
+               /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */
                G.interactive_fd = xdup_CLOEXEC_and_close(G.interactive_fd, avoid_fd);
                debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G.interactive_fd);
                return 1; /* "we closed fd" */
@@ -6775,8 +6783,8 @@ static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
 static void restore_redirects(struct squirrel *sq)
 {
        if (sq) {
-               int i = 0;
-               while (sq[i].orig_fd >= 0) {
+               int i;
+               for (i = 0; sq[i].orig_fd >= 0; i++) {
                        if (sq[i].moved_to >= 0) {
                                /* We simply die on error */
                                debug_printf_redir("restoring redirected fd from %d to %d\n", sq[i].moved_to, sq[i].orig_fd);
@@ -6786,7 +6794,6 @@ static void restore_redirects(struct squirrel *sq)
                                debug_printf_redir("restoring redirected fd %d: closing it\n", sq[i].orig_fd);
                                close(sq[i].orig_fd);
                        }
-                       i++;
                }
                free(sq);
        }
@@ -6796,6 +6803,25 @@ static void restore_redirects(struct squirrel *sq)
        restore_redirected_FILEs();
 }
 
+static int internally_opened_fd(int fd, struct squirrel *sq)
+{
+       int i;
+
+#if ENABLE_HUSH_INTERACTIVE
+       if (fd == G.interactive_fd)
+               return 1;
+#endif
+       /* If this one of script's fds? */
+       if (fd_in_FILEs(fd))
+               return 1;
+
+       if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) {
+               if (fd == sq[i].moved_to)
+                       return 1;
+       }
+       return 0;
+}
+
 /* squirrel != NULL means we squirrel away copies of stdin, stdout,
  * and stderr if they are redirected. */
 static int setup_redirects(struct command *prog, struct squirrel **sqp)
@@ -6878,6 +6904,12 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp)
                         * and second redirect closes 3! Restore code then closes 3 again.
                         */
                } else {
+                       /* if newfd is a script fd or saved fd, simulate EBADF */
+                       if (internally_opened_fd(newfd, sqp ? *sqp : NULL)) {
+                               //errno = EBADF;
+                               //bb_perror_msg_and_die("can't duplicate file descriptor");
+                               newfd = -1; /* same effect as code above */
+                       }
                        xdup2(newfd, redir->rd_fd);
                        if (redir->rd_dup == REDIRFD_TO_FILE)
                                /* "rd_fd > FILE" */