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

nv_setlist(): add check for readonly (re: 264ba48b)

One area where readonly is still ineffective is the local
environment list for a command (preceding assignments) if that
command is not executed using exec(3) after fork(2). Builtin
commands are one example. The following succeeds but should fail:

	(readonly v=1; v=2 true)  # succeeds, but should fail

If the shell is compiled with SHOPT_SPAWN (the default) then this
also applies to external commands invoked with sh_ntfork():

	(readonly v=1; v=2 env)	  # succeeds if SHOPT_SPAWN

This presents to the user as inconsitent behaviour because external
commands may be fork()ed under certain circumstances but not
others, depending on complex optimisations. One example is:

	$ ksh -c 'readonly v=1; v=2 env'
	ksh: v: is read only
	$ ksh -c 'readonly v=1; v=2 env; :'
	(bad: environment list is output, including 'v=2')

In the first command above, where 'v2=env' is the last command in
the -c script, the optimisation skips creating a scope and assigns
the environment list in the current scope.

src/cmd/ksh93/sh/name.c: nv_setlist():
- Add check for readonly. This requires searching for the variable
  in the main tree using nv_search() before a locally scoped one is
  added using nv_open(). Since nv_search() only works with plain
  variable names, temporarily end the string at '='.

src/cmd/ksh93/tests/readonly.sh:
- Add version check and fork the test command substitution subshell
  on older versions that would otherwise abort the tests due to the
  combination of an excessively low arithmetic recursion tolerance
  and a bug that sometimes fails to restore the shell's arithmetic
  recursion level.
This commit is contained in:
Martijn Dekker 2021-04-10 23:12:18 +01:00
parent 66c37202fd
commit f6bc5c03ca
2 changed files with 19 additions and 0 deletions

View file

@ -261,6 +261,7 @@ void nv_setlist(register struct argnod *arg,register int flags, Namval_t *typ)
Shell_t *shp = sh_getinterp();
register char *cp;
register Namval_t *np, *mp;
char *eqp;
char *trap=shp->st.trap[SH_DEBUGTRAP];
char *prefix = shp->prefix;
int traceon = (sh_isoption(SH_XTRACE)!=0);
@ -592,6 +593,19 @@ void nv_setlist(register struct argnod *arg,register int flags, Namval_t *typ)
cp = arg->argval;
mp = 0;
}
if(eqp = strchr(cp,'='))
{
/* Check for read-only */
*eqp = '\0';
np = nv_search(cp,shp->var_tree,0);
*eqp = '=';
if(np && nv_isattr(np,NV_RDONLY))
{
errormsg(SH_DICT,ERROR_exit(1),e_readonly,nv_name(np));
UNREACHABLE();
}
}
np = nv_open(cp,shp->prefix_root?shp->prefix_root:shp->var_tree,flags);
if(!np->nvfun && (flags&NV_NOREF))
{

View file

@ -319,6 +319,7 @@ n=${#rtests[@]}
for ((i=0; i<$n; i++))
do
got=$(
((.sh.version < 20210404)) && ulimit -t unlimited 2>/dev/null # fork to dodge an arith recursion detection bug
trap "${rtests[$i].res}" EXIT
eval "${rtests[$i].ini}"
eval "${rtests[$i].chg}" 2>&1
@ -340,5 +341,9 @@ unset i n got rtests
(readonly v=1; typeset -x v) 2>/dev/null || err_exit "readonly variable cannot be exported (2)"
(readonly v=1; typeset -rx v) 2>/dev/null || err_exit "readonly variable cannot be set readonly and exported"
# ======
# the environment list was not checked for readonly for commands that are not fork()ed
(readonly v=1; v=2 true) 2>/dev/null && err_exit 'readonly not verified for command environment list'
# ======
exit $((Errors<125?Errors:125))