1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00

lex.c: endword(): fix out-of-bounds index to state table

The lexer use 256-byte state tables (see data/lexstates.c), one
byte per possible value for the (unsigned) char type. But the sp
variable used as an index to a state table in loops like this...
                while((n = state[*sp++]) == 0)
                        ;
...is a char*, a pointer to a char. The C standard does not define
if the char type is signed or not (!). On clang and gcc, it is
signed. That means that, whenever a single-byte, high-bit (> 127)
character is encountered, the value wraps around to negative, and a
read occurs outside of the actual state table, causing potentially
incorrect behaviour or a crash.

src/cmd/ksh93/sh/lex.c:
- endword(): Make sp and three related variables explicitly
  unsigned char pointers. This requires a bunch of annoying
  typecasts to stop compilers complaining; so be it.
- To avoid even more typecasts, make stack_shift() follow suit.
- Reorder variable declarations for legibility.
This commit is contained in:
Martijn Dekker 2022-07-11 00:35:10 +02:00
parent 6728720f8f
commit 3ce064bbba
2 changed files with 21 additions and 18 deletions

3
NEWS
View file

@ -7,6 +7,9 @@ Any uppercase BUG_* names are modernish shell bug IDs.
- Fixed a potential crash on retrieving an empty line from the command history. - Fixed a potential crash on retrieving an empty line from the command history.
- Fixed a potential crash in the lexical analyser on processing single-byte
characters with the highest bit set.
2022-07-09: 2022-07-09:
- Fixed a bug that broke '[[ ... ]]' test expressions for the command - Fixed a bug that broke '[[ ... ]]' test expressions for the command

View file

@ -2106,15 +2106,15 @@ noreturn void sh_syntax(Lex_t *lp)
UNREACHABLE(); UNREACHABLE();
} }
static char *stack_shift(register char *sp,char *dp) static unsigned char *stack_shift(unsigned char *sp, unsigned char *dp)
{ {
register char *ep; unsigned char *ep;
register int offset = stktell(sh.stk); int offset = stktell(sh.stk);
register int left = offset-(sp-stkptr(sh.stk,0)); int left = offset - (sp - (unsigned char*)stkptr(sh.stk, 0));
register int shift = (dp+1-sp); int shift = (dp+1-sp);
offset += shift; offset += shift;
stkseek(sh.stk,offset); stkseek(sh.stk,offset);
sp = stkptr(sh.stk,offset); sp = (unsigned char*)stkptr(sh.stk,offset);
ep = sp - shift; ep = sp - shift;
while(left--) while(left--)
*--sp = *--ep; *--sp = *--ep;
@ -2131,15 +2131,12 @@ static char *stack_shift(register char *sp,char *dp)
*/ */
static struct argnod *endword(int mode) static struct argnod *endword(int mode)
{ {
register const char *state = sh_lexstates[ST_NESTED]; const char *const state = sh_lexstates[ST_NESTED];
register int n; unsigned char *sp, *dp, *ep=0, *xp=0; /* must be unsigned: pointed-to values used as index to 256-byte state table */
register char *sp,*dp; int inquote=0, inlit=0; /* set within quoted strings */
register int inquote=0, inlit=0; /* set within quoted strings */ int n, bracket=0;
struct argnod* argp=0;
char *ep=0, *xp=0;
int bracket=0;
sfputc(sh.stk,0); sfputc(sh.stk,0);
sp = stkptr(sh.stk,ARGVAL); sp = (unsigned char*)stkptr(sh.stk,ARGVAL);
if(mbwide()) if(mbwide())
{ {
do do
@ -2178,13 +2175,16 @@ static struct argnod *endword(int mode)
switch(n) switch(n)
{ {
case S_EOF: case S_EOF:
stkseek(sh.stk,dp-stkptr(sh.stk,0)); {
struct argnod* argp=0;
stkseek(sh.stk,dp - (unsigned char*)stkptr(sh.stk,0));
if(mode<=0) if(mode<=0)
{ {
argp = (struct argnod*)stkfreeze(sh.stk,0); argp = (struct argnod*)stkfreeze(sh.stk,0);
argp->argflag = ARG_RAW|ARG_QUOTED; argp->argflag = ARG_RAW|ARG_QUOTED;
} }
return(argp); return(argp);
}
case S_LIT: case S_LIT:
if(!(inquote&1)) if(!(inquote&1))
{ {
@ -2195,8 +2195,8 @@ static struct argnod *endword(int mode)
if(ep) if(ep)
{ {
*dp = 0; *dp = 0;
stresc(ep); stresc((char*)ep);
dp = ep+ strlen(ep); dp = ep + strlen((char*)ep);
} }
ep = 0; ep = 0;
} }
@ -2300,7 +2300,7 @@ static struct argnod *endword(int mode)
{ {
inquote >>= 1; inquote >>= 1;
if(xp) if(xp)
dp = sh_checkid(xp,dp); dp = (unsigned char*)sh_checkid((char*)xp,(char*)dp);
xp = 0; xp = 0;
if(--bracket<=0 && mode<0) if(--bracket<=0 && mode<0)
inquote = 1; inquote = 1;