Skip to content

Commit

Permalink
Further robustify .get and .set discipline functions (re: 430e478) (#368
Browse files Browse the repository at this point in the history
)

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: #347
  • Loading branch information
McDutchie authored Dec 9, 2021
1 parent d56eaff commit 2322f93
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 11 deletions.
2 changes: 2 additions & 0 deletions src/cmd/ksh93/Mamfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/cmd/ksh93/sh/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
41 changes: 30 additions & 11 deletions src/cmd/ksh93/sh/nvdisc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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*);

Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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))
{
Expand All @@ -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);
Expand All @@ -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))
Expand Down
22 changes: 22 additions & 0 deletions src/cmd/ksh93/tests/pty.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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))

0 comments on commit 2322f93

Please sign in to comment.