1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-11-24 14:01:03 +01:00

54064: avoid crash on named references to argv/ARGC, improve valid_nameref()

Leaves some edgecase issues unresolved (see tests).
This commit is contained in:
Bart Schaefer 2025-11-10 15:13:36 -08:00
parent 45b6de088a
commit 8eec2c793c
3 changed files with 134 additions and 26 deletions

View file

@ -1,3 +1,9 @@
2025-11-10 Bart Schaefer <schaefer@zsh.org>
* 54064: Src/params.c, Test/K01nameref.ztst: avoid crash on named
references to argv/ARGC, improve syntax checks in valid_nameref().
Leaves some edgecase issues unresolved (see tests).
2025-11-10 Oliver Kiddle <opk@zsh.org>
* 53402, 54042: Src/Zle/termquery.c, Src/Zle/zle_main.c,

View file

@ -1309,7 +1309,6 @@ isident(char *s)
/* Require balanced [ ] pairs with something between */
if (!(ss = parse_subscript(++ss, 1, ']')))
return 0;
untokenize(s);
return !ss[1];
}
@ -3247,8 +3246,8 @@ assignsparam(char *s, char *val, int flags)
return NULL;
}
if (*val && (v->pm->node.flags & PM_NAMEREF)) {
if (!valid_refname(val)) {
zerr("invalid variable name: %s", val);
if (!valid_refname(val, v->pm->node.flags)) {
zerr("invalid name reference: %s", val);
zsfree(val);
unqueue_signals();
errflag |= ERRFLAG_ERROR;
@ -6505,30 +6504,48 @@ upscope_upper(Param pm, int reflevel)
/**/
static int
valid_refname(char *val)
valid_refname(char *val, int flags)
{
char *t = itype_end(val, INAMESPC, 0);
char *t;
if (idigit(*val))
return 0;
if (*t != 0) {
if (*t == '[') {
tokenize(t = dupstring(t+1));
while ((t = parse_subscript(t, 0, ']')) && *t++ == Outbrack) {
if (*t == Inbrack)
++t;
else
break;
}
if (t && *t) {
/* zwarn("%s: stuff after subscript: %s", val, t); */
t = NULL;
}
} else if (t[1] || !(*t == '!' || *t == '?' ||
*t == '$' || *t == '-' ||
*t == '0' || *t == '_')) {
/* Skipping * @ # because of doshfunc() implementation */
t = NULL;
if (flags & PM_UPPER) {
/* Upward reference to positionals is doomed to fail */
if (idigit(*val))
return 0;
t = itype_end(val, INAMESPC, 0);
if ((t - val == 4) &&
(!strncmp(val, "argv", 4) ||
!strncmp(val, "ARGC", 4)))
return 0;
} else if (idigit(*val)) {
t = val;
while (*++t)
if (!idigit(*t))
break;
if (*t && *t != '[') /* Need to test Inbrack here too? */
return 0;
} else
t = itype_end(val, INAMESPC, 0);
if (t == val) {
if (!(*t == '!' || *t == '?' ||
*t == '$' || *t == '-' ||
*t == '_'))
return 0;
++t;
}
if (*t == '[') {
/* Another bit of isident() to emulate */
tokenize(t = dupstring(t+1));
while ((t = parse_subscript(t, 0, ']')) && *t++ == Outbrack) {
if (*t == Inbrack)
++t;
else
break;
}
if (t && *t) {
/* zwarn("%s: stuff after subscript: %s", val, t); */
return 0;
}
}
return !!t;

View file

@ -531,7 +531,7 @@ F:ksh93 does not implement this either
unset -n ptr1
typeset -n ptr1='not[2]good'
1:invalid nameref
*?*invalid variable name: not\[2\]good
*?*invalid name reference: not\[2\]good
unset -n ptr1
unset hash
@ -1181,6 +1181,91 @@ F:previously this could create an infinite recursion and crash
>h2: ref1=f1 ref2=f1 ref3=f1
>f1: ref1=f1 ref2=f1 ref3=f1
#
# The following two tests are linked, do not separate
#
edgelocal() ( local -n x=$1; typeset -p x; print -r $x )
edgeupper() ( local -nu x=$1; typeset -p x; print -r $x )
for edge in argv ARGC \@ \* \# 0 1 01 \! \? - _
do
edgelocal $edge
edgelocal "$edge""[1]"
edgeupper $edge
done
0:references to builtin specials
F:Subscripting on 1 01 ! ? - should print first character but do not
>typeset -n x=argv
>argv
>typeset -n x='argv[1]'
>argv[1]
>typeset -n x=ARGC
>1
>typeset -n x='ARGC[1]'
>1
>typeset -n x=0
>edgelocal
>typeset -n x='0[1]'
>e
>typeset -n x=1
>
>typeset -n x='1[1]'
>
>typeset -n x=01
>
>typeset -n x='01[1]'
>
>typeset -n x=!
>0
>typeset -n x='![1]'
>
>typeset -un x=!
>0
>typeset -n x='?'
>0
>typeset -n x='?[1]'
>
>typeset -un x='?'
>0
>typeset -n x=-
>569X
>typeset -n x='-[1]'
>
>typeset -un x=-
>569X
>typeset -n x=_
>x
>typeset -n x='_[1]'
>x
>typeset -un x=_
>x
?edgeupper: invalid name reference: argv
?edgeupper: invalid name reference: ARGC
?edgelocal: invalid name reference: @
?edgelocal: invalid name reference: @[1]
?edgeupper: invalid name reference: @
?edgelocal: invalid name reference: *
?edgelocal: invalid name reference: *[1]
?edgeupper: invalid name reference: *
?edgelocal: invalid name reference: #
?edgelocal: invalid name reference: #[1]
?edgeupper: invalid name reference: #
?edgeupper: invalid name reference: 0
?edgeupper: invalid name reference: 1
?edgeupper: invalid name reference: 01
edgelocal \$
edgelocal '$[1]'
edgeupper \$
unfunction edgelocal edgeupper
0qf:references to $$
F:$$[1] reference should print the first digit of $$ but prints nothing
>typeset -n x='$'
>$$
>typeset -n x='\$[1]'
>$$[1]
>$$
#
# The following tests are run in interactive mode, using PS1 as an
# assignable special with side-effects. This crashed at one time.