diff --git a/NEWS b/NEWS index 5a095c2a7..638e062a5 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,12 @@ For full details, see the git log at: https://github.com/ksh93/ksh Any uppercase BUG_* names are modernish shell bug IDs. +2021-04-04: + +- Harden readonly variables. Readonly variables or arrays no longer allow + attribute changes which would otherwise allow their value to be altered. + Expanded support for readonly variables within multidimensional arrays. + 2021-04-03: - Fixed a bug that caused the uname builtin's -d option to change the output diff --git a/src/cmd/ksh93/bltins/typeset.c b/src/cmd/ksh93/bltins/typeset.c index 198ff15d9..80761dd47 100644 --- a/src/cmd/ksh93/bltins/typeset.c +++ b/src/cmd/ksh93/bltins/typeset.c @@ -732,6 +732,11 @@ static int setall(char **argv,register int flag,Dt_t *troot,struct tdata *tp np = nv_open(name,troot,nvflags|((nvflags&NV_ASSIGN)?0:NV_ARRAY)|((iarray|(nvflags&(NV_REF|NV_NOADD)==NV_REF))?NV_FARRAY:0)); if(!np) continue; + if(np->nvflag&NV_RDONLY && !tp->pflag && (flag & ~NV_NOFREE) != NV_RDONLY) + { + errormsg(SH_DICT,ERROR_exit(1),e_readonly,nv_name(np)); + UNREACHABLE(); + } if(nv_isnull(np) && !nv_isarray(np) && nv_isattr(np,NV_NOFREE)) nv_offattr(np,NV_NOFREE); else if(tp->tp && !nv_isattr(np,NV_MINIMAL|NV_EXPORT) && (mp=(Namval_t*)np->nvenv) && (ap=nv_arrayptr(mp)) && (ap->nelem&ARRAY_TREE)) @@ -889,14 +894,7 @@ static int setall(char **argv,register int flag,Dt_t *troot,struct tdata *tp } } else - { - if((flag&NV_RDONLY) && (curflag&NV_RDONLY)) - { - errormsg(SH_DICT,ERROR_exit(1),e_readonly,nv_name(np)); - UNREACHABLE(); - } newflag = curflag & ~flag; - } if (tp->aflag && (tp->argnum || (curflag!=newflag))) { if(shp->subshell) diff --git a/src/cmd/ksh93/include/version.h b/src/cmd/ksh93/include/version.h index 3780e731f..01cbcd8a7 100644 --- a/src/cmd/ksh93/include/version.h +++ b/src/cmd/ksh93/include/version.h @@ -20,7 +20,7 @@ #define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */ #define SH_RELEASE_SVER "1.0.0-alpha" /* semantic version number: https://semver.org */ -#define SH_RELEASE_DATE "2021-04-03" /* must be in this format for $((.sh.version)) */ +#define SH_RELEASE_DATE "2021-04-04" /* must be in this format for $((.sh.version)) */ #define SH_RELEASE_CPYR "(c) 2020-2021 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/array.c b/src/cmd/ksh93/sh/array.c index e119141ac..b0c00a856 100644 --- a/src/cmd/ksh93/sh/array.c +++ b/src/cmd/ksh93/sh/array.c @@ -1522,6 +1522,15 @@ char *nv_endsubscript(Namval_t *np, register char *cp, int mode) } if(mode && np) { + /* Block an attempt to alter a readonly array via subscript assignment or by appending the array. + However need to allow instances of type variables. This exception is observed when np->nvflag + has NV_BINARY and NV_LJUST set besides NV_RDONLY and NV_ARRAY. */ + if(nv_isattr(np,NV_RDONLY) && nv_isattr(np,NV_ARRAY) && mode&NV_ASSIGN && np->nvflag&(NV_BINARY|NV_LJUST)^(NV_BINARY|NV_LJUST)) + { + errormsg(SH_DICT,ERROR_exit(1),e_readonly,nv_name(np)); + UNREACHABLE(); + } + Namarr_t *ap = nv_arrayptr(np); int scan = 0; #if SHOPT_FIXEDARRAY diff --git a/src/cmd/ksh93/sh/streval.c b/src/cmd/ksh93/sh/streval.c index 9a4e552c5..fa94d863b 100644 --- a/src/cmd/ksh93/sh/streval.c +++ b/src/cmd/ksh93/sh/streval.c @@ -47,7 +47,7 @@ # define SH_DICT "libshell" #endif -#define MAXLEVEL 9 +#define MAXLEVEL 1024 #define SMALL_STACK 12 /* diff --git a/src/cmd/ksh93/tests/readonly.sh b/src/cmd/ksh93/tests/readonly.sh new file mode 100644 index 000000000..cb8c29b11 --- /dev/null +++ b/src/cmd/ksh93/tests/readonly.sh @@ -0,0 +1,335 @@ +######################################################################## +# # +# This file is part of the ksh 93u+m package # +# Copyright (c) 1982-2012 AT&T Intellectual Property # +# Copyright (c) 2021 Contributors to ksh 93u+m # +# # +# and is licensed under the # +# Eclipse Public License, Version 1.0 # +# # +# A copy of the License is available at # +# http://www.eclipse.org/org/documents/epl-v10.html # +# (with md5 checksum b35adb5213ca9657e911e9befb180842) # +# # +# hyenias # +# # +######################################################################## + +. "${SHTESTS_COMMON:-${0%/*}/_common}" + +rtests=( + ( + ini='typeset -ri x=900' + chg='typeset -R1 x' + res='typeset -p x' + exp='typeset -r -i x=900' + ) + ( + ini='typeset -ri x=900' + chg='typeset -X x; typeset -L1 x; typeset -i x' + res='typeset -p x' + exp='typeset -r -i x=900' + ) + ( + ini='typeset -L3 x=0123; readonly x' + chg='typeset -Z5 x' + res='typeset -p x' + exp='typeset -r -L 3 x=012' + ) + ( + ini='typeset -rL3 x=0123' + chg='typeset -R5 x' + res='typeset -p x' + exp='typeset -r -L 3 x=012' + ) + ( + ini='typeset -rL3 x=0123' + chg='typeset -L5 x' + res='typeset -p x' + exp='typeset -r -L 3 x=012' + ) + ( + ini='typeset -R3 x; typeset -r x' + chg='x=0' + res='typeset -p x' + exp='typeset -r -R 3 x' + ) + ( + ini='set -A arr[0] a b c; readonly arr' + chg='arr[1]=1' + res='typeset -p arr' + exp='typeset -r -a arr=((a b c) )' + ) + ( + ini='set -A arr[0] a b c; readonly arr' + chg='arr[0][1]=d' + res='typeset -p arr' + exp='typeset -r -a arr=((a b c) )' + ) + ( + ini='set -A arr[0] a b c; readonly arr' + chg='set +A arr 1' + res='typeset -p arr' + exp='typeset -r -a arr=((a b c) )' + ) + ( + ini='typeset -a arr=((a b c) 1); readonly arr' + chg='arr[1]=d' + res='typeset -p arr' + exp='typeset -r -a arr=((a b c) 1)' + ) + ( + ini='typeset -ra arr=((a b c) 1)' + chg='arr[1]=d' + res='typeset -p arr' + exp='typeset -r -a arr=((a b c) 1)' + ) + ( + ini='typeset -ra arr=((a b c) 1)' + chg='arr[1]=()' + res='typeset -p arr' + exp='typeset -r -a arr=((a b c) 1)' + ) + ( + ini='typeset -r -a arr=((a b c) 1)' + chg='arr[0][1]=d' + res='typeset -p arr' + exp='typeset -r -a arr=((a b c) 1)' + ) + ( + ini='typeset -r -a arr=((a b c) 1)' + chg='arr[0][1]=()' + res='typeset -p arr' + exp='typeset -r -a arr=((a b c) 1)' + ) + ( + ini='typeset -r -a arr=((a b c) 1)' + chg='arr[0][1]=(d)' + res='typeset -p arr' + exp='typeset -r -a arr=((a b c) 1)' + ) + (# For a move, the unset should be blocked but copy succeeds. + ini='readonly old=RDONLY' + chg='typeset -m new=old' + res='typeset -p old new' + exp=$'typeset -r old=RDONLY\nnew=RDONLY' + ) + ( + ini='typeset -C arr=(typeset -r -a alphas=(a b c);name=x)' + chg='arr.name=y; arr.alphas[1]=b' + res='typeset -p arr' + exp='typeset -C arr=(typeset -r -a alphas=(a b c);name=y)' + ) + ( + ini='typeset -C arr=(typeset -r -a alphas=(a b c);name=x)' + chg='arr.name=y; arr.alphas[1]=()' + res='typeset -p arr' + exp='typeset -C arr=(typeset -r -a alphas=(a b c);name=y)' + ) + ( + ini='typeset -C arr=(typeset -r -a alphas=(a b c);name=x)' + chg='arr.name=y; arr.alphas[1]=(b)' + res='typeset -p arr' + exp='typeset -C arr=(typeset -r -a alphas=(a b c);name=y)' + ) + ( + ini='arr=(alphas=(a b c);name=x); readonly arr.alphas' + chg='arr.alphas[1]=([b]=5)' + res='typeset -p arr arr.alphas' + exp=$'typeset -C arr=(typeset -r -a alphas=(a b c);name=x)\ntypeset -r -a arr.alphas=(a b c)' + ) + ( + ini='arr=(alphas=(a b c);name=x); readonly arr.alphas' + chg='arr.alphas[1]=(b)' + res='typeset -p arr arr.alphas' + exp=$'typeset -C arr=(typeset -r -a alphas=(a b c);name=x)\ntypeset -r -a arr.alphas=(a b c)' + ) + ( + ini='typeset -ra -E arr=(0 1 2 3)' + chg='arr[1]=()' + res='typeset -p arr' + exp='typeset -r -a -E arr=(0 1 2 3)' + ) + ( + ini='typeset -T FB_t=(typeset x=foo y=bar; typeset -r z=${_.y}; create() { _.y=bam; }; )' + chg='FB_t fb; fb.z=foo' + res='typeset -p fb' + exp='FB_t fb=(x=foo;y=bam;typeset -r z=bar)' + ) + ( + ini='typeset -ra arr=((a b c) 1)' + chg='arr+=(2)' + res='typeset -p arr' + exp='typeset -r -a arr=((a b c) 1)' + ) + ( + ini='typeset -ra arr=((a b c) 1)' + chg='arr[0]+=(d)' + res='typeset -p arr' + exp='typeset -r -a arr=((a b c) 1)' + ) + ( + ini='typeset -ra -i arr=((10 11 12) 3 4)' + chg='(( arr[1] += 2 ))' + res='typeset -p arr' + exp='typeset -r -a -i arr=((10 11 12) 3 4)' + ) + ( + ini='typeset -ra -i arr=((10 11 12) 3 4)' + chg='(( arr[1] -= 2 ))' + res='typeset -p arr' + exp='typeset -r -a -i arr=((10 11 12) 3 4)' + ) + ( + ini='typeset -ra -i arr=((10 11 12) 3 4)' + chg='(( arr[1] *= 2 ))' + res='typeset -p arr' + exp='typeset -r -a -i arr=((10 11 12) 3 4)' + ) + ( + ini='typeset -ra -i arr=((10 11 12) 3 4)' + chg='(( arr[1] /= 2 ))' + res='typeset -p arr' + exp='typeset -r -a -i arr=((10 11 12) 3 4)' + ) + ( + ini='typeset -ra -i arr=((10 11 12) 3 4)' + chg='(( arr[0][1] += 2 ))' + res='typeset -p arr' + exp='typeset -r -a -i arr=((10 11 12) 3 4)' + ) + ( + ini='typeset -ra -i arr=((10 11 12) 3 4)' + chg='(( arr[0][1] -= 2 ))' + res='typeset -p arr' + exp='typeset -r -a -i arr=((10 11 12) 3 4)' + ) + ( + ini='typeset -ra -i arr=((10 11 12) 3 4)' + chg='(( arr[0][1] *= 2 ))' + res='typeset -p arr' + exp='typeset -r -a -i arr=((10 11 12) 3 4)' + ) + ( + ini='typeset -ra -i arr=((10 11 12) 3 4)' + chg='(( arr[0][1] /= 2 ))' + res='typeset -p arr' + exp='typeset -r -a -i arr=((10 11 12) 3 4)' + ) + ( + ini='typeset -r -A arr=([a]=10 [b]=20 [c]=30)' + chg='arr[b]=40' + res='typeset -p arr' + exp='typeset -r -A arr=([a]=10 [b]=20 [c]=30)' + ) + ( + ini='typeset -r -A arr=([a]=10 [b]=20 [c]=30)' + chg='arr[b]=()' + res='typeset -p arr' + exp='typeset -r -A arr=([a]=10 [b]=20 [c]=30)' + ) + ( + ini='typeset -r -A arr=([a]=10 [b]=20 [c]=30)' + chg='arr[b]=(40)' + res='typeset -p arr' + exp='typeset -r -A arr=([a]=10 [b]=20 [c]=30)' + ) + ( + ini='typeset -r -A arr=([a]=([10]=X [11]=XI [12]=XII) [b]=20)' + chg='arr[c]=30' + res='typeset -p arr' + exp='typeset -r -A arr=([a]=([10]=X [11]=XI [12]=XII) [b]=20)' + ) + ( + ini='typeset -r -A arr=([a]=([10]=X [11]=XI [12]=XII) [b]=20)' + chg='arr[c]+=30' + res='typeset -p arr' + exp='typeset -r -A arr=([a]=([10]=X [11]=XI [12]=XII) [b]=20)' + ) + ( + ini='typeset -r -A arr=([a]=([10]=X [11]=XI [12]=XII) [b]=20)' + chg='arr[a][11]=0xb' + res='typeset -p arr' + exp='typeset -r -A arr=([a]=([10]=X [11]=XI [12]=XII) [b]=20)' + ) + ( + ini='typeset -r -A arr=([a]=([10]=X [11]=XI [12]=XII) [b]=20)' + chg='arr[a][11]+=0xb' + res='typeset -p arr' + exp='typeset -r -A arr=([a]=([10]=X [11]=XI [12]=XII) [b]=20)' + ) + ( + ini='typeset -r -A arr=([a]=([10]=X [11]=XI [12]=XII) [b]=20)' + chg='arr[a][13]=XIII' + res='typeset -p arr' + exp='typeset -r -A arr=([a]=([10]=X [11]=XI [12]=XII) [b]=20)' + ) + ( + ini='typeset -r -A -i arr=([a]=10 [b]=20 [c]=30)' + chg='(( arr[b] += 5 ))' + res='typeset -p arr' + exp='typeset -r -A -i arr=([a]=10 [b]=20 [c]=30)' + ) + ( + ini='typeset -r -A -i arr=([a]=10 [b]=20 [c]=30)' + chg='(( arr[b] -= 5 ))' + res='typeset -p arr' + exp='typeset -r -A -i arr=([a]=10 [b]=20 [c]=30)' + ) + ( + ini='typeset -r -A -i arr=([a]=10 [b]=20 [c]=30)' + chg='(( arr[b] *= 5 ))' + res='typeset -p arr' + exp='typeset -r -A -i arr=([a]=10 [b]=20 [c]=30)' + ) + ( + ini='typeset -r -A -i arr=([a]=10 [b]=20 [c]=30)' + chg='(( arr[b] /= 5 ))' + res='typeset -p arr' + exp='typeset -r -A -i arr=([a]=10 [b]=20 [c]=30)' + ) + ( + ini='typeset -r -A -i arr=([a]=([X]=10 [XI]=11 [XII]=12) [b]=20)' + chg='(( arr[a][XI] += 5 ))' + res='typeset -p arr' + exp='typeset -r -A -i arr=([a]=([X]=10 [XI]=11 [XII]=12) [b]=20)' + ) + ( + ini='typeset -r -A -i arr=([a]=([X]=10 [XI]=11 [XII]=12) [b]=20)' + chg='(( arr[a][XI] -= 5 ))' + res='typeset -p arr' + exp='typeset -r -A -i arr=([a]=([X]=10 [XI]=11 [XII]=12) [b]=20)' + ) + ( + ini='typeset -r -A -i arr=([a]=([X]=10 [XI]=11 [XII]=12) [b]=20)' + chg='(( arr[a][XI] *= 5 ))' + res='typeset -p arr' + exp='typeset -r -A -i arr=([a]=([X]=10 [XI]=11 [XII]=12) [b]=20)' + ) + ( + ini='typeset -r -A -i arr=([a]=([X]=10 [XI]=11 [XII]=12) [b]=20)' + chg='(( arr[a][XI] /= 5 ))' + res='typeset -p arr' + exp='typeset -r -A -i arr=([a]=([X]=10 [XI]=11 [XII]=12) [b]=20)' + ) +) + +typeset -i i +n=${#rtests[@]} +for ((i=0; i<$n; i++)) +do + got=$( + trap "${rtests[$i].res}" EXIT + eval "${rtests[$i].ini}" + eval "${rtests[$i].chg}" 2>&1 + ) + [[ $got == *$': is read only\n'* ]] || err_exit "Readonly variable did not warn for rtests[$i]: "\ + "setup='${rtests[$i].ini}', change='${rtests[$i].chg}'" + got=${got#*$': is read only\n'} + [[ ${rtests[$i].exp} == "$got" ]] || err_exit "Readonly variable changed on rtests[$i]: "\ + "expected '${rtests[$i].exp}', got '$got'" +done +unset i n got rtests + +# ====== +exit $((Errors<125?Errors:125))