From 888e8277430bae236413a7a367b7fec3214a574c Mon Sep 17 00:00:00 2001 From: Vincent Mihalkovic Date: Tue, 14 Apr 2026 10:58:53 +0200 Subject: [PATCH] Fix export/typeset/readonly failure at 64KiB buffer boundaries Resolves: RHEL-137711 Co-Assisted-By: Claude Opus 4.6 --- ksh-1.0.11-lex-varname-buffer-boundary.patch | 138 +++++++++++++++++++ ksh.spec | 10 +- 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 ksh-1.0.11-lex-varname-buffer-boundary.patch diff --git a/ksh-1.0.11-lex-varname-buffer-boundary.patch b/ksh-1.0.11-lex-varname-buffer-boundary.patch new file mode 100644 index 0000000..d31e979 --- /dev/null +++ b/ksh-1.0.11-lex-varname-buffer-boundary.patch @@ -0,0 +1,138 @@ +From: Martijn Dekker +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 \ */ + 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 + diff --git a/ksh.spec b/ksh.spec index 34e16d4..9639fd1 100644 --- a/ksh.spec +++ b/ksh.spec @@ -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 - 3:1.0.10-12 +- Fix export/typeset/readonly failure at 64KiB buffer boundaries + Resolves: RHEL-137711 + * Wed Apr 01 2026 Vincent Mihalkovic - 3:1.0.10-11 - Fix trap and command-substitution memory leak Resolves: RHEL-19580