Package: libklibc-dev
Version: 2.0.8-6
Followup-For: Bug #943425
X-Debbugs-Cc: t...@debian.org
I am able to track this down on the porterbox zelenka.
$ apt-get source mksh
$ cd mksh-59c
$ mkdir -p build/klibc
$ cd build/klibc
$ cp /usr/lib/klibc/bin/mksh .
$ chmod +x mksh # because the x attribute is removed if testsfail
$ gdb --args ./mksh -c 'x=q; e=1; x=${ echo a; typeset e=2; return 3; echo
x$e;}; echo 3:y$x,$e,$?.'
(gdb) r
[...]
Program received signal SIGSEGV, Segmentation fault.
0x01007c32 in comsub (fn=14, cp=0x0, xp=) at
../../eval.c:1611
warning: Source file is more recent than executable.
1611lseek(shf_fileno(shf), (off_t)0, SEEK_SET);
(gdb) bt
#0 0x01007c32 in comsub (fn=14, cp=0x0, xp=) at
../../eval.c:1611
#1 expand (ccp=ccp@entry=0x3fffdfe4768 "\001x\001=\016\\echo a ; \\typeset e=2
; \\return 3 ; \\echo x$e ",
wp=wp@entry=0x3ffed48, f=f@entry=4128) at ../../eval.c:346
#2 0x0100a366 in evalstr (
cp=0x3fffdfe4768 "\001x\001=\016\\echo a ; \\typeset e=2 ; \\return 3 ;
\\echo x$e ", f=f@entry=4128)
at ../../eval.c:173
#3 0x0100d082 in comexec (t=0x3fffdfe4888, tp=tp@entry=0x0,
ap=0x3fffdfe45e8, flags=,
xerrok=) at ../../exec.c:640
#4 0x0100bf0a in execute (t=, flags=,
xerrok=xerrok@entry=0x0)
at ../../exec.c:162
#5 0x0100c0a2 in execute (t=t@entry=0x3fffdfe4588,
flags=flags@entry=0, xerrok=xerrok@entry=0x0)
at ../../exec.c:204
#6 0x0101e048 in shell (s=s@entry=0x3fffdfe3b68, level=level@entry=0)
at ../../main.c:954
#7 0x01000e78 in main (argc=, argv=) at
../../main.c:742
(gdb) print shf
$1 = (struct shf *) 0x0
The code in question (where it crashes) is thus:
1584 } else if (fn == FUNSUB) {
1585 int ofd1;
1586 struct temp *tf = NULL;
1587
1588 /*
1589 * create a temporary file, open for reading and
writing,
1590 * with an shf open for reading (buffered) but yet
unused
1591 */
1592 maketemp(ATEMP, TT_FUNSUB, );
1593 if (!tf->shf) {
1594 errorf(Tf_temp,
1595 Tcreate, tf->tffn, cstrerror(errno));
1596 }
1597 /* extract shf from temporary file, unlink and free it
*/
1598 shf = tf->shf;
1599 unlink(tf->tffn);
1600 afree(tf, ATEMP);
1601 /* save stdout and let it point to the tempfile */
1602 ofd1 = savefd(1);
1603 ksh_dup2(shf_fileno(shf), 1, false);
1604 /*
1605 * run tree, with output thrown into the tempfile,
1606 * in a new function block
1607 */
1608 valsub(t, NULL);
1609 subst_exstat = exstat & 0xFF;
1610 /* rewind the tempfile and restore regular stdout */
1611 lseek(shf_fileno(shf), (off_t)0, SEEK_SET);
1612 restfd(1, ofd1);
The crash occurs in line 1611 because shf (a local variable) is nil.
The really interesting part, though, is in line 1608, a call to valsub():
2093 /* helper function due to setjmp/longjmp woes */
2094 static char *
2095 valsub(struct op *t, Area *ap)
2096 {
2097 char * volatile cp = NULL;
2098 struct tbl * volatile vp = NULL;
2099
2100 newenv(E_FUNC);
2101 newblock();
2102 if (ap)
2103 vp = local(TREPLY, false);
2104 if (!kshsetjmp(e->jbuf))
2105 execute(t, XXCOM | XERROK, NULL);
2106 if (vp)
2107 strdupx(cp, str_val(vp), ap);
2108 quitenv(NULL);
2109
2110 return (cp);
2111 }
Let's look again at the invocation that caused the crash:
x=q; e=1; x=${ echo a; typeset e=2; return 3; echo x$e;}; echo
3:y$x,$e,$?.
This one does not crash:
x=q; e=1; x=${ echo a; typeset e=2; echo x$e;}; echo 2:y$x,$e,$?.
The difference here is that 'return' is used in the crash case,
which executes a kshlongjmp(), that is siglongjmp(); kshsetjmp(x)
is sigsetjmp(x,0), which klibc defines as:
34 #define sigsetjmp(__env, __save) \
35 ({ \
36 struct __sigjmp_buf *__e = (__env); \
37 sigprocmask(0, NULL, &__e->__sigs); \
38 setjmp(__e->__jmpbuf); \
39 })
This apparently has two problems:
- the __save argument is ignored, contrary to sigsetjmp docs:
If, and only if, the savesigs argument provided to sigsetjmp() is non-
zero, the process's current signal mask is saved in env and will be re-
stored if a siglongjmp() is later performed with this env.
- it appears as if the combination of sigsetjmp/siglongjmp does not restore
all callee-saved variables