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.
0x0000000001007c32 in comsub (fn=14, cp=0x0, xp=<synthetic pointer>) at 
../../eval.c:1611
warning: Source file is more recent than executable.
1611                    lseek(shf_fileno(shf), (off_t)0, SEEK_SET);
(gdb) bt
#0  0x0000000001007c32 in comsub (fn=14, cp=0x0, xp=<synthetic pointer>) 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=0x3ffffffed48, f=f@entry=4128) at ../../eval.c:346
#2  0x000000000100a366 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  0x000000000100d082 in comexec (t=0x3fffdfe4888, tp=tp@entry=0x0, 
ap=0x3fffdfe45e8, flags=<optimized out>,
    xerrok=<optimized out>) at ../../exec.c:640
#4  0x000000000100bf0a in execute (t=<optimized out>, flags=<optimized out>, 
xerrok=xerrok@entry=0x0)
    at ../../exec.c:162
#5  0x000000000100c0a2 in execute (t=t@entry=0x3fffdfe4588, 
flags=flags@entry=0, xerrok=xerrok@entry=0x0)
    at ../../exec.c:204
#6  0x000000000101e048 in shell (s=s@entry=0x3fffdfe3b68, level=level@entry=0) 
at ../../main.c:954
#7  0x0000000001000e78 in main (argc=<optimized out>, argv=<optimized out>) 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, &tf);
   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 correctly on s390x; comparing with glibc shows
  that the wrong FPU registers seem to be saved but mksh does not use the
  FPU anyway

Setting breakpoints to lines 1608 (valsub call) and 1609:

Breakpoint 3, comsub (fn=14, cp=0x3fffdfe476d "\\echo a ; \\typeset e=2 ; 
\\return 3 ; \\echo x$e ",
    xp=<synthetic pointer>) at ../../eval.c:1608
1608                    valsub(t, NULL);
(gdb) print shf
$5 = (struct shf *) 0x3fffdfe5de8
(gdb) print &shf
Address requested for identifier "shf" which is in register $v10
(gdb) info r
pswm           0x705200180000000   505845723963588608
pswa           0x1007c06           16808966
r0             0x11f               287
r1             0xfffffffffffff000  18446744073709547520
r2             0x1                 1
r3             0x102dea0           16965280
r4             0x0                 0
r5             0x120               288
r6             0x3ff0000000e       4393751543822
r7             0x1020              4128
r8             0x0                 0
r9             0xe                 14
r10            0x0                 0
r11            0xc                 12
r12            0x3fffdfe5708       4398012847880
r13            0x102ee90           16969360
r14            0x1007c06           16808966
r15            0x3ffffffea90       4398046505616
acr0           0x3ff               1023
acr1           0xfdff7710          4261377808
acr2           0x0                 0
acr3           0x0                 0
acr4           0x0                 0
acr5           0x0                 0
acr6           0x0                 0
acr7           0x0                 0
acr8           0x0                 0
acr9           0x0                 0
acr10          0x0                 0
acr11          0x0                 0
acr12          0x0                 0
acr13          0x0                 0
acr14          0x0                 0
acr15          0x0                 0
fpc            0x0                 0
orig_r2        0xb                 11
last_break     0x101d89c           0x101d89c <ksh_dup2+68>
system_call    0x0                 0
tdb0           <unavailable>
tac            <unavailable>
tct            <unavailable>
atia           <unavailable>
tr0            <unavailable>
tr1            <unavailable>
tr2            <unavailable>
tr3            <unavailable>
tr4            <unavailable>
tr5            <unavailable>
tr6            <unavailable>
tr7            <unavailable>
tr8            <unavailable>
tr9            <unavailable>
tr10           <unavailable>
tr11           <unavailable>
tr12           <unavailable>
tr13           <unavailable>
tr14           <unavailable>
tr15           <unavailable>
gsd            <unavailable>
gssm           <unavailable>
gsepla         <unavailable>
bc_gsd         <unavailable>
bc_gssm        <unavailable>
bc_gsepla      <unavailable>
pc             0x1007c06           0x1007c06 <expand+2342>
cc             0x2                 2

... uhm, $v10 does not appear here?

(gdb) print $v10
$6 = {v4_float = {1.43352833e-42, -4.22639375e+37, 0, 0}, v2_double = 
{2.1729070589754877e-311, 0}, v16_int8 = {
    0, 0, 3, -1, -3, -2, 93, -24, 0, 0, 0, 0, 0, 0, 0, 0}, v8_int16 = {0, 1023, 
-514, 24040, 0, 0, 0, 0},
  v4_int32 = {1023, -33661464, 0, 0}, v2_int64 = {4398012849640, 0}, uint128 = 
81129017470195127308370827018240}

0x3FFFDFE5DE8 is 4398012849640 which is in v2_int64, found.

I have a feeling... anyway:

(gdb) c
Continuing.

Breakpoint 2, comsub (fn=14, cp=0x0, xp=<synthetic pointer>) at 
../../eval.c:1609
1609                    subst_exstat = exstat & 0xFF;
(gdb) info r
pswm           0x705100180000000   505828131777544192
pswa           0x1007c14           16808980
r0             0xffffffffffffffff  18446744073709551615
r1             0x10fc0             69568
r2             0x0                 0
r3             0x9fc0              40896
r4             0xfc0               4032
r5             0x0                 0
r6             0x3ff0000000e       4393751543822
r7             0x1020              4128
r8             0x0                 0
r9             0xe                 14
r10            0x0                 0
r11            0xc                 12
r12            0x3fffdfe5708       4398012847880
r13            0x102ee90           16969360
r14            0x1007c14           16808980
r15            0x3ffffffea90       4398046505616
acr0           0x3ff               1023
acr1           0xfdff7710          4261377808
acr2           0x0                 0
acr3           0x0                 0
acr4           0x0                 0
acr5           0x0                 0
acr6           0x0                 0
acr7           0x0                 0
acr8           0x0                 0
acr9           0x0                 0
acr10          0x0                 0
acr11          0x0                 0
acr12          0x0                 0
acr13          0x0                 0
acr14          0x0                 0
acr15          0x0                 0
fpc            0x0                 0
orig_r2        0x2                 2
last_break     0x10066d0           0x10066d0 <valsub+160>
system_call    0x0                 0
tdb0           <unavailable>
tac            <unavailable>
tct            <unavailable>
atia           <unavailable>
tr0            <unavailable>
tr1            <unavailable>
tr2            <unavailable>
tr3            <unavailable>
tr4            <unavailable>
tr5            <unavailable>
tr6            <unavailable>
tr7            <unavailable>
tr8            <unavailable>
tr9            <unavailable>
tr10           <unavailable>
tr11           <unavailable>
tr12           <unavailable>
tr13           <unavailable>
tr14           <unavailable>
tr15           <unavailable>
gsd            <unavailable>
gssm           <unavailable>
gsepla         <unavailable>
bc_gsd         <unavailable>
bc_gssm        <unavailable>
bc_gsepla      <unavailable>
pc             0x1007c14           0x1007c14 <expand+2356>
cc             0x1                 1
(gdb) print $v10
$7 = {v4_float = {0, 0, 0, 0}, v2_double = {0, 0}, v16_int8 = {0 <repeats 16 
times>}, v8_int16 = {0, 0, 0, 0, 
    0, 0, 0, 0}, v4_int32 = {0, 0, 0, 0}, v2_int64 = {0, 0}, uint128 = 0}



-- System Information:
Debian Release: 11.0
  APT prefers unstable-debug
  APT policy: (500, 'unstable-debug'), (500, 'unstable')
Architecture: s390x

Kernel: Linux 4.19.0-16-s390x (SMP w/2 CPU threads)
Locale: LANG=C, LC_CTYPE=C.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /bin/dash
Init: unable to detect

Versions of packages libklibc-dev depends on:
ii  libklibc        2.0.8-6
ii  linux-libc-dev  5.10.28-1

libklibc-dev recommends no packages.

libklibc-dev suggests no packages.

-- no debconf information

Reply via email to