diff --git a/src/cmd/ksh93/sh/io.c b/src/cmd/ksh93/sh/io.c index a7481dcb9..1e76e00d8 100644 --- a/src/cmd/ksh93/sh/io.c +++ b/src/cmd/ksh93/sh/io.c @@ -1132,18 +1132,6 @@ int sh_redirect(struct ionod *iop, int flag) { iof=iop->iofile; fn = (iof&IOUFD); - if(fn==1) - { - if(sh.subshell && sh.comsub && (flag==1 || flag==2)) - { - if(sh.subshare) - { - errormsg(SH_DICT,ERROR_exit(1),"cannot redirect stdout inside shared-state comsub"); - UNREACHABLE(); - } - sh_subfork(); - } - } if(sh.redir0 && fn==0 && !(iof&IOMOV)) sh.redir0 = 2; io_op[0] = '0'+(iof&IOUFD); diff --git a/src/cmd/ksh93/sh/xec.c b/src/cmd/ksh93/sh/xec.c index d0e16e4e2..5c673e828 100644 --- a/src/cmd/ksh93/sh/xec.c +++ b/src/cmd/ksh93/sh/xec.c @@ -1296,6 +1296,25 @@ int sh_exec(register const Shnode_t *t, int flags) } else type = (execflg && !sh.subshell && !sh.st.trapcom[0]); + /* + * A command substitution will hang on exit, writing infinite '\0', if, + * within it, standard output (FD 1) is redirected for a built-in command + * that calls sh_subfork(), or redirected permanently using 'exec' or + * 'redirect'. This forking workaround is necessary to avoid that bug. + * For shared-state comsubs, forking is incorrect, so error out then. + * TODO: actually fix the bug and remove this workaround. + */ + if((io->iofile & IOUFD)==1 && sh.subshell && sh.comsub) + { + if(!sh.subshare) + sh_subfork(); + else if(type==2) /* block stdout perma-redirects: would hang */ + { + errormsg(SH_DICT,ERROR_exit(1),"cannot redirect stdout" + " inside shared-state comsub"); + UNREACHABLE(); + } + } sh.redir0 = 1; sh_redirect(io,type); for(item=buffp->olist;item;item=item->next) diff --git a/src/cmd/ksh93/tests/io.sh b/src/cmd/ksh93/tests/io.sh index 92fe16435..482be8aa6 100755 --- a/src/cmd/ksh93/tests/io.sh +++ b/src/cmd/ksh93/tests/io.sh @@ -905,5 +905,19 @@ then for cmd in echo print printf done fi +# ====== +# Command substitution hangs, writing infinite zero bytes, when redirecting standard output on a built-in that forks +# https://github.com/ksh93/ksh/issues/416 +exp='line' +"$SHELL" -c 'echo "$(ulimit -t unlimited >/dev/null 2>&1; echo "ok $$")"' >out 2>&1 & +pid=$! +(sleep 1; kill -9 "$pid") 2>/dev/null & +if wait "$pid" 2>/dev/null +then kill "$!" # the sleep process + [[ $(