diff --git a/NEWS b/NEWS index acb11f356..7bc53afe7 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,12 @@ Any uppercase BUG_* names are modernish shell bug IDs. - Fixed a bug introduced on 2021-01-20: if a DEBUG trap action yielded exit status 2, the execution of the next command was not skipped as documented. +- Fixed multiple buffer overflows causing crashes in typeset -L/-R-/-Z. + +- Fixed typeset -Z zero-filling: if the number was zero, all zeros + were skipped when changing the initial size value of the -Z attribute, + leaving an empty string. + 2021-02-18: - A bug was fixed in the 'read' builtin that caused it to fail to process diff --git a/src/cmd/ksh93/sh/name.c b/src/cmd/ksh93/sh/name.c index b3be067e7..aa5ccfd51 100644 --- a/src/cmd/ksh93/sh/name.c +++ b/src/cmd/ksh93/sh/name.c @@ -1573,9 +1573,7 @@ void nv_putval(register Namval_t *np, const char *string, int flags) Shell_t *shp = sh_getinterp(); register const char *sp=string; register union Value *up; - register char *cp; register int size = 0; - register int dot; int was_local = nv_local; union Value u; #if SHOPT_FIXEDARRAY @@ -1788,7 +1786,7 @@ void nv_putval(register Namval_t *np, const char *string, int flags) else { const char *tofree=0; - int offset=0,append; + int offset = 0; #if _lib_pathnative char buff[PATH_MAX]; #endif /* _lib_pathnative */ @@ -1871,9 +1869,13 @@ void nv_putval(register Namval_t *np, const char *string, int flags) tofree = 0; if(nv_isattr(np,NV_LJUST|NV_RJUST) && nv_isattr(np,NV_LJUST|NV_RJUST)!=(NV_LJUST|NV_RJUST)) tofree = 0; - if (sp) + if(!sp) + up->cp = NIL(char*); + else { - append=0; + char *cp = NIL(char*); /* pointer to new string */ + int dot; /* attribute or type length; defaults to string length */ + int append = 0; /* offset for appending */ if(sp==up->cp && !(flags&NV_APPEND)) return; dot = strlen(sp); @@ -1953,25 +1955,22 @@ void nv_putval(register Namval_t *np, const char *string, int flags) { if(tofree && tofree!=Empty && tofree!=Null) { - cp = (char*)realloc((void*)tofree,((unsigned)dot+append+8)); + cp = (char*)realloc((void*)tofree,((unsigned)dot+append+1)); tofree = 0; } else - cp = (char*)malloc(((unsigned)dot+8)); + cp = (char*)malloc(((unsigned)dot+append+1)); cp[dot+append] = 0; nv_offattr(np,NV_NOFREE); } } - - } - else - cp = 0; - up->cp = cp; - if(sp) - { - int c = cp[dot+append]; - memmove(cp+append,sp,dot); - cp[dot+append] = c; + if(dot) + { + char c = cp[dot+append]; + strncpy(cp+append, sp, dot+1); + cp[dot+append] = c; + } + up->cp = cp; if(nv_isattr(np, NV_RJUST) && nv_isattr(np, NV_ZFILL)) rightjust(cp,size,'0'); else if(nv_isattr(np, NV_LJUST|NV_RJUST)==NV_RJUST) @@ -3029,9 +3028,41 @@ void nv_newattr (register Namval_t *np, unsigned newatts, int size) if (sp = nv_getval(np)) { if(nv_isattr(np,NV_ZFILL)) - while(*sp=='0') sp++; - cp = (char*)malloc((n=strlen (sp)) + 8); - strcpy(cp, sp); + { + while(*sp=='0') sp++; /* skip initial zeros */ + if(!*sp) sp--; /* if number was 0, leave one zero */ + } + n = strlen(sp); + if(size==0 || (newatts&(NV_INTEGER|NV_BINARY))) + { + /* allocate to match existing value for numerics and auto length assignment for -L/R/Z */ + cp = (char*)malloc((size_t)n + 1); + strcpy(cp, sp); + } + else if(size>=n) + { + /* growing string */ + cp = (char*)malloc((size_t)size + 1); + strcpy(cp, sp); + } + else + { + /* shrinking string */ + cp = (char*)malloc((size_t)size + 1); + if(newatts&NV_RJUST) + strncpy(cp, n - size + sp, size); + else + { + /* NV_LJUST and the rest */ + if(newatts&NV_ZFILL) + { + while(*sp=='0') sp++; /* skip initial zeros */ + if(!*sp) sp--; /* if number was 0, leave one zero */ + } + strncpy(cp, sp, size); + } + cp[size] = '\0'; + } if(sp && (mp=nv_opensub(np))) { if(trans) diff --git a/src/cmd/ksh93/tests/attributes.sh b/src/cmd/ksh93/tests/attributes.sh index a56f4de88..73cb5f236 100755 --- a/src/cmd/ksh93/tests/attributes.sh +++ b/src/cmd/ksh93/tests/attributes.sh @@ -526,11 +526,11 @@ typeset -A expect=( [F12]='typeset -x -r -F 12 foo' [H]='typeset -x -r -H foo' [L]='typeset -x -r -L 0 foo' -# [L17]='typeset -x -r -L 17 foo' # TODO: outputs '-L 0' +# [L17]='typeset -x -r -L 17 foo' # TODO: outputs 'typeset -x -r -L 0 foo' [Mtolower]='typeset -x -r -l foo' [Mtoupper]='typeset -x -r -u foo' [R]='typeset -x -r -R 0 foo' -# [R17]='typeset -x -r -R 17 foo' # TODO: outputs '-L 0' +# [R17]='typeset -x -r -R 17 foo' # TODO: outputs 'typeset -x -r -R 0 foo' [X17]='typeset -x -r -X 17 foo' [S]='typeset -x -r foo' [T]='typeset -x -r foo' @@ -615,19 +615,27 @@ done unset expect # ====== +# Bug introduced in 0e4c4d61: could not alter the size of an existing justified string attribute # https://github.com/ksh93/ksh/issues/142#issuecomment-780931533 got=$(unset s; typeset -L 100 s=h; typeset +p s; typeset -L 5 s; typeset +p s) exp=$'typeset -L 100 s\ntypeset -L 5 s' [[ $got == "$exp" ]] || err_exit 'cannot alter size of existing justified string attribute' \ - "expected $(printf %q "$exp"), got $(printf %q "$got")" + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" got=$(unset s; typeset -L 100 s=h; typeset +p s; typeset -R 5 s; typeset +p s) exp=$'typeset -L 100 s\ntypeset -R 5 s' [[ $got == "$exp" ]] || err_exit 'cannot set -R after setting -L' \ - "expected $(printf %q "$exp"), got $(printf %q "$got")" + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" got=$(unset s; typeset -L 100 s=h; typeset +p s; typeset -Z 5 s; typeset +p s) exp=$'typeset -L 100 s\ntypeset -Z 5 -R 5 s' [[ $got == "$exp" ]] || err_exit 'cannot set -Z after setting -L' \ - "expected $(printf %q "$exp"), got $(printf %q "$got")" + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" + +# Old bug: zero-filling zero would cause the zero number to disappear +# https://github.com/ksh93/ksh/issues/142#issuecomment-782013674 +got=$(typeset -i x=0; typeset -Z5 x; echo $x; typeset -Z3 x; echo $x; typeset -Z7 x; echo $x) +exp=$'00000\n000\n0000000' +[[ $got == "$exp" ]] || err_exit 'failed to zero-fill zero' \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" # ====== exit $((Errors<125?Errors:125))