Module Name: src Committed By: kre Date: Wed Sep 15 18:29:45 UTC 2021
Modified Files: src/bin/sh: main.c parser.c redir.c redir.h Log Message: Improve the solution for the 2nd access to a fd which shouldn't be available ("13") issue reported by Jan Schaumann on netbsd-users. This fixes a bug in the earlier fix (a day or so ago) which could allow the shell's idea of which fd range was in use by the script to get wildly incorrect, but now also actually looks to see which fd's are in use as renamed other user fd's during the lifetime of a redirection which needs to be able to be undone (most redirections occur after a fork and are permanent in the child process). Attempting to access such a fd (as with attempts to access a fd in use by the shell for its own purposes) is treated as an attempt to access a closed fd (EBADF). Attempting to reuse the fd for some other purpose (which should be rare, even for scripts attempting to cause problems, since the shell generally knows which fds the script wants to use, and avoids them) will cause the renamed (renumbered) fd to be renamed again (moved aside to some other available fd), just as happens with the shell's private fds. Also, when a generic fd is required, don't give up because of EMFILE or similar unless there are no available fds at all (we might prefer >10 or bigger, but if there are none there, use anything). This avoids redirection errors when ulimit -n has been set small, and all the fds >10 that are available have been used, but we need somewhere to park the old user of a fd while we reuse that fd for the redirection. To generate a diff of this commit: cvs rdiff -u -r1.85 -r1.86 src/bin/sh/main.c cvs rdiff -u -r1.173 -r1.174 src/bin/sh/parser.c cvs rdiff -u -r1.67 -r1.68 src/bin/sh/redir.c cvs rdiff -u -r1.25 -r1.26 src/bin/sh/redir.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/bin/sh/main.c diff -u src/bin/sh/main.c:1.85 src/bin/sh/main.c:1.86 --- src/bin/sh/main.c:1.85 Fri Feb 7 01:25:08 2020 +++ src/bin/sh/main.c Wed Sep 15 18:29:45 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: main.c,v 1.85 2020/02/07 01:25:08 fox Exp $ */ +/* $NetBSD: main.c,v 1.86 2021/09/15 18:29:45 kre Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -42,7 +42,7 @@ __COPYRIGHT("@(#) Copyright (c) 1991, 19 #if 0 static char sccsid[] = "@(#)main.c 8.7 (Berkeley) 7/19/95"; #else -__RCSID("$NetBSD: main.c,v 1.85 2020/02/07 01:25:08 fox Exp $"); +__RCSID("$NetBSD: main.c,v 1.86 2021/09/15 18:29:45 kre Exp $"); #endif #endif /* not lint */ @@ -85,6 +85,7 @@ int rootpid; int rootshell; struct jmploc main_handler; int max_user_fd; +long user_fd_limit; #if PROFILE short profile_buf[16384]; extern int etext(); @@ -131,6 +132,7 @@ main(int argc, char **argv) max_user_fd = fcntl(0, F_MAXFD); if (max_user_fd < 2) max_user_fd = 2; + user_fd_limit = sysconf(_SC_OPEN_MAX); setlocale(LC_ALL, ""); Index: src/bin/sh/parser.c diff -u src/bin/sh/parser.c:1.173 src/bin/sh/parser.c:1.174 --- src/bin/sh/parser.c:1.173 Tue Sep 14 14:49:39 2021 +++ src/bin/sh/parser.c Wed Sep 15 18:29:45 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: parser.c,v 1.173 2021/09/14 14:49:39 kre Exp $ */ +/* $NetBSD: parser.c,v 1.174 2021/09/15 18:29:45 kre Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -37,7 +37,7 @@ #if 0 static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95"; #else -__RCSID("$NetBSD: parser.c,v 1.173 2021/09/14 14:49:39 kre Exp $"); +__RCSID("$NetBSD: parser.c,v 1.174 2021/09/15 18:29:45 kre Exp $"); #endif #endif /* not lint */ @@ -746,9 +746,12 @@ fixredir(union node *n, const char *text if (!err) n->ndup.vname = NULL; - if (is_number(text)) + if (is_number(text)) { n->ndup.dupfd = number(text); - else if (text[0] == '-' && text[1] == '\0') + if (n->ndup.dupfd < user_fd_limit && + n->ndup.dupfd > max_user_fd) + max_user_fd = n->ndup.dupfd; + } else if (text[0] == '-' && text[1] == '\0') n->ndup.dupfd = -1; else { @@ -757,8 +760,6 @@ fixredir(union node *n, const char *text else n->ndup.vname = makeword(startlinno - elided_nl); } - if (n->ndup.dupfd > max_user_fd) - max_user_fd = n->ndup.dupfd; } @@ -1592,8 +1593,9 @@ parseredir(const char *out, int c) fd = (*out == '\0') ? -1 : number(out); /* number(out) >= 0 */ np->nfile.fd = fd; /* do this again later with updated fd */ if (fd != np->nfile.fd) - error("file descriptor (%d) out of range", fd); - if (fd > max_user_fd) + error("file descriptor (%d) out of range (max %ld)", + fd, user_fd_limit - 1); + if (fd < user_fd_limit && fd > max_user_fd) max_user_fd = fd; VTRACE(DBG_LEXER, ("parseredir after '%s%c' ", out, c)); Index: src/bin/sh/redir.c diff -u src/bin/sh/redir.c:1.67 src/bin/sh/redir.c:1.68 --- src/bin/sh/redir.c:1.67 Tue Sep 14 14:49:39 2021 +++ src/bin/sh/redir.c Wed Sep 15 18:29:45 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: redir.c,v 1.67 2021/09/14 14:49:39 kre Exp $ */ +/* $NetBSD: redir.c,v 1.68 2021/09/15 18:29:45 kre Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -37,7 +37,7 @@ #if 0 static char sccsid[] = "@(#)redir.c 8.2 (Berkeley) 5/4/95"; #else -__RCSID("$NetBSD: redir.c,v 1.67 2021/09/14 14:49:39 kre Exp $"); +__RCSID("$NetBSD: redir.c,v 1.68 2021/09/15 18:29:45 kre Exp $"); #endif #endif /* not lint */ @@ -121,6 +121,7 @@ STATIC int big_sh_fd = 0; STATIC const struct renamelist *is_renamed(const struct renamelist *, int); STATIC void fd_rename(struct redirtab *, int, int); +STATIC int * saved_redirected_fd(int); STATIC void free_rl(struct redirtab *, int); STATIC void openredirect(union node *, char[10], int); STATIC int openhere(const union node *); @@ -136,6 +137,7 @@ struct shell_fds { /* keep track of int STATIC struct shell_fds *sh_fd_list; +STATIC int pick_new_fd(int); STATIC void renumber_sh_fd(struct shell_fds *); STATIC struct shell_fds *sh_fd(int); @@ -150,6 +152,21 @@ is_renamed(const struct renamelist *rl, return NULL; } +STATIC int * +saved_redirected_fd(int fd) +{ + struct redirtab *rt; + struct renamelist *rl; + + for (rt = redirlist; rt != NULL; rt = rt->next) { + for (rl = rt->renamed; rl != NULL; rl = rl->next) { + if (rl->into == fd) + return &rl->into; + } + } + return NULL; +} + STATIC void free_rl(struct redirtab *rt, int reset) { @@ -221,10 +238,22 @@ redirect(union node *redir, int flags) redirlist = sv; } for (n = redir ; n ; n = n->nfile.next) { + int *renamed; + fd = n->nfile.fd; - VTRACE(DBG_REDIR, ("redir %d (max=%d) ", fd, max_user_fd)); - if (fd > max_user_fd) + VTRACE(DBG_REDIR, ("redir %d (max=%d limit=%ld) ", + fd, max_user_fd, user_fd_limit)); + if (fd < user_fd_limit && fd > max_user_fd) max_user_fd = fd; + if ((renamed = saved_redirected_fd(fd)) != NULL) { + int to = pick_new_fd(fd); + + VTRACE(DBG_REDIR, + ("redirect: moved holding fd %d to %d\n", fd, to)); + *renamed = to; + if (to != fd) /* always... */ + (void)close(fd); + } renumber_sh_fd(sh_fd(fd)); if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && n->ndup.dupfd == fd) { @@ -255,6 +284,10 @@ redirect(union node *redir, int flags) i = fcntl(fd, F_DUPFD, big_sh_fd); if (i >= 0) break; + if (errno == EMFILE || errno == EINVAL) + i = fcntl(fd, F_DUPFD, 3); + if (i >= 0) + break; /* FALLTHRU */ default: error("%d: %s", fd, strerror(errno)); @@ -264,7 +297,7 @@ redirect(union node *redir, int flags) if (i >= 0) (void)fcntl(i, F_SETFD, FD_CLOEXEC); fd_rename(sv, fd, i); - VTRACE(DBG_REDIR, ("saved as %d ", i)); + VTRACE(DBG_REDIR, ("fd %d saved as %d ", fd, i)); INTON; } VTRACE(DBG_REDIR, ("%s\n", fd == 0 ? "STDIN" : "")); @@ -353,7 +386,8 @@ openredirect(union node *redir, char mem case NTOFD: case NFROMFD: if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ - if (sh_fd(redir->ndup.dupfd) != NULL) + if (sh_fd(redir->ndup.dupfd) != NULL || + saved_redirected_fd(redir->ndup.dupfd) != NULL) error("Redirect (from %d to %d) failed: %s", redir->ndup.dupfd, fd, strerror(EBADF)); if (fd < 10 && redir->ndup.dupfd < 10 && @@ -604,7 +638,7 @@ to_upper_fd(int fd) int i; VTRACE(DBG_REDIR|DBG_OUTPUT, ("to_upper_fd(%d)", fd)); - if (big_sh_fd < 10) + if (big_sh_fd < 10 || big_sh_fd >= user_fd_limit) find_big_fd(); do { i = fcntl(fd, F_DUPFD_CLOEXEC, big_sh_fd); @@ -672,6 +706,26 @@ sh_fd(int fd) return NULL; } +STATIC int +pick_new_fd(int fd) +{ + int to; + + to = fcntl(fd, F_DUPFD_CLOEXEC, big_sh_fd); + if (to == -1 && big_sh_fd >= 22) + to = fcntl(fd, F_DUPFD_CLOEXEC, big_sh_fd/2); + if (to == -1) + to = fcntl(fd, F_DUPFD_CLOEXEC, fd + 1); + if (to == -1) + to = fcntl(fd, F_DUPFD_CLOEXEC, 10); + if (to == -1) + to = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (to == -1) + error("insufficient file descriptors available"); + CLOEXEC(to); + return to; +} + STATIC void renumber_sh_fd(struct shell_fds *fp) { @@ -689,22 +743,14 @@ renumber_sh_fd(struct shell_fds *fp) if (fp->fd >= big_sh_fd) find_big_fd(); - to = fcntl(fp->fd, F_DUPFD_CLOEXEC, big_sh_fd); - if (to == -1 && big_sh_fd >= 22) - to = fcntl(fp->fd, F_DUPFD_CLOEXEC, big_sh_fd/2); - if (to == -1) - to = fcntl(fp->fd, F_DUPFD_CLOEXEC, fp->fd + 1); - if (to == -1) - to = fcntl(fp->fd, F_DUPFD_CLOEXEC, 10); - if (to == -1) - to = fcntl(fp->fd, F_DUPFD_CLOEXEC, 3); - if (to == -1) - error("insufficient file descriptors available"); - CLOEXEC(to); + to = pick_new_fd(fp->fd); if (fp->fd == to) /* impossible? */ return; + VTRACE(DBG_REDIR, ("renumber_sh_fd: moved shell fd %d to %d\n", + fp->fd, to)); + (*fp->cb)(fp->fd, to); (void)close(fp->fd); fp->fd = to; @@ -831,7 +877,7 @@ getflags(int fd, int p) { int c, f; - if (sh_fd(fd) != NULL) { + if (sh_fd(fd) != NULL || saved_redirected_fd(fd) != NULL) { if (!p) return -1; error("Can't get status for fd=%d (%s)", fd, strerror(EBADF)); @@ -991,8 +1037,9 @@ fdflagscmd(int argc, char *argv[]) while (num[0] == '0' && num[1] != '\0') /* skip 0's */ num++; - if (strlen(num) > 5) - error("%s too big to be a file descriptor", num); + if (strlen(num) > 5 || + (fd >= user_fd_limit && fd > max_user_fd)) + error("%s: too big to be a file descriptor", num); if (setflags) setone(fd, pos, neg, verbose); Index: src/bin/sh/redir.h diff -u src/bin/sh/redir.h:1.25 src/bin/sh/redir.h:1.26 --- src/bin/sh/redir.h:1.25 Fri Apr 3 16:22:23 2020 +++ src/bin/sh/redir.h Wed Sep 15 18:29:45 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: redir.h,v 1.25 2020/04/03 16:22:23 joerg Exp $ */ +/* $NetBSD: redir.h,v 1.26 2021/09/15 18:29:45 kre Exp $ */ /*- * Copyright (c) 1991, 1993 @@ -53,3 +53,4 @@ struct output; int outredir(struct output *, union node *, int); extern int max_user_fd; /* highest fd used by user */ +extern long user_fd_limit; /* highest possible user fd */