From da97587e9eae3dfa08e28b1a318786d55a33ab7f Mon Sep 17 00:00:00 2001 From: Martijn Dekker Date: Thu, 23 Jun 2022 02:56:18 +0100 Subject: [PATCH] lex.c: prevent restoring outdated stack pointer Lexical levels are stored in a dynamically grown array of int values grown by the stack_grow function. The pointer lex_match and the maximum index lex_max are part of the lexer state struct that is now saved and restored in various places -- see e.g. 37044047, a2bc49be. If the stack needs to be grown, it is reallocated in stack_grow() using sh_realloc(). If that happens between saving and restoring the lexer state, then an outdated pointer is restored, and crash. src/cmd/ksh93/include/shlex.h, src/cmd/ksh93/sh/lex.c: - Take lex_match and lex_max out of the lexer state struct and make them separate static variables. src/cmd/ksh93/edit/edit.c: - While we're at it, save and restore the lexer state in a way that is saner than the 93v- beta approach (re: 37044047) as well as more readable. Instead of permanently allocating memory, use a local variable to save the struct. Save/restore directly around the sh_trap() call that actually needs this done. Resolves: https://github.com/ksh93/ksh/issues/482 --- NEWS | 2 ++ src/cmd/ksh93/edit/edit.c | 12 +++--------- src/cmd/ksh93/include/shlex.h | 2 -- src/cmd/ksh93/sh/lex.c | 21 ++++++++++++--------- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/NEWS b/NEWS index 30bf4199f..258481890 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,8 @@ Any uppercase BUG_* names are modernish shell bug IDs. - Fixed: 'echo' failed when used inside a command substitution that is inside a nested compound assignment. +- Fixed a crash in KEYBD trap handling introduced on 2021-11-29. + 2022-06-20: - Fixed a race condition that could cause redirections to fail with a diff --git a/src/cmd/ksh93/edit/edit.c b/src/cmd/ksh93/edit/edit.c index 65f6dff54..0deec4c6a 100644 --- a/src/cmd/ksh93/edit/edit.c +++ b/src/cmd/ksh93/edit/edit.c @@ -50,7 +50,6 @@ static char CURSOR_UP[20] = { ESC, '[', 'A', 0 }; static char KILL_LINE[20] = { ESC, '[', 'J', 0 }; -static Lex_t *savelex; #if SHOPT_MULTIBYTE @@ -219,8 +218,6 @@ int tty_set(int fd, int action, struct termios *tty) void tty_cooked(register int fd) { register Edit_t *ep = (Edit_t*)(sh.ed_context); - if(sh.st.trap[SH_KEYTRAP] && savelex) - memcpy(sh.lex_context,savelex,sizeof(Lex_t)); ep->e_keytrap = 0; if(ep->e_raw==0) return; @@ -842,12 +839,6 @@ void ed_setup(register Edit_t *ep, int fd, int reedit) ep->e_lbuf[n] = *pp++; ep->e_default = 0; } - if(sh.st.trap[SH_KEYTRAP]) - { - if(!savelex) - savelex = (Lex_t*)sh_malloc(sizeof(Lex_t)); - memcpy(savelex, sh.lex_context, sizeof(Lex_t)); - } } #endif /* SHOPT_ESH || SHOPT_VSH */ @@ -1637,6 +1628,7 @@ static int keytrap(Edit_t *ep,char *inbuff,register int insize, int bufsize, int { register char *cp; int savexit; + Lex_t *lexp = (Lex_t*)sh.lex_context, savelex; #if SHOPT_MULTIBYTE char buff[MAXLINE]; ed_external(ep->e_inbuf,cp=buff); @@ -1657,7 +1649,9 @@ static int keytrap(Edit_t *ep,char *inbuff,register int insize, int bufsize, int nv_putval(ED_TXTNOD,(char*)cp,NV_NOFREE); nv_putval(ED_MODENOD,ep->e_vi_insert,NV_NOFREE); savexit = sh.savexit; + savelex = *lexp; sh_trap(sh.st.trap[SH_KEYTRAP],0); + *lexp = savelex; sh.savexit = savexit; if((cp = nv_getval(ED_CHRNOD)) == inbuff) nv_unset(ED_CHRNOD); diff --git a/src/cmd/ksh93/include/shlex.h b/src/cmd/ksh93/include/shlex.h index 5bd8ca89b..b9315f116 100644 --- a/src/cmd/ksh93/include/shlex.h +++ b/src/cmd/ksh93/include/shlex.h @@ -61,8 +61,6 @@ struct _shlex_pvt_lexdata_ char *first; int level; int lastc; - int lex_max; - int *lex_match; int lex_state; int docextra; #if SHOPT_KIA diff --git a/src/cmd/ksh93/sh/lex.c b/src/cmd/ksh93/sh/lex.c index 0a07d3a8c..231cc8a9c 100644 --- a/src/cmd/ksh93/sh/lex.c +++ b/src/cmd/ksh93/sh/lex.c @@ -62,13 +62,13 @@ local_iswblank(wchar_t wc) #endif -#define pushlevel(lp,c,s) ((lp->lexd.level>=lp->lexd.lex_max?stack_grow(lp):1) &&\ - ((lp->lexd.lex_match[lp->lexd.level++]=lp->lexd.lastc),\ +#define pushlevel(lp,c,s) ((lp->lexd.level>=lex_max?stack_grow():1) &&\ + ((lex_match[lp->lexd.level++]=lp->lexd.lastc),\ lp->lexd.lastc=(((s)<lexd.lastc>>CHAR_BIT) #define endchar(lp) (lp->lexd.lastc&0xff) #define setchar(lp,c) (lp->lexd.lastc = ((lp->lexd.lastc&~0xff)|(c))) -#define poplevel(lp) (lp->lexd.lastc=lp->lexd.lex_match[--lp->lexd.level]) +#define poplevel(lp) (lp->lexd.lastc=lex_match[--lp->lexd.level]) static char *fmttoken(Lex_t*, int); static struct argnod *endword(int); @@ -77,9 +77,12 @@ static void setupalias(Lex_t*,const char*, Namval_t*); static int comsub(Lex_t*,int); static void nested_here(Lex_t*); static int here_copy(Lex_t*, struct ionod*); -static int stack_grow(Lex_t*); +static int stack_grow(void); static const Sfdisc_t alias_disc = { NULL, NULL, NULL, alias_exceptf, NULL }; +/* these were taken out of the Lex_t struct because they should never be saved and restored (see stack_grow()) */ +static int lex_max, *lex_match; + #if SHOPT_KIA static void refvar(Lex_t *lp, int type) @@ -2472,12 +2475,12 @@ static void setupalias(Lex_t *lp, const char *string,Namval_t *np) /* * grow storage stack for nested constructs by STACK_ARRAY */ -static int stack_grow(Lex_t *lp) +static int stack_grow(void) { - lp->lexd.lex_max += STACK_ARRAY; - if(lp->lexd.lex_match) - lp->lexd.lex_match = (int*)sh_realloc((char*)lp->lexd.lex_match,sizeof(int)*lp->lexd.lex_max); + lex_max += STACK_ARRAY; + if(lex_match) + lex_match = (int*)sh_realloc((char*)lex_match,sizeof(int)*lex_max); else - lp->lexd.lex_match = (int*)sh_malloc(sizeof(int)*STACK_ARRAY); + lex_match = (int*)sh_malloc(sizeof(int)*STACK_ARRAY); return(1); }