From e6d0187dd8680db591dc43dd3481725108d1206a Mon Sep 17 00:00:00 2001 From: Martijn Dekker Date: Tue, 8 Feb 2022 18:33:44 +0000 Subject: [PATCH] Don't allow 'enum' and 'typeset -T' to override special built-ins Special builtins are undeleteable for a reason. But 'enum' and 'typeset -T' allow overriding them, causing an inconsistent state. @JohnoKing writes: | The behavior is rather buggy, as it appears to successfully | override normal builtins but fails to delete the special | builtins, leading to scenarios where both the original builtin | and type are run: | | $ typeset -T eval=(typeset BAD; typeset TYPE) # This should have failed | $ eval foo=BAD | /usr/bin/ksh: eval: line 1: foo: not found | $ enum trap=(BAD TYPE) # This also should have failed | $ trap foo=BAD | /usr/bin/ksh: trap: condition(s) required | $ enum umask=(BAD TYPE) | $ umask foo=BAD | $ echo $foo | BAD | | # Examples of general bugginess | $ trap bar=TYPE | /usr/bin/ksh: trap: condition(s) required | $ echo $bar | TYPE | $ eval var=TYPE | /usr/bin/ksh: eval: line 1: var: not found | $ echo $var | TYPE This commit fixes the following: The 'enum' and 'typeset -T' commands are no longer allowed to override and replace special built-in commands, except for type definition commands previously created by these commands; these are already (dis)allowed elsewhere. A command like 'typeset -T foo_t' without any assignments no longer creates an incompletely defined 'foo_t' built-in comamnd. Instead, it is now silently ignored for backwards compatibility. This did have a regression test checking for it, but I'm changing it because that's just not a valid use case. An incomplete type definition command does nothing useful and only crashes the shell when run. src/cmd/ksh93/bltins/enum.c: b_enum(): - Do not allow overriding non-type special built-ins. src/cmd/ksh93/sh/name.c: nv_setlist(): - Do not allow 'typeset -T' to override non-type special built-ins. To avoid an inconsistent state, this must be checked for while processing the assignments list before typeset is really invoked. src/cmd/ksh93/bltins_typeset.c: b_typeset(): - Only create a type command if sh.envlist is set, i.e., if some shell assignment(s) were passed to the 'typeset -T' command. Progresses: https://github.com/ksh93/ksh/issues/350 --- NEWS | 11 +++++++++++ src/cmd/ksh93/COMPATIBILITY | 4 ++++ src/cmd/ksh93/bltins/enum.c | 9 +++++++++ src/cmd/ksh93/bltins/typeset.c | 2 +- src/cmd/ksh93/include/version.h | 2 +- src/cmd/ksh93/sh/name.c | 7 +++++++ src/cmd/ksh93/tests/enum.sh | 7 +++++++ src/cmd/ksh93/tests/types.sh | 12 ++++++++++-- 8 files changed, 50 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index ff7aac20b..f6d47edae 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,17 @@ For full details, see the git log at: https://github.com/ksh93/ksh/tree/1.0 Any uppercase BUG_* names are modernish shell bug IDs. +2022-02-08: + +- To avoid an inconsistent state, the 'enum' and 'typeset -T' commands are + no longer allowed to replace special built-in commands. For instance, + 'enum trap=(a b c)' is now an error because 'trap' is a special built-in. + The ability of 'typeset -T' to redefine a type is not affected. + +- A command like 'typeset -T foo_t' without any assignment no longer creates + an incompletely defined 'foo_t' built-in comamnd that will crash the shell + when used. Instead, it is now silently ignored for backwards compatibility. + 2022-02-05: - Fixed: for indexed arrays, given an unset array member a[i] with i > 0, diff --git a/src/cmd/ksh93/COMPATIBILITY b/src/cmd/ksh93/COMPATIBILITY index f80020aea..94aec7dc9 100644 --- a/src/cmd/ksh93/COMPATIBILITY +++ b/src/cmd/ksh93/COMPATIBILITY @@ -168,6 +168,10 @@ For more details, see the NEWS file and for complete details, see the git log. return value that fits in a signed integer, typically a 32-bit value. Note that $? is truncated to 8 bits when the current (sub)shell exits. +31. The 'enum' and 'typeset -T' commands are no longer allowed to + override and replace special built-in commands, except for type + definition commands previously created by these commands. + ____________________________________________________________________________ KSH-93 VS. KSH-88 diff --git a/src/cmd/ksh93/bltins/enum.c b/src/cmd/ksh93/bltins/enum.c index 7993064a3..a085f7616 100644 --- a/src/cmd/ksh93/bltins/enum.c +++ b/src/cmd/ksh93/bltins/enum.c @@ -88,6 +88,8 @@ static const char enum_type[] = "[+SEE ALSO?\benum\b(1), \btypeset\b(1)]" ; +extern const char is_spcbuiltin[]; + struct Enum { Namfun_t hdr; @@ -243,6 +245,13 @@ int b_enum(int argc, char** argv, Shbltin_t *context) #endif while(cp = *argv++) { + /* Do not allow 'enum' to override special built-ins -- however, exclude + * previously created type commands from this search as that is handled elsewhere. */ + if((tp=nv_search(cp,sh.bltin_tree,0)) && nv_isattr(tp,BLT_SPC) && !nv_search(cp,sh.typedict,0)) + { + errormsg(SH_DICT,ERROR_exit(1),"%s:%s",cp,is_spcbuiltin); + UNREACHABLE(); + } if(!(np = nv_open(cp, (void*)0, NV_VARNAME|NV_NOADD)) || !(ap=nv_arrayptr(np)) || ap->fun || (sz=ap->nelem&(((1L<nvenv = tdata.help; flag &= ~NV_TYPE; diff --git a/src/cmd/ksh93/include/version.h b/src/cmd/ksh93/include/version.h index 9d4263dc2..b889ee203 100644 --- a/src/cmd/ksh93/include/version.h +++ b/src/cmd/ksh93/include/version.h @@ -21,7 +21,7 @@ #define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */ #define SH_RELEASE_SVER "1.0.0-beta.2" /* semantic version number: https://semver.org */ -#define SH_RELEASE_DATE "2022-02-05" /* must be in this format for $((.sh.version)) */ +#define SH_RELEASE_DATE "2022-02-08" /* must be in this format for $((.sh.version)) */ #define SH_RELEASE_CPYR "(c) 2020-2022 Contributors to ksh " SH_RELEASE_FORK /* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */ diff --git a/src/cmd/ksh93/sh/name.c b/src/cmd/ksh93/sh/name.c index 521e36569..1c4591684 100644 --- a/src/cmd/ksh93/sh/name.c +++ b/src/cmd/ksh93/sh/name.c @@ -319,6 +319,13 @@ void nv_setlist(register struct argnod *arg,register int flags, Namval_t *typ) cp = sh_mactrim(fp->fornam,-1); else cp = fp->fornam; + /* Do not allow 'typeset -T' to override special built-ins -- however, exclude + * previously created type commands from this search as that is handled elsewhere. */ + if(maketype && (np=nv_search(cp,sh.bltin_tree,0)) && nv_isattr(np,BLT_SPC) && !nv_search(cp,sh.typedict,0)) + { + errormsg(SH_DICT,ERROR_exit(1),"%s:%s",cp,is_spcbuiltin); + UNREACHABLE(); + } error_info.line = fp->fortyp-sh.st.firstline; if(!array && tp->tre.tretyp!=TLST && tp->com.comset && !tp->com.comarg && tp->com.comset->argval[0]==0 && tp->com.comset->argval[1]=='[') array |= (tp->com.comset->argflag&ARG_MESSAGE)?NV_IARRAY:NV_ARRAY; diff --git a/src/cmd/ksh93/tests/enum.sh b/src/cmd/ksh93/tests/enum.sh index af1fb3832..bcc7eeb7a 100755 --- a/src/cmd/ksh93/tests/enum.sh +++ b/src/cmd/ksh93/tests/enum.sh @@ -161,5 +161,12 @@ then enum PARSER2_t=(a b) fi PATH=/dev/null command -v PARSER2_t >/dev/null && err_exit "PARSER2_t incompletely defined though definition was never executed" +# ====== +# https://github.com/ksh93/ksh/issues/350#issuecomment-982168684 +got=$("$SHELL" -c 'enum trap=(BAD TYPE)' 2>&1) +exp=': trap: is a special shell builtin' +[[ $got == *"$exp" ]] || err_exit "enum overrides special builtin" \ + "(expected match of *$(printf %q "$exp"); got $(printf %q "$got"))" + # ====== exit $((Errors<125?Errors:125)) diff --git a/src/cmd/ksh93/tests/types.sh b/src/cmd/ksh93/tests/types.sh index f7fcf36a0..ced406e81 100755 --- a/src/cmd/ksh93/tests/types.sh +++ b/src/cmd/ksh93/tests/types.sh @@ -565,8 +565,9 @@ unset z u_t -a x | read z [[ $z == unset ]] && err_exit 'unset discipline called on type creation' -{ z=$($SHELL 2> /dev/null 'typeset -T foo; typeset -T') ;} 2> /dev/null -[[ $z == 'typeset -T foo' ]] || err_exit '"typeset -T foo; typeset -T" failed' +got=$("$SHELL" -c 'typeset -T foo; typeset -T' 2>&1) +[[ -z $got ]] || err_exit '"typeset -T foo; typeset -T" exposed incomplete type builtin' \ + "(got $(printf %q "$got"))" { z=$($SHELL 2> /dev/null 'typeset -T foo=bar; typeset -T') ;} 2> /dev/null [[ $z ]] && err_exit '"typeset -T foo=bar" should not creates type foo' @@ -657,5 +658,12 @@ exp='Subsh_t -a v=((typeset -i x=1) (typeset -i x=2) (typeset -i x=3))' "(expected $(printf %q "$exp"), got $(printf %q "$got"))" PATH=/dev/null command -v Subsh_t >/dev/null && err_exit "Subsh_t leaked out of subshell" +# ====== +# https://github.com/ksh93/ksh/issues/350#issuecomment-982168684 +got=$("$SHELL" -c 'typeset -T trap=( typeset -i i )' 2>&1) +exp=': trap: is a special shell builtin' +[[ $got == *"$exp" ]] || err_exit "typeset -T overrides special builtin" \ + "(expected match of *$(printf %q "$exp"); got $(printf %q "$got"))" + # ====== exit $((Errors<125?Errors:125))