[RFC][PATCH] NT_FILE/NT_SIGINFO breakage on mips compat coredumps

Al Viro
2020-12-24T19:46:02Z

On Wed, Dec 23, 2020 at 07:12:13AM +0000, Al Viro wrote:
> On Wed, Dec 23, 2020 at 07:03:20AM +0000, Al Viro wrote:
>
> Argh.... Wrong commit blamed - the parent of the correct one.
> It's actually 2aa362c49c31 ("coredump: extend core dump note section to
> contain file names of mapped files"). My apologies - fat-fingered
> cut'n'paste...
>
> siginfo commit does suffer the same problem, but it becomes an issue
> only for 32bit processes under mips64 big-endian kernel (there it yields
> e.g. zero .__sigfault.si_addr in $_siginfo when using gdb with a coredump
> of 32bit process, whatever the actual faulting address had been). And
> b-e mips64 is rather uncommon, so that's less of an issue.

FWIW, here's debian/mips image (stretch) booted with
qemu-system-mips64 -M malta -cpu 5KEc:
root@mips:~# uname -a
Linux mips 4.9.0-13-5kc-malta #1 Debian 4.9.228-1 (2020-07-05) mips64 GNU/Linux
root@mips:~# cat a.c
main()
{
*(char *)0x0123 = 0;
}
root@mips:~# gcc a.c
a.c:1:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
main()
^~~~
root@mips:~# ulimit -c unlimited
root@mips:~# ./a.out
[ 519.744983] do_page_fault(): sending SIGSEGV to a.out for invalid write access to 0000000000000123
[ 519.746735] epc = 00000000558477c0 in a.out[55847000+1000]
[ 519.747758] ra = 000000007792f4a8 in libc-2.24.so[77916000+16a000]
Segmentation fault (core dumped)
root@mips:~# gdb a.out core
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "mips-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...(no debugging symbols found)...done.
[New LWP 1202]
Core was generated by `./a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x55dde7c0 in main ()
(gdb) print $_siginfo
$1 = {si_signo = 11, si_errno = 1, si_code = 0, _sifields = {_pad = {0, 0,
291, 0 <repeats 26 times>}, _kill = {si_pid = 0, si_uid = 0}, _timer = {
si_tid = 0, si_overrun = 0, si_sigval = {sival_int = 291,
sival_ptr = 0x123}}, _rt = {si_pid = 0, si_uid = 0, si_sigval = {
sival_int = 291, sival_ptr = 0x123}}, _sigchld = {si_pid = 0,
si_uid = 0, si_status = 291, si_utime = 0, si_stime = 0}, _sigfault = {
si_addr = 0x0}, _sigpoll = {si_band = 0, si_fd = 0}}}
(gdb) quit

Note the wrong value in _sigfault.si_addr - it should've been 0x123, not 0.

root@mips:~# readelf -n core

Displaying notes found at file offset 0x00000234 with length 0x000005f4:
Owner Data size Description
CORE 0x00000100 NT_PRSTATUS (prstatus structure)
CORE 0x00000080 NT_PRPSINFO (prpsinfo structure)
CORE 0x00000080 NT_SIGINFO (siginfo_t data)
CORE 0x00000090 NT_AUXV (auxiliary vector)
CORE 0x000001e1 NT_FILE (mapped files)
Page size: 9
Start End Page Offset
CORE 0x00000108 NT_FPREGSET (floating point registers)

For comparison, exact same image booted with qemu-system-mips -M malta:

root@mips:~# uname -a
Linux mips 4.9.0-13-4kc-malta #1 Debian 4.9.228-1 (2020-07-05) mips GNU/Linux
root@mips:~# ulimit -c unlimited
root@mips:~# ./a.out
[ 83.380870] do_page_fault(): sending SIGSEGV to a.out for invalid write access to 00000123
[ 83.390678] epc = 55e0e7c0 in a.out[55e0e000+1000]
[ 83.391525] ra = 76f644a8 in libc-2.24.so[76f4b000+16a000]
Segmentation fault (core dumped)
root@mips:~# gdb a.out core
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "mips-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...(no debugging symbols found)...done.
[New LWP 1184]
Core was generated by `./a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x55e0e7c0 in main ()
(gdb) print $_siginfo
$1 = {si_signo = 11, si_errno = 1, si_code = 0, _sifields = {_pad = {291,
0 <repeats 28 times>}, _kill = {si_pid = 291, si_uid = 0}, _timer = {
si_tid = 291, si_overrun = 0, si_sigval = {sival_int = 0,
sival_ptr = 0x0}}, _rt = {si_pid = 291, si_uid = 0, si_sigval = {
sival_int = 0, sival_ptr = 0x0}}, _sigchld = {si_pid = 291,
si_uid = 0, si_status = 0, si_utime = 0, si_stime = 0}, _sigfault = {
si_addr = 0x123}, _sigpoll = {si_band = 291, si_fd = 0}}}
(gdb) quit
root@mips:~# readelf -n core

Displaying notes found at file offset 0x00000234 with length 0x00000580:
Owner Data size Description
CORE 0x00000100 NT_PRSTATUS (prstatus structure)
CORE 0x00000080 NT_PRPSINFO (prpsinfo structure)
CORE 0x00000080 NT_SIGINFO (siginfo_t data)
CORE 0x00000090 NT_AUXV (auxiliary vector)
CORE 0x0000016d NT_FILE (mapped files)
Page size: 4096
Start End Page Offset
0x55e0e000 0x55e0f000 0x00000000
/root/a.out
0x55e1e000 0x55e1f000 0x00000000
/root/a.out
0x76f4b000 0x770b5000 0x00000000
/lib/mips-linux-gnu/libc-2.24.so
0x770b5000 0x770c5000 0x0000016a
/lib/mips-linux-gnu/libc-2.24.so
0x770c5000 0x770c8000 0x0000016a
/lib/mips-linux-gnu/libc-2.24.so
0x770c8000 0x770cb000 0x0000016d
/lib/mips-linux-gnu/libc-2.24.so
0x770cd000 0x770f0000 0x00000000
/lib/mips-linux-gnu/ld-2.24.so
0x770ff000 0x77100000 0x00000022
/lib/mips-linux-gnu/ld-2.24.so
0x77100000 0x77101000 0x00000023
/lib/mips-linux-gnu/ld-2.24.so
CORE 0x00000108 NT_FPREGSET (floating point registers)

So that's not so theoretical - big-endian mips64 userland is unsupported,
but booting the big-endian mips32 userland on mips64 hardware is clearly
meant to work - they even ship a 64bit kernel built for that.

IOW, both O32 and N32 coredumps in 64bit mips kernels have broken
NT_FILE and NT_SIGINFO. And while NT_SIGINFO breakage is really
visible only on b-e, NT_FILE one is common to b-e and l-e. One of
the effects of the latter is that current gdb fails to work with
threaded coredumps of 32bit processes produced on boxen with 64bit
kernels. Coredumps generated by gcore(1) are fine...

I think the following ought to be applied. Comments?

[mips] fix malformed NT_FILE and NT_SIGINFO in 32bit coredumps

Patches that introduced NT_FILE and NT_SIGINFO notes back in 2012
had taken care of native (fs/binfmt_elf.c) and compat (fs/compat_binfmt_elf.c)
coredumps; unfortunately, compat on mips (which does not go through the
usual compat_binfmt_elf.c) had not been noticed.

As the result, both N32 and O32 coredumps on 64bit mips kernels
have those sections malformed enough to confuse the living hell out of
all gdb and readelf versions (up to and including the tip of binutils-gdb.git).

Longer term solution is to make both O32 and N32 compat use the
regular compat_binfmt_elf.c, but that's too much for backports. The minimal
solution is to do in arch/mips/kernel/binfmt_elf[on]32.c the same thing
those patches have done in fs/compat_binfmt_elf.c

Cc: stable@kernel.org # v3.7+
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
---
diff --git a/arch/mips/kernel/binfmt_elfn32.c b/arch/mips/kernel/binfmt_elfn32.c
index 6ee3f7218c67..c4441416e96b 100644
--- a/arch/mips/kernel/binfmt_elfn32.c
+++ b/arch/mips/kernel/binfmt_elfn32.c
@@ -103,4 +103,11 @@ jiffies_to_old_timeval32(unsigned long jiffies, struct old_timeval32 *value)
#undef ns_to_kernel_old_timeval
#define ns_to_kernel_old_timeval ns_to_old_timeval32

+/*
+ * Some data types as stored in coredump.
+ */
+#define user_long_t compat_long_t
+#define user_siginfo_t compat_siginfo_t
+#define copy_siginfo_to_external copy_siginfo_to_external32
+
#include "../../../fs/binfmt_elf.c"
diff --git a/arch/mips/kernel/binfmt_elfo32.c b/arch/mips/kernel/binfmt_elfo32.c
index 6dd103d3cebb..7b2a23f48c1a 100644
--- a/arch/mips/kernel/binfmt_elfo32.c
+++ b/arch/mips/kernel/binfmt_elfo32.c
@@ -106,4 +106,11 @@ jiffies_to_old_timeval32(unsigned long jiffies, struct old_timeval32 *value)
#undef ns_to_kernel_old_timeval
#define ns_to_kernel_old_timeval ns_to_old_timeval32

+/*
+ * Some data types as stored in coredump.
+ */
+#define user_long_t compat_long_t
+#define user_siginfo_t compat_siginfo_t
+#define copy_siginfo_to_external copy_siginfo_to_external32
+
#include "../../../fs/binfmt_elf.c"