Fix export/typeset/readonly failure at 64KiB buffer boundaries

Resolves: RHEL-137711
Co-Assisted-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vincent Mihalkovic 2026-04-14 10:58:53 +02:00
parent 4fed517046
commit 888e827743
2 changed files with 147 additions and 1 deletions

View File

@ -0,0 +1,138 @@
From: Martijn Dekker <martijn@inlv.org>
Date: Thu, 29 May 2025 00:56:48 +0100
Subject: Fix corruption in varname length calculation (re: c3651897)
# Backport: regenerated for downstream ksh 1.0.10 (c10s)
# Upstream-commit: e19960076327debb272f11041b26e7c2925d8a0a
# Also-includes: b6cd1082 (regression test)
# Also-includes: ac343fd7 + f99fbafa (line continuation fix)
diff --git a/src/cmd/ksh93/sh/lex.c b/src/cmd/ksh93/sh/lex.c
index 354bc20..d86f6ff 100644
--- a/src/cmd/ksh93/sh/lex.c
+++ b/src/cmd/ksh93/sh/lex.c
@@ -249,8 +249,7 @@ int sh_lex(Lex_t* lp)
int n, c, mode=ST_BEGIN, wordflags=0;
int inlevel=lp->lexd.level, assignment=0, ingrave=0;
int epatchar=0;
- char *varnamefirst = NULL;
- int varnamelength = 0;
+ int varnametry = 0, varnamecount = 0, varnamelength = 0;
SETLEN(1);
if(lp->lexd.paren)
{
@@ -308,7 +307,11 @@ int sh_lex(Lex_t* lp)
{
/* skip over characters in the current state */
state = sh_lexstates[mode];
- while((n=STATE(state,c))==0);
+ do {
+ n = STATE(state,c);
+ if (varnametry)
+ varnamecount += LEN;
+ } while (n == 0);
switch(n)
{
case S_BREAK:
@@ -583,6 +586,8 @@ int sh_lex(Lex_t* lp)
lp->comp_assign = 0;
return lp->token=c;
case S_ESC:
+ if(varnametry)
+ varnamecount--;
/* check for \<new-line> */
fcgetc(n);
c=2;
@@ -650,14 +655,18 @@ int sh_lex(Lex_t* lp)
}
/* FALLTHROUGH */
case S_RES:
- varnamefirst = fcseek(0) - LEN;
+ varnametry = 1;
+ varnamecount = LEN;
if(!lp->lexd.dolparen)
lp->lexd.first = fcseek(0)-LEN;
else if(lp->lexd.docword)
lp->lexd.docend = fcseek(0)-LEN;
mode = ST_NAME;
if(c=='.')
+ {
fcseek(-LEN);
+ varnamecount -= LEN;
+ }
if(n!=S_TILDE)
continue;
tilde:
@@ -1116,12 +1125,13 @@ int sh_lex(Lex_t* lp)
goto epat;
continue;
case S_EQ:
- if(varnamefirst && !varnamelength)
+ if(varnametry && !varnamelength)
{
- varnamelength = fcseek(0) - LEN - varnamefirst;
+ varnamelength = varnamecount - LEN;
if(varnamelength > 0 && fcpeek(-LEN - 1) == '+')
varnamelength--; /* += */
}
+ varnametry = 0;
assignment = lp->assignok;
/* FALLTHROUGH */
case S_COLON:
@@ -1136,8 +1146,8 @@ int sh_lex(Lex_t* lp)
}
break;
case S_BRACT:
- if(varnamefirst && !varnamelength && fcpeek(-LEN - 1)!='.')
- varnamelength = fcseek(0) - LEN - varnamefirst;
+ if(varnametry && !varnamelength && fcpeek(-LEN - 1)!='.')
+ varnamelength = varnamecount - LEN;
/* check for possible subscript */
if((n=endchar(lp))==RBRACT || n==RPAREN ||
(mode==ST_BRACE) ||
diff --git a/src/cmd/ksh93/tests/locale.sh b/src/cmd/ksh93/tests/locale.sh
index 9fae8b1..c677836 100755
--- a/src/cmd/ksh93/tests/locale.sh
+++ b/src/cmd/ksh93/tests/locale.sh
@@ -468,5 +468,16 @@ then unset s "${!LC_@}"
"(expected $(printf %q "$exp"); got $(printf %q "$got"))"
fi
+# ======
+# https://github.com/ksh93/ksh/issues/861#issuecomment-2917738184
+if ((SHOPT_MULTIBYTE))
+then export LANG=C.UTF-8
+ got=$(set +x; redirect 2>&1; readonly コーンシェル=OK; echo "$コーンシェル")
+ exp=OK
+ [[ $got == "$exp" ]] || err_exit 'declaration command assignment with multibyte variable name' \
+ "(expected $(printf %q "$exp"); got $(printf %q "$got"))"
+ unset LANG
+fi
+
# ======
exit $((Errors<125?Errors:125))
diff --git a/src/cmd/ksh93/tests/variables.sh b/src/cmd/ksh93/tests/variables.sh
index 5754085..91c3a37 100755
--- a/src/cmd/ksh93/tests/variables.sh
+++ b/src/cmd/ksh93/tests/variables.sh
@@ -1626,5 +1626,18 @@ got=$(set +x; { "$SHELL" -c '
(((e=$?)==0)) || err_exit "crash after unsetting SHLVL" \
"(expected status 0, got status $e$( ((e>128)) && print -n /SIG && kill -l "$e"))"
+# ======
+# https://github.com/ksh93/ksh/issues/861
+unset i
+typeset -i i
+for ((i=0; i<10000; i++))
+do echo "readonly this_is_very_long_variable_name_number_$i=1"
+done >issue861.sh
+chmod +x issue861.sh
+got=$(./issue861.sh 2>&1)
+[[ e=$? -eq 0 && -z $got ]] || err_exit "variable name length corrupted when reading across buffer boundary" \
+ "(got status $e, $(printf %q "$got"))"
+unset i
+
# ======
exit $((Errors<125?Errors:125))
--
2.53.0

View File

@ -4,7 +4,7 @@ URL: http://www.kornshell.com/
License: EPL-2.0
Epoch: 3
Version: 1.0.10
Release: 11%{?dist}
Release: 12%{?dist}
Source0: https://github.com/ksh93/%{name}/archive/v%{version}/%{name}-%{version}.tar.gz
Source1: kshcomp.conf
Source2: kshrc.rhs
@ -38,6 +38,10 @@ Patch8: ksh-1.0.10-issue-951-comsub-nested-func.patch
# https://github.com/ksh93/ksh/pull/948
Patch9: ksh-1.0.11-subshell-trap-leak.patch
# RHEL-137711: export/typeset/readonly fails at 64KiB buffer boundaries
# Upstream: https://github.com/ksh93/ksh/commit/e1996007 (+ b6cd1082, ac343fd7, f99fbafa)
Patch10: ksh-1.0.11-lex-varname-buffer-boundary.patch
Conflicts: pdksh
Requires: coreutils, diffutils
BuildRequires: gcc
@ -166,6 +170,10 @@ fi
%config(noreplace) %{_sysconfdir}/binfmt.d/kshcomp.conf
%changelog
* Tue Apr 14 2026 Vincent Mihalkovic <vmihalko@redhat.com> - 3:1.0.10-12
- Fix export/typeset/readonly failure at 64KiB buffer boundaries
Resolves: RHEL-137711
* Wed Apr 01 2026 Vincent Mihalkovic <vmihalko@redhat.com> - 3:1.0.10-11
- Fix trap and command-substitution memory leak
Resolves: RHEL-19580