ksh: Some bugs and crashes when using the DEBUG trap

Tested with ksh Version AJMv 93u+m/1.0.0-alpha+ccd98fe7 2021-01-08 on Ubuntu 20.04

1. Redirecting disables the DEBUG trap

edit: fixed in 2a835a2d8a7979d37820705087ad43bcdadc5201, e664b78f980fa31bd5e4c5755ce025dd5dae5618

trap 'echo LINENO: $LINENO >&1' DEBUG # 1 
echo foo                              # 2
var=$(echo)                           # 3
echo bar                              # 4
echo baz                              # 5

Expected (This is what happens when you remove >&1).

LINENO: 2
foo
LINENO: 3
LINENO: 4
bar
LINENO: 5
baz

Actual

LINENO: 2
foo
bar
baz

2. Crashes when re-trapping inside a subshell

edit: fixed in 2a835a2d8a7979d37820705087ad43bcdadc5201

set -e
trap ':' DEBUG
( trap ':' DEBUG )
echo ok
ksh.sh[22]: ^P?`?^X?%?d: not found [No such file or directory]

3. The split word will not work

edit: fixed in 70368c57d67c6d3f327a52de39697e8c6647ee7a

v=''
trap ': $v' DEBUG
A="a b c"
set -- $A
printf '%s\n' "$@"

Expected

a
b
c

Actual

a b c

4. Side effects to exit status

edit: fixed in d00b4b39f66e2912797195f2aac9913218826350

trap ':' DEBUG

(exit 123)
echo $? # => 123 (Correct)

r=$(exit 123)
echo $? # => Same as above, expected 123, but actually 0.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 19

Commits related to this issue

Most upvoted comments

Thank you. However, unfortunately it did not work in my actual use case. It might be because I use a lot of subshells or enable/disable the DEBUG trap many times, but I’m not sure why.

I am using the following workaround. This avoids bug 1 and bug 3. I am guessing that the internal state was reset by reassigning the IFS.

trap '(echo "kcov@${.sh.file}@${LINENO}@" >/dev/fd/$KCOV_BASH_XTRACEFD); IFS=$IFS' DEBUG

The very handy multishell repo allows us to use ‘git blame’ to trace the origin of these bugs.

The off-by-one error causing various bugs was introduced in ksh 93t 2008-07-25:
https://github.com/multishell/ksh93/commit/8e947ccf8ce2bf1fda57f04e59f4a9a4782cab99 (fault.c, line 321)

The incorrect check causing the exit status bug was introduced in ksh 93t 2008-11-04:
https://github.com/multishell/ksh93/commit/b1ade26843968b5606753eba1e0afa099daab528 (fault.c, line 459)

The ifstable save/restore causing the field splitting bug was introduced in ksh 93t+ 2009-11-30:
https://github.com/multishell/ksh93/commit/53d9f0095adece1dfe82a9f2b878fc6fc811a8bc (fault.c, lines 440, 444, 482)

So all the bugs reported in this issue were fixed by simply reverting these specific changes. I think that they are some experiments that the developers simply forgot to remove. I’ve suspected such a thing multiple times before. ksh93 was developed by researchers who were genius innovators, but incredibly sloppy maintainers.

The crash caused by bug 2 is intermittent for me, but when it doesn’t crash, it leaks:

for sig in ERR KEYBD DEBUG
do	trap ': main' $sig
	( trap ": LEAK : $sig" $sig )
	trap
	trap - "$sig"
done

Output (occasionally):

trap -- ': main' ERR
trap -- ': main' KEYBD
trap -- ': LEAK' DEBUG

The problem seems to be an off-by-one error when resetting/restoring the traps. This fix works for me:

diff --git a/src/cmd/ksh93/sh/fault.c b/src/cmd/ksh93/sh/fault.c
index 72b3eea..b518d97 100644
--- a/src/cmd/ksh93/sh/fault.c
+++ b/src/cmd/ksh93/sh/fault.c
@@ -344,7 +344,7 @@ void        sh_sigreset(register int mode)
                        sh.sigflag[sig] = flag;
                }
        }
-       for(sig=SH_DEBUGTRAP-1;sig>=0;sig--)
+       for(sig=SH_DEBUGTRAP;sig>=0;sig--)
        {
                if(trap=sh.st.trap[sig])
                {

Output after fix:

trap -- ': main' ERR
trap -- ': main' KEYBD
trap -- ': main' DEBUG

…and your own test case passes as well.

Cool, glad to see that. 😃 The fix for redefining a running function was part of 047cb3303c7ea80a9cfde74c517b0496093abb65.

It’s still experimental, but I’ve started testing 93u+m (https://github.com/shellspec/shellspec/commit/aed1c899c23e4a9f89115b85e7a3bb15b5a46662). It seems to be working fine.

I’m glad to see that the following code now works correctly with 93u+m. It was not working with ksh2020 on Ubuntu 20.04 either.

# I'm working around this issue with using `alias`.
# shellspec_redefinable() { eval "alias $1='shellspec_redefinable_ $1'"; }
# shellspec_redefinable_() { "$@"; }
#
# shellspec_redefinable f

f() { echo foo; }
(
  f() { echo bar; }
  f # The output is "foo" for ksh93u+ and ksh2020.
)

Bug 4 (side effects to exit status) is an easy fix:

That’s impressive! I could not find a workaround on the shell script side of it.

Thank you for rebooting the development of ksh!

Bug 4 (side effects to exit status) is an easy fix:

diff --git a/src/cmd/ksh93/sh/fault.c b/src/cmd/ksh93/sh/fault.c
index 440e761..72b3eea 100644
--- a/src/cmd/ksh93/sh/fault.c
+++ b/src/cmd/ksh93/sh/fault.c
@@ -489,7 +489,7 @@ int sh_trap(const char *trap, int mode)
 	sh_popcontext(shp,&buff);
 	shp->intrap--;
 	sfsync(shp->outpool);
-	if(!shp->indebug && jmpval!=SH_JMPEXIT && jmpval!=SH_JMPFUN)
+	if(jmpval!=SH_JMPEXIT && jmpval!=SH_JMPFUN)
 		shp->exitval=savxit;
 	stakset(savptr,staktop);
 	fcrestore(&savefc);
  1. Confirmed also on ksh 93u+ (2012-08-01), and ksh2020 (which was based on 93v- beta; both are abandoned).
  2. Confirmed also on ksh 93u+, but not ksh2020. There may be a fix to backport. edit: in fact, ksh2020 has the bug, the crash is just intermittent. If it doesn’t crash, it leaks. See: https://github.com/ksh93/ksh/issues/155#issuecomment-766216838
  3. Confirmed also on ksh 93u+ and ksh2020.
  4. Confirmed also on ksh 93u+ and ksh2020.

So, at least this 93u+m fork didn’t introduce any of these bugs 😉. Downside of that is that these longstanding bugs will probably be difficult to track down.