From 2322f939429ae002f92ea333a3bb6b149aad1431 Mon Sep 17 00:00:00 2001 From: Martijn Dekker Date: Thu, 9 Dec 2021 06:31:54 +0100 Subject: [PATCH] Further robustify .get and .set discipline functions (re: 430e4781) (#368) This should fix various crashes that remain, at least: * when running a PS2 discipline at parse time * when pressing Ctrl+C on a PS2 prompt * when a special builtin within a discipline throws an error within a virtual subshell src/cmd/ksh93/sh/nvdisc.c: - In both assign() which handles .set disciplines and lookup() which handles .get disciplines, to stop errors in discipline functions from wreaking havoc: - Save, reinitialise and restore the lexer state in case the discipline is run at parse time. This happens with PS2; I'm not currently aware of other contexts but that doesn't mean there aren't any or that there won't be any. Plus, I determined by experimenting that doing this here seems to be the only way to make it work reliably. Thankfully the overhead is low. - Check the topfd redirection state and run sh_iorestore() if needed. Without this, if a special builtin with a redirection throws an error in a discipline function, its redirection(s) remain permanent. For example, 'trap --bad-option 2>/dev/null' in a PS2.get() discipline would kill standard error, including all your prompts. src/cmd/ksh93/sh/io.c: io_prompt(): - Before getting the value of the PS2 prompt, save the stack state and restore it after. This stops a PS2.get discipline function from corrupting a command substitution that the user is typing. Doing this in assign()/lookup() is ineffective, so do it here. Fixes: https://github.com/ksh93/ksh/issues/347 --- src/cmd/ksh93/Mamfile | 2 ++ src/cmd/ksh93/sh/io.c | 7 +++++++ src/cmd/ksh93/sh/nvdisc.c | 41 ++++++++++++++++++++++++++++---------- src/cmd/ksh93/tests/pty.sh | 22 ++++++++++++++++++++ 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/src/cmd/ksh93/Mamfile b/src/cmd/ksh93/Mamfile index 66a4f3e5664f..a9ce33904a10 100644 --- a/src/cmd/ksh93/Mamfile +++ b/src/cmd/ksh93/Mamfile @@ -669,6 +669,8 @@ make install done main.o generated make nvdisc.o make sh/nvdisc.c + prev include/shlex.h implicit + prev include/io.h implicit prev include/path.h implicit prev include/builtins.h implicit prev include/variables.h implicit diff --git a/src/cmd/ksh93/sh/io.c b/src/cmd/ksh93/sh/io.c index 22bd4ef5dd96..363df597c0ba 100644 --- a/src/cmd/ksh93/sh/io.c +++ b/src/cmd/ksh93/sh/io.c @@ -2186,8 +2186,15 @@ static int io_prompt(Shell_t *shp,Sfio_t *iop,register int flag) goto done; } case 2: + { + /* PS2 prompt. Save stack state to avoid corrupting command substitutions + * in case we're executing a PS2.get discipline function at parse time. */ + int savestacktop = staktell(); + char *savestackptr = stakfreeze(0); cp = nv_getval(sh_scoped(shp,PS2NOD)); + stakset(savestackptr, savestacktop); break; + } case 3: cp = nv_getval(sh_scoped(shp,PS3NOD)); break; diff --git a/src/cmd/ksh93/sh/nvdisc.c b/src/cmd/ksh93/sh/nvdisc.c index ee58346fa9b1..14cbef77bfe4 100644 --- a/src/cmd/ksh93/sh/nvdisc.c +++ b/src/cmd/ksh93/sh/nvdisc.c @@ -28,6 +28,8 @@ #include "variables.h" #include "builtins.h" #include "path.h" +#include "io.h" +#include "shlex.h" static void assign(Namval_t*,const char*,int,Namfun_t*); @@ -283,21 +285,30 @@ static void assign(Namval_t *np,const char* val,int flags,Namfun_t *handle) nq = vp->disc[type=UNASSIGN]; if(nq && !isblocked(bp,type)) { - int bflag=0, savexit=sh.savexit, jmpval=0; - struct checkpt buff; + struct checkpt checkpoint; + int jmpval; + int savexit = sh.savexit; + Lex_t *lexp = (Lex_t*)sh.lex_context, savelex; + int bflag; + /* disciplines like PS2 may run at parse time; save, reinit and restore the lexer state */ + savelex = *lexp; + sh_lexopen(lexp, &sh, 0); /* needs full init (0), not what it calls reinit (1) */ block(bp,type); - if (type==APPEND && (bflag= !isblocked(bp,LOOKUPS))) + if(bflag = (type==APPEND && !isblocked(bp,LOOKUPS))) block(bp,LOOKUPS); - sh_pushcontext(&sh,&buff,1); - jmpval = sigsetjmp(buff.buff,0); + sh_pushcontext(&sh, &checkpoint, 1); + jmpval = sigsetjmp(checkpoint.buff, 0); if(!jmpval) sh_fun(nq,np,(char**)0); - sh_popcontext(&sh,&buff); + sh_popcontext(&sh, &checkpoint); + if(sh.topfd != checkpoint.topfd) + sh_iorestore(&sh, checkpoint.topfd, jmpval); unblock(bp,type); if(bflag) unblock(bp,LOOKUPS); if(!vp->disc[type]) chktfree(np,vp); + *lexp = savelex; sh.savexit = savexit; /* avoid influencing $? */ } if(nv_isarray(np)) @@ -381,8 +392,13 @@ static char* lookup(Namval_t *np, int type, Sfdouble_t *dp,Namfun_t *handle) union Value *up = np->nvalue.up; if(nq && !isblocked(bp,type)) { - int savexit = sh.savexit, jmpval = 0; - struct checkpt buff; + struct checkpt checkpoint; + int jmpval; + int savexit = sh.savexit; + Lex_t *lexp = (Lex_t*)sh.lex_context, savelex; + /* disciplines like PS2 may run at parse time; save, reinit and restore the lexer state */ + savelex = *lexp; + sh_lexopen(lexp, &sh, 0); /* needs full init (0), not what it calls reinit (1) */ node = *SH_VALNOD; if(!nv_isnull(SH_VALNOD)) { @@ -395,11 +411,13 @@ static char* lookup(Namval_t *np, int type, Sfdouble_t *dp,Namfun_t *handle) nv_setsize(SH_VALNOD,10); } block(bp,type); - sh_pushcontext(&sh,&buff,1); - jmpval = sigsetjmp(buff.buff,0); + sh_pushcontext(&sh, &checkpoint, 1); + jmpval = sigsetjmp(checkpoint.buff, 0); if(!jmpval) sh_fun(nq,np,(char**)0); - sh_popcontext(&sh,&buff); + sh_popcontext(&sh, &checkpoint); + if(sh.topfd != checkpoint.topfd) + sh_iorestore(&sh, checkpoint.topfd, jmpval); unblock(bp,type); if(!vp->disc[type]) chktfree(np,vp); @@ -416,6 +434,7 @@ static char* lookup(Namval_t *np, int type, Sfdouble_t *dp,Namfun_t *handle) /* restore everything but the nvlink field */ memcpy(&SH_VALNOD->nvname, &node.nvname, sizeof(node)-sizeof(node.nvlink)); } + *lexp = savelex; sh.savexit = savexit; /* avoid influencing $? */ } if(nv_isarray(np)) diff --git a/src/cmd/ksh93/tests/pty.sh b/src/cmd/ksh93/tests/pty.sh index 35d6e7c27ad8..c156f1f35247 100755 --- a/src/cmd/ksh93/tests/pty.sh +++ b/src/cmd/ksh93/tests/pty.sh @@ -907,5 +907,27 @@ w true); echo "Exit status is $?" u Exit status is 0 ! +# err_exit # +tst $LINENO <<"!" +L interrupted PS2 discipline function +# https://github.com/ksh93/ksh/issues/347 + +d 15 +p :test-1: +w PS2.get() { trap --bad-option 2>/dev/null; .sh.value="NOT REACHED"; } +p :test-2: +w echo \$\( +r :test-2: echo \$\( +w echo one \\ +r > echo one \\ +w two three +r > two three +w echo end +r > echo end +w \) +r > \) +r one two three end +! + # ====== exit $((Errors<125?Errors:125))