diff --git a/src/cmd/ksh93/bltins/typeset.c b/src/cmd/ksh93/bltins/typeset.c index 52b9c899e..d0a31da2a 100644 --- a/src/cmd/ksh93/bltins/typeset.c +++ b/src/cmd/ksh93/bltins/typeset.c @@ -800,7 +800,7 @@ static int setall(char **argv,register int flag,Dt_t *troot,struct tdata *tp if (tp->aflag && (tp->argnum>0 || (curflag!=newflag))) { if(shp->subshell) - sh_assignok(np,3); + sh_assignok(np,2); if(troot!=shp->var_tree) nv_setattr(np,newflag&~NV_ASSIGN); else diff --git a/src/cmd/ksh93/sh/subshell.c b/src/cmd/ksh93/sh/subshell.c index 87c7d65b2..504afb5ca 100644 --- a/src/cmd/ksh93/sh/subshell.c +++ b/src/cmd/ksh93/sh/subshell.c @@ -235,8 +235,6 @@ int nv_subsaved(register Namval_t *np) * add == 1: Create a copy of the node pointer in the current virtual subshell. * add == 2: This will create a copy of the node pointer like 1, but it will disable the * optimization for ${.sh.level}. - * add == 3: This is like 1, but it will never skip the following variables: - * ${.sh.level}, $_, ${.sh.subscript} and ${.sh.name}. */ Namval_t *sh_assignok(register Namval_t *np,int add) { @@ -248,11 +246,11 @@ Namval_t *sh_assignok(register Namval_t *np,int add) Namval_t *mpnext; Namarr_t *ap; unsigned int save; - /* don't bother to save if in a ${ subshare; } */ - if(sp->subshare) - return(np); - /* don't bother with this */ - if(!sp->shpwd || (add != 3 && ((add != 2 && np==SH_LEVELNOD) || np==L_ARGNOD || np==SH_SUBSCRNOD || np==SH_NAMENOD))) + /* + * Don't save if told not to (see nv_restore()) or if we're in a ${ subshare; }. + * Also, moving/copying ${.sh.level} (SH_LEVELNOD) may crash the shell. + */ + if(!sp->shpwd || shp->subshare || add<2 && np==SH_LEVELNOD) return(np); if((ap=nv_arrayptr(np)) && (mp=nv_opensub(np))) { diff --git a/src/cmd/ksh93/tests/variables.sh b/src/cmd/ksh93/tests/variables.sh index 0ffe75dd4..4fa7a1df1 100755 --- a/src/cmd/ksh93/tests/variables.sh +++ b/src/cmd/ksh93/tests/variables.sh @@ -1011,6 +1011,23 @@ $SHELL -c ' e=$? ((e == 1)) || err_exit "Failure in making one or more special variables readonly in a subshell (exit status $e)" +# ... subshell leak test +$SHELL -c ' + errors=0 + for var + do if [[ $var == .sh.level ]] + then continue # known to fail + fi + if eval "($var=bug); [[ \${$var} == bug ]]" 2>/dev/null + then echo " $0: special variable $var leaks out of subshell" >&2 + let errors++ + fi + done + exit $((errors + 1)) +' subshell_leak_test "$@" +e=$? +((e == 1)) || err_exit "One or more special variables leak out of a subshell (exit status $e)" + # ====== # ${.sh.pid} should be the forked subshell's PID (