148 lines
4.5 KiB
Diff
148 lines
4.5 KiB
Diff
From 99028e9f709ee44f7c730d2883e31e37adab4d57 Mon Sep 17 00:00:00 2001
|
||
From: Martijn Dekker <martijn@inlv.org>
|
||
Date: Mon, 23 Mar 2026 22:45:04 +0000
|
||
Subject: [PATCH] Fix bug in comsubs involving nested functions with redirects
|
||
(re: e373e8c1, 274def65, 0ceb4864, 5def4398)
|
||
MIME-Version: 1.0
|
||
Content-Type: text/plain; charset=UTF-8
|
||
Content-Transfer-Encoding: 8bit
|
||
|
||
Vincent Mihalkovič (@vmihalko) from Red Hat reports:
|
||
> When executing a function in a subshell: the output is lost when
|
||
> a previous line in the function redirects to stderr while not
|
||
> having any output.
|
||
>
|
||
> ### Minimal reproducer
|
||
>
|
||
> #!/usr/bin/ksh
|
||
> function echo_devnull {
|
||
> echo "DEVNULL" >/dev/null
|
||
> }
|
||
> function func {
|
||
> echo_devnull >&2
|
||
> echo "FUNC"
|
||
> }
|
||
> OUT=$(func)
|
||
> echo "OUT: ${OUT}"
|
||
>
|
||
> Expected: 'OUT: FUNC' Actual: 'OUT:' (empty)
|
||
>
|
||
> In 'OUT=$(func)', output from 'func' is lost when a prior line in
|
||
> 'func' runs the customer pattern 'echo_devnull >&2' (redirect
|
||
> that call’s stdout to stderr) and 'echo_devnull' itself sends
|
||
> stdout to '/dev/null'. The following 'echo "FUNC"' should appear
|
||
> in 'OUT' but 'OUT' is empty. bash is OK.
|
||
|
||
src/cmd/ksh93/sh/io.c: sh_redirect():
|
||
- Amend the forking workaround yet again. Return to something
|
||
closer to the original 93u+ workaround: fork if redirections
|
||
persist past the command (flag==1 || flag==2) or standard output
|
||
is on an Sfio string buffer. All regression tests pass, so this
|
||
should not introduce the 93u+ bugs fixed in the meantime.
|
||
|
||
Resolves: https://github.com/ksh93/ksh/issues/951
|
||
|
||
Upstream-commit: 99028e9f709ee44f7c730d2883e31e37adab4d57
|
||
Resolves: RHEL-155803
|
||
---
|
||
src/cmd/ksh93/sh/io.c | 4 +-
|
||
src/cmd/ksh93/tests/subshell.sh | 67 +++++++++++++++++++++++++++++++++
|
||
2 files changed, 68 insertions(+), 3 deletions(-)
|
||
|
||
diff --git a/src/cmd/ksh93/sh/io.c b/src/cmd/ksh93/sh/io.c
|
||
index ea83132..d54c649 100644
|
||
--- a/src/cmd/ksh93/sh/io.c
|
||
+++ b/src/cmd/ksh93/sh/io.c
|
||
@@ -1143,10 +1143,8 @@ int sh_redirect(struct ionod *iop, int flag)
|
||
* 'redirect'. This forking workaround is necessary to avoid that bug.
|
||
* For shared-state comsubs, forking is incorrect, so error out then.
|
||
* TODO: actually fix the bug and remove this workaround.
|
||
- * (Note that sh.redir0 is set to 1 in xec.c immediately before processing
|
||
- * redirections for any built-in command, including 'exec' and 'redirect'.)
|
||
*/
|
||
- if(sh.subshell && sh.comsub && sh.redir0==1)
|
||
+ if(sh.subshell && sh.comsub && (flag==1 || flag==2 || (sfset(sfstdout,0,0) & SFIO_STRING)))
|
||
{
|
||
struct ionod *i;
|
||
for(i = iop; i; i = i->ionxt)
|
||
diff --git a/src/cmd/ksh93/tests/subshell.sh b/src/cmd/ksh93/tests/subshell.sh
|
||
index 7a91abf..1f896f1 100755
|
||
--- a/src/cmd/ksh93/tests/subshell.sh
|
||
+++ b/src/cmd/ksh93/tests/subshell.sh
|
||
@@ -1219,5 +1219,72 @@ got=$("$SHELL" -c 'x=$(fn(){ return 265; };echo ok|fn); echo exited $?' 2>&1)
|
||
[[ e=$? -eq 0 && $got == "$exp" ]] || err_exit "regression involving SIGPIPE in subshell" \
|
||
"(expected status 0 and $(printf %q "$exp"), got status $e and $(printf %q "$got"))"
|
||
|
||
+# ======
|
||
+# Command substitution loses stdout after nested function redirects stdout to /dev/null
|
||
+# https://github.com/ksh93/ksh/issues/951
|
||
+
|
||
+got=$(
|
||
+ function echo_devnull {
|
||
+ echo "DEVNULL" >/dev/null
|
||
+ }
|
||
+ function func {
|
||
+ echo_devnull >&2
|
||
+ echo "FUNC"
|
||
+ }
|
||
+ OUT=$(func)
|
||
+ echo "OUT: ${OUT}"
|
||
+)
|
||
+exp='OUT: FUNC'
|
||
+[[ $got == "$exp" ]] || err_exit "bug 951 test 1a (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||
+
|
||
+got=$(
|
||
+ function echo_devnull {
|
||
+ echo "DEVNULL" >/dev/null
|
||
+ }
|
||
+ function func {
|
||
+ echo_devnull >&2
|
||
+ echo "FUNC"
|
||
+ }
|
||
+ OUT=${ func; }
|
||
+ echo "OUT: ${OUT}"
|
||
+)
|
||
+exp='OUT: FUNC'
|
||
+[[ $got == "$exp" ]] || err_exit "bug 951 test 1b (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||
+
|
||
+got=$(
|
||
+ function echo_devnull {
|
||
+ ulimit -c 0
|
||
+ }
|
||
+ function func {
|
||
+ echo_devnull >&2
|
||
+ echo "FUNC"
|
||
+ }
|
||
+ OUT=$(func)
|
||
+ echo "OUT: ${OUT}"
|
||
+)
|
||
+exp='OUT: FUNC'
|
||
+[[ $got == "$exp" ]] || err_exit "bug 951 test 2a (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||
+
|
||
+got=$(
|
||
+ function echo_devnull {
|
||
+ ulimit -c 0
|
||
+ }
|
||
+ function func {
|
||
+ echo_devnull >&2
|
||
+ echo "FUNC"
|
||
+ }
|
||
+ OUT=${ func; }
|
||
+ echo "OUT: ${OUT}"
|
||
+)
|
||
+exp='OUT: FUNC'
|
||
+[[ $got == "$exp" ]] || err_exit "bug 951 test 2b (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||
+
|
||
+got=$(
|
||
+ OUT=$(ulimit -c 0 >&2; echo OK)
|
||
+ echo "OUT: ${OUT}"
|
||
+)
|
||
+exp='OUT: OK'
|
||
+[[ $got == "$exp" ]] || err_exit "bug 951 test 3 (expected $(printf %q "$exp"), got $(printf %q "$got"))"
|
||
+
|
||
# ======
|
||
exit $((Errors<125?Errors:125))
|
||
--
|
||
2.53.0
|
||
|