diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-31 04:32:06 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-07-31 04:35:18 +0200 |
commit | 32fdf2f9fc9a617918672d71579f4ad42eb9bde9 (patch) | |
tree | f73fbaa257aeb6810557fe7a36ae6b4b3dfb72e4 | |
parent | 035486c7500c09616a6c1040d8e70923532a5c2d (diff) | |
download | busybox-32fdf2f9fc9a617918672d71579f4ad42eb9bde9.tar.gz |
ash,hush: ">&10" redirects to script/tty fds should not work
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>
-rw-r--r-- | shell/ash.c | 39 | ||||
-rw-r--r-- | shell/hush.c | 52 |
2 files changed, 72 insertions, 19 deletions
diff --git a/shell/ash.c b/shell/ash.c index ac676c2dc..5c2e06599 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -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); diff --git a/shell/hush.c b/shell/hush.c index d4101d66f..cc785d36b 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -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" */ |