diff --git a/NEWS b/NEWS index af9e39b3b..7ff156c96 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,14 @@ For full details, see the git log at: https://github.com/ksh93/ksh Any uppercase BUG_* names are modernish shell bug IDs. +2021-03-13: + +- Fixed a file descriptor leak that occurred when ksh used /dev/fd for + process substitutions passed to functions. + +- Fixed a separate file descriptor leak that happened when a process + substitution was passed to a nonexistent command. + 2021-03-11: - Fixed an intermittent bug that caused process substitutions to infinitely diff --git a/src/cmd/ksh93/SHOPT.sh b/src/cmd/ksh93/SHOPT.sh index e7f56a7a1..c95f05404 100644 --- a/src/cmd/ksh93/SHOPT.sh +++ b/src/cmd/ksh93/SHOPT.sh @@ -13,6 +13,7 @@ SHOPT BRACEPAT=1 # C-shell {...,...} expansions (, required) SHOPT CMDLIB_HDR= # '' # custom -lcmd list for path-bound builtins SHOPT CMDLIB_DIR= # '\"/opt/ast/bin\"' # virtual directory prefix for path-bound builtins SHOPT CRNL= # accept MS Windows newlines () for +SHOPT DEVFD= # use /dev/fd instead of FIFOs for process substitutions SHOPT DYNAMIC=1 # dynamic loading for builtins SHOPT ECHOPRINT= # make echo equivalent to print SHOPT EDPREDICT=1 # predictive editing diff --git a/src/cmd/ksh93/include/defs.h b/src/cmd/ksh93/include/defs.h index 10941a6be..b9bd06212 100644 --- a/src/cmd/ksh93/include/defs.h +++ b/src/cmd/ksh93/include/defs.h @@ -43,14 +43,6 @@ # define mbmax() 1 #endif -/* - * Until a file descriptor leak with process substitution - * is fixed, disable /dev/fd use to avoid the problem. - * https://github.com/ksh93/ksh/issues/67 - */ -#undef SHOPT_DEVFD -#define SHOPT_DEVFD 0 - #include #include #include "FEATURE/externs" diff --git a/src/cmd/ksh93/include/version.h b/src/cmd/ksh93/include/version.h index c53abee89..9eb95dec4 100644 --- a/src/cmd/ksh93/include/version.h +++ b/src/cmd/ksh93/include/version.h @@ -20,7 +20,7 @@ #define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */ #define SH_RELEASE_SVER "1.0.0-alpha" /* semantic version number: https://semver.org */ -#define SH_RELEASE_DATE "2021-03-11" /* must be in this format for $((.sh.version)) */ +#define SH_RELEASE_DATE "2021-03-13" /* must be in this format for $((.sh.version)) */ #define SH_RELEASE_CPYR "(c) 2020-2021 Contributors to ksh " SH_RELEASE_FORK /* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */ diff --git a/src/cmd/ksh93/sh/args.c b/src/cmd/ksh93/sh/args.c index f9920bc70..339452407 100644 --- a/src/cmd/ksh93/sh/args.c +++ b/src/cmd/ksh93/sh/args.c @@ -758,7 +758,7 @@ struct argnod *sh_argprocsub(Shell_t *shp,struct argnod *argp) job.jobcontrol = savejobcontrol; sh_setstate(savestates); #if SHOPT_DEVFD - close(pv[1-fd]); + sh_close(pv[1-fd]); sh_iosave(shp,-pv[fd], shp->topfd, (char*)0); #else /* remember the FIFO for cleanup in case the command never opens it (see fifo_cleanup(), xec.c) */ diff --git a/src/cmd/ksh93/sh/xec.c b/src/cmd/ksh93/sh/xec.c index 850307d82..a5e9e50c4 100644 --- a/src/cmd/ksh93/sh/xec.c +++ b/src/cmd/ksh93/sh/xec.c @@ -1014,7 +1014,7 @@ int sh_exec(register const Shnode_t *t, int flags) char *trap; Namval_t *np, *nq, *last_table; struct ionod *io; - int command=0, flgs=NV_ASSIGN; + int command=0, flgs=NV_ASSIGN, jmpval=0; shp->bltindata.invariant = type>>(COMBITS+2); type &= (COMMSK|COMSCAN); sh_stats(STAT_SCMDS); @@ -1258,7 +1258,7 @@ int sh_exec(register const Shnode_t *t, int flags) volatile int scope=0, share=0; volatile void *save_ptr; volatile void *save_data; - int jmpval, save_prompt; + int save_prompt; int was_nofork = execflg?sh_isstate(SH_NOFORK):0; struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt)); #if SHOPT_VSH @@ -1440,10 +1440,6 @@ int sh_exec(register const Shnode_t *t, int flags) sh_unscope(shp); bp->ptr = (void*)save_ptr; bp->data = (void*)save_data; - /* don't restore for 'exec' or 'redirect' in subshell */ - if((shp->topfd>topfd) && !(shp->subshell && (np==SYSEXEC || np==SYSREDIR))) - sh_iorestore(shp,topfd,jmpval); - shp->redir0 = 0; if(jmpval) siglongjmp(*shp->jmplist,jmpval); @@ -1456,7 +1452,6 @@ int sh_exec(register const Shnode_t *t, int flags) if(!command && np && nv_isattr(np,NV_FUNCTION)) { volatile int indx; - int jmpval=0; struct checkpt *buffp = (struct checkpt*)stkalloc(shp->stk,sizeof(struct checkpt)); #if SHOPT_NAMESPACE Namval_t node, *namespace=0; @@ -1557,6 +1552,8 @@ int sh_exec(register const Shnode_t *t, int flags) #if !SHOPT_DEVFD fifo_cleanup(); #endif + if(shp->topfd > topfd && !(shp->subshell && (np==SYSEXEC || np==SYSREDIR))) + sh_iorestore(shp,topfd,jmpval); exitset(); break; } @@ -1631,6 +1628,10 @@ int sh_exec(register const Shnode_t *t, int flags) # endif /* _lib_fork */ if(parent<0) { + /* prevent a file descriptor leak from a 'not found' error */ + if(shp->topfd > topfd) + sh_iorestore(shp,topfd,0); + if(shp->comsub==1 && usepipe && unpipe) sh_iounpipe(shp); break; diff --git a/src/cmd/ksh93/tests/io.sh b/src/cmd/ksh93/tests/io.sh index 1638e0077..41f7452dd 100755 --- a/src/cmd/ksh93/tests/io.sh +++ b/src/cmd/ksh93/tests/io.sh @@ -629,6 +629,7 @@ fi # ====== # File descriptor leak with process substitution +# https://github.com/ksh93/ksh/issues/67 err=$( ulimit -n 15 || exit 0 fdUser() { @@ -641,6 +642,21 @@ err=$( ) || err_exit 'Process substitution leaks file descriptors when used as argument to function' \ "(got $(printf %q "$err"))" +# File descriptor leak after 'command not found' with process substitution as argument +err=$( + ulimit -n 25 || exit 0 + set +x + PATH=/dev/null + for ((i=1; i<10; i++)) + do notfound <(:) >(:) 2> /dev/null + done 2>&1 + exit 0 +) || err_exit "Process substitution leaks file descriptors when used as argument to nonexistent command" \ + "(got $(printf %q "$err"))" + +got=$(command -x cat <(command -x echo foo) 2>&1) || err_exit "process substitution doesn't work with 'command'" \ + "(got $(printf %q "$got"))" + # ====== # A redirection with a null command could crash under certain circumstances (rhbz#1200534) "$SHELL" -i >/dev/null 2>&1 -c '