Bug#943425: klibc: [s390x] SIGSEGV in mksh testcase funsub-2
Package: libklibc-dev
Version: 2.0.8-6
Followup-For: Bug #943425
X-Debbugs-Cc: tg@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 to: