reassign 961097 libncurses6 6.2-1 retitle 961097 libncurses6: unloads libgpm2 in SIGTSTP handler, process returns to unmapped address on resume severity 961097 normal thanks
I tested this with the following package versions: -- >8 -- nabijaczleweli@szarotka:~$ dpkg -l htop* *gpm* libncurses6* Desired=Unknown/Install/Remove/Purge/Hold | Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend |/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad) ||/ Name Version Architecture Description +++-======================-============-============-====================================== ii gpm 1.20.7-6.8 x32 General Purpose Mouse interface ii gpm-dbgsym 1.20.7-6.8 x32 debug symbols for gpm ii htop 2.2.0-2 x32 interactive processes viewer ii htop-dbgsym 2.2.0-2 x32 debug symbols for htop ii libgpm2:x32 1.20.7-6.8 x32 General Purpose Mouse - shared library ii libgpm2-dbgsym:x32 1.20.7-6.8 x32 debug symbols for libgpm2 ii libncurses6:x32 6.2-1 x32 shared libraries for terminal handling ii libncurses6-dbgsym:x32 6.2-1 x32 debug symbols for libncurses6 misio@aqq:~$ dpkg -l htop* *gpm* libncurses6* Desired=Unknown/Install/Remove/Purge/Hold | Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend |/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad) ||/ Name Version Architecture Description +++-=================-============-============-====================================== ii gpm 1.20.7-6 amd64 General Purpose Mouse interface ii htop 2.2.0-2 amd64 interactive processes viewer ii libgpm2:amd64 1.20.7-6 amd64 General Purpose Mouse - shared library ii libncurses6:amd64 6.2-1 amd64 shared libraries for terminal handling -- >8 -- The setup required: 1. One (preferably two) virtual terminals you can write to, likely by logging in (here /dev/tty1 will be the main one, and /dev/tty2 will be a dump terminal); this is of critical importance ‒ you *need* to be on a teletype to reproduce this (presumably an X session that uses gpm for the mouse would also work? I haven't tried that, I'm not sure how I'd even set that up, especially in a.d. 2020) 2. gpm running in the main terminal, verify that it works by copy-pasting something; (this will *not* happen if the controlling tty is not a tty e.g. redirected via script(1)); 3. htop (presumably any ncurses program that uses the mouse, none came to mind; would love to try some ‒ suggestions?) The base repro: The optional redirection does not affect any part of this, but makes the output palatable. However, if you click about on the *main* terminal, you should see the selection update on the *output* one; this is obvious if they're one and the same (no redirection), less obvious if they are not. -- >8 -- $ htop [> /dev/tty2] ^Z fg htop 2.2.0 aborting. Please report bug at http://hisham.hm/htop Segmentation fault -- >8 -- That is, of course, if you're lucky ‒ this only happens on my x32 system with no additional output on amd64, press Enter to get a shell prompt. Verifying that this is what happens: Build override.c: -- >8 -- // cc -ooverride.so override.c -fPIC -shared #include <stdio.h> int dlclose(void * handle) { fprintf(stderr, "dlclose(%p)\n", handle); return 0; } -- >8 -- Invoke (no address sanitiser): -- >8 -- $ LD_PRELOAD=./noclose.so htop nabijaczleweli@szarotka:/mnt/zest/htop$ LD_PRELOAD=./override.so htop > /dev/tty2 ^Zdlclose(0x57da5520) [1]+ Stopped LD_PRELOAD=./override.so htop > /dev/tty2 nabijaczleweli@szarotka:/mnt/zest/htop$ fg LD_PRELOAD=./override.so htop > /dev/tty2 ^Zdlclose(0x57da5520) [1]+ Stopped LD_PRELOAD=./override.so htop > /dev/tty2 nabijaczleweli@szarotka:/mnt/zest/htop$ fg LD_PRELOAD=./override.so htop > /dev/tty2 ^Zdlclose(0x57da5520) [1]+ Stopped LD_PRELOAD=./override.so htop > /dev/tty2 nabijaczleweli@szarotka:/mnt/zest/htop$ fg LD_PRELOAD=./override.so htop > /dev/tty2 q dlclose(0x57da5520) nabijaczleweli@szarotka:/mnt/zest/htop$ -- >8 -- Invoke (ASAN htop build): -- >8 -- nabijaczleweli@szarotka:/mnt/zest/htop$ LD_PRELOAD=/lib/x86_64-linux-gnux32/libasan.so.5:./override.so ./htop/htop > /dev/tty2 ^Zdlclose(0xf4802e00) [1]+ Stopped LD_PRELOAD=/lib/x86_64-linux-gnux32/libasan.so.5:./override.so ./htop/htop > /dev/tty2 nabijaczleweli@szarotka:/mnt/zest/htop$ fg LD_PRELOAD=/lib/x86_64-linux-gnux32/libasan.so.5:./override.so ./htop/htop > /dev/tty2 ^Zdlclose(0xf4802e00) [1]+ Stopped LD_PRELOAD=/lib/x86_64-linux-gnux32/libasan.so.5:./override.so ./htop/htop > /dev/tty2 nabijaczleweli@szarotka:/mnt/zest/htop$ fg LD_PRELOAD=/lib/x86_64-linux-gnux32/libasan.so.5:./override.so ./htop/htop > /dev/tty2 q dlclose(0xf4802e00) nabijaczleweli@szarotka:/mnt/zest/htop$ -- >8 -- If you want to follow along at home but don't wish to use a debugger, try the cunningly named smart override: -- >8 -- // cc -osmoverride.so smoverride.c -fPIC -shared -ldl #define _GNU_SOURCE #include <dlfcn.h> #include <stdio.h> typedef void *(* dlopen_t)(const char * filename, int flags); void * dlopen(const char * filename, int flags) { void * handle = ((dlopen_t)dlsym(RTLD_NEXT, "dlopen"))(filename, flags); fprintf(stderr, "dlopen(%s, %d): %p\n", filename, flags, handle); return handle; } int dlclose(void * handle) { fprintf(stderr, "dlclose(%p)\n", handle); return 0; } -- >8 -- Invoke (with ASAN): -- >8 -- nabijaczleweli@szarotka:/mnt/zest/htop$ LD_PRELOAD=/lib/x86_64-linux-gnux32/libasan.so.5:./smoverride.so ./htop/htop > /dev/tty2 dlopen(libgpm.so.2, 2): 0xf4802e00 ^Zdlclose(0xf4802e00) [3]+ Stopped LD_PRELOAD=/lib/x86_64-linux-gnux32/libasan.so.5:./smoverride.so ./htop/htop > /dev/tty2 nabijaczleweli@szarotka:/mnt/zest/htop$ fg LD_PRELOAD=/lib/x86_64-linux-gnux32/libasan.so.5:./smoverride.so ./htop/htop > /dev/tty2 dlopen(libgpm.so.2, 2): 0xf4802e00 ^Zdlclose(0xf4802e00) [3]+ Stopped LD_PRELOAD=/lib/x86_64-linux-gnux32/libasan.so.5:./smoverride.so ./htop/htop > /dev/tty2 nabijaczleweli@szarotka:/mnt/zest/htop$ fg LD_PRELOAD=/lib/x86_64-linux-gnux32/libasan.so.5:./smoverride.so ./htop/htop > /dev/tty2 dlopen(libgpm.so.2, 2): 0xf4802e00 q dlclose(0xf4802e00) nabijaczleweli@szarotka:/mnt/zest/htop$ -- >8 -- A quick trip to /usr/include/dlfcn.h and /usr/include/x86_64-linux-gnux32/bits/dlfcn.h reveals that the 2 flag is RTLD_NOW: -- >8 -- /* The MODE argument to `dlopen' contains one of the following: */ #define RTLD_LAZY 0x00001 /* Lazy function call binding. */ #define RTLD_NOW 0x00002 /* Immediate function call binding. */ #define RTLD_BINDING_MASK 0x3 /* Mask of binding time value. */ #define RTLD_NOLOAD 0x00004 /* Do not load the object. */ #define RTLD_DEEPBIND 0x00008 /* Use deep binding. */ -- >8 -- As we can see, libgpm is being dlopen(3)ed on start(/resume), and dlclose(3)d on SIGTSTP. Every time. The Weltmer method: GDB does a surprisingly okay job of this, provided you start it in src/ in the patched gpm source, and install libgpm2-dbgsym. The preferred layout is split (though src will suffice here); this looks lovely on a nice crisp colourful terminal, but like an identifier soup in a white-on-grey debbugs mail; I did the best I could with what screendump(1) gave me, but still; the most important screenshots are attached, but consider grabbing them with more detail form https://lfs.nabijaczleweli.xyz/0001-b.d.o-961097/ -- >8 -- nabijaczleweli@szarotka:/mnt/zest/htop/gpm/src$ gdb ../../htop/htop GNU gdb (Debian 9.1-3) 9.1 Copyright (C) 2020 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 "x86_64-linux-gnux32". 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 ../../htop/htop... (gdb) break gpm_suspend_hook Function "gpm_suspend_hook" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (gpm_suspend_hook) pending. (gdb) set disassembly-flavor intel (gdb) layout split -- >8 -- Note, how gpm_suspend_hook() does not yet exist. By breaking on that function, we can, after ^Zing continue through libc into it, and carefully step through it until just before the lines invoking the saved signal handler https://sources.debian.org/src/gpm/1.20.7-6/src/lib/liblow.c/#L172-L175 and jump directly into bardo, yielding: -- >8 -- ┌──lib/liblow.c─────────────────────────────────────────────────────────────┐ │ 157 │ │ 158 /* Open a completely transparent gpm connection */ │ │ 159 gpm_connect.eventMask = 0; │ │ 160 gpm_connect.defaultMask = ~0; │ │ 161 gpm_connect.minMod = ~0; │ │ 162 gpm_connect.maxMod = 0; │ │ 163 /* cannot do this under xterm, tough */ │ │B+ 164 success = (Gpm_Open (&gpm_connect, 0) >= 0); │ │ 165 │ │ 166 /* take the default action, whatever it is (probably a stop :) */ │ │ >167 sigprocmask (SIG_SETMASK, &old_sigset, 0); │ │ 168 sigaction (SIGTSTP, &gpm_saved_suspend_hook, 0); │ │ 169 kill (getpid (), SIGTSTP); │ │ 170 │ │ 171 /* in bardo here */ │ │ 172 │ │ 173 /* Reincarnation. Prepare for another death early. */ │ │ 174 sigemptyset(&sa.sa_mask); │ │ 175 sa.sa_handler = gpm_suspend_hook; │ └───────────────────────────────────────────────────────────────────────────┘ multi-thre Thread 0xf6ff3740 ( In: gpm_suspend_hook L167 PC: 0xf5e09f2b Starting program: /mnt/zest/htop/htop/htop > /dev/tty2 [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnux32/libthread_db.so.1". ^Z Program received signal SIGTSTP, Stopped (user). 0xf72c5383 in __GI___poll (fds=0xffffd1c0, nfds=2, timeout=timeout@entry=1500) at ../sysdeps/unix/sysv/linux/poll.c:29 (gdb) cont Continuing. Breakpoint 1, gpm_suspend_hook (signum=20) at lib/liblow.c:147 warning: Source file is more recent than executable. (gdb) break 164 Breakpoint 2 at 0xf5e09f1a: file lib/liblow.c, line 164. (gdb) cont Continuing. Breakpoint 2, gpm_suspend_hook (signum=<optimized out>) at lib/liblow.c:164 (gdb) n (gdb) jump 171 Continuing at 0xf5e09f5b. -- >8 -- htop then continues (with a functional mouse!), until it's Qd or ^Zed again. Plumbing the depths: You'll also need the patched ncurses source unpacked such that ../../ncurses/tty/lib_tstp.c resolves and to install libncurses6-dbgsym. First, note the backtrace up to the entry into gpm_suspend_hook(), and the hook gpm saved during the first Gpm_Open() call: -- >8 -- ┌──lib/liblow.c──────────────────────────────────────────────────────────────────────────────────────────────────┐ │ 163 /* cannot do this under xterm, tough */ │ │ 164 success = (Gpm_Open (&gpm_connect, 0) >= 0); │ │ 165 │ │ 166 /* take the default action, whatever it is (probably a stop :) */ │ │ 167 sigprocmask (SIG_SETMASK, &old_sigset, 0); │ │ 168 sigaction (SIGTSTP, &gpm_saved_suspend_hook, 0); │ │ 169 kill (getpid (), SIGTSTP); │ │ 170 │ │ 171 /* in bardo here */ │ │ 172 │ │ 173 /* Reincarnation. Prepare for another death early. */ │ │b+ 174 sigemptyset(&sa.sa_mask); │ │ 175 sa.sa_handler = gpm_suspend_hook; │ │ 176 sa.sa_flags = SA_NOMASK; │ │ 177 sigaction (SIGTSTP, &sa, 0); │ │ 178 │ │ 179 /* Pop the gpm stack by closing the useless connection */ │ │ 180 /* but do it only when we know we opened one.. */ │ │ 181 if (success) { │ ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ 0xf5e09f1a <gpm_suspend_hook+90> call 0xf5e09030 <Gpm_Open@plt> │ │ 0xf5e09f1f <gpm_suspend_hook+95> xor edx,edx │ │ 0xf5e09f21 <gpm_suspend_hook+97> mov rsi,r12 │ │ 0xf5e09f24 <gpm_suspend_hook+100> mov edi,0x2 │ │ 0xf5e09f29 <gpm_suspend_hook+105> mov ebp,eax │ │ 0xf5e09f2b <gpm_suspend_hook+107> lea r12d,[rsp+0x110] │ │ 0xf5e09f33 <gpm_suspend_hook+115> call 0xf5e091f0 <sigprocmask@plt> │ │ 0xf5e09f38 <gpm_suspend_hook+120> xor edx,edx │ │ 0xf5e09f3a <gpm_suspend_hook+122> lea esi,[rip+0x4280] # 0xf5e0e1c0 <gpm_saved_suspend_hook> │ │ 0xf5e09f40 <gpm_suspend_hook+128> mov edi,0x14 │ │ 0xf5e09f45 <gpm_suspend_hook+133> call 0xf5e09360 <sigaction@plt> │ │ 0xf5e09f4a <gpm_suspend_hook+138> call 0xf5e092a0 <getpid@plt> │ │ 0xf5e09f4f <gpm_suspend_hook+143> mov esi,0x14 │ │ 0xf5e09f54 <gpm_suspend_hook+148> mov edi,eax │ │ 0xf5e09f56 <gpm_suspend_hook+150> call 0xf5e09230 <kill@plt> │ │b+ 0xf5e09f5b <gpm_suspend_hook+155> lea edi,[rsp+0x114] │ │ 0xf5e09f62 <gpm_suspend_hook+162> call 0xf5e092c0 <sigemptyset@plt> │ │ 0xf5e09f67 <gpm_suspend_hook+167> lea eax,[rip+0xffffffffffffff53] # 0xf5e09ec0 <gpm_suspend_hook> │ │ 0xf5e09f6d <gpm_suspend_hook+173> xor edx,edx │ │ 0xf5e09f6f <gpm_suspend_hook+175> mov esi,r12d │ └────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ multi-thre Thread 0xf6ff3740 ( In: gpm_suspend_hook L147 PC: 0xf5e09ec0 (gdb) bt #0 gpm_suspend_hook (signum=20) at lib/liblow.c:147 #1 <signal handler called> #2 0xf72c5383 in __GI___poll (fds=0xffffd1c0, nfds=2, timeout=timeout@entry=1500) at ../sysdeps/unix/sysv/linux/poll.c:29 #3 0xf72dea6c in __poll_chk (fds=fds@entry=0xffffd1c0, nfds=<optimized out>, timeout=timeout@entry=1500, fdslen=fdslen@entry=16) at poll_chk.c:27 #4 0xf79a5ce4 in poll (__timeout=1500, __nfds=<optimized out>, __fds=0xffffd1c0) at /usr/include/x86_64-linux-gnux32/bits/poll2.h:41 #5 _nc_timed_wait (sp=sp@entry=0xf2b03680, mode=mode@entry=3, milliseconds=1500, timeleft=timeleft@entry=0x0) at ../../ncurses/tty/lib_twait.c:271 #6 0xf79cb5fa in check_mouse_activity (delay=<optimized out>, sp=0xf2b03680) at ../../ncurses/base/lib_getch.c:496 #7 _nc_wgetch (win=win@entry=0xf3d034c8, result=result@entry=0xffffd328, use_meta=1) at ../../ncurses/base/lib_getch.c:496 #8 0xf79cc093 in wgetch (win=<optimized out>) at ../../ncurses/base/lib_getch.c:133 #9 0x5660fe58 in ScreenManager_run () #10 0x565e58d4 in main () (gdb) p gpm_saved_suspend_hook $1 = {__sigaction_handler = {sa_handler = 0xf79d6660 <handle_SIGTSTP>, sa_sigaction = 0xf79d6660 <handle_SIGTSTP>}, sa_mask = {__val = {0, 0, 0, 0, 4294967295, 16, 0, 4294955536, 4294955424, 10, 0, 1, 0, 4294955560, 0, 2048, 0, 4078920968, 0, 4154716774, 2201035008, 2132542208, 16778261, 4125155488, 0, 4125155392, 0, 4154597826, 0, 4294955680, 0, 4125155392}}, sa_flags = 335544320, sa_restorer = 0xf720f710 <__restore_rt>} (gdb) break handle_SIGTSTP Breakpoint 2 at 0xf79d6660: file ../../ncurses/tty/lib_tstp.c, line 139. (gdb) break 173 Breakpoint 3 at 0xf5e09f5b: file lib/liblow.c, line 174. -- >8 -- Indeed, the hook points to handle_SIGTSTP() in ncurses (<https://sources.debian.org/src/ncurses/6.2-1/ncurses/tty/lib_tstp.c/#L138>). Breaking on the reincarnation line means we break on the LEA after kill(SIGTSTP), which will also be the return address for that function ‒ 0xf5e09f5b. If we continue now, we will end up in ncurses, in handle_SIGTSTP(): -- >8 -- ┌──../../ncurses/tty/lib_tstp.c──────────────────────────────────────────────────────┐ │ 131 * │ │ 132 * This implementation will probably be changed to use signal(3) in │ │ 133 * the future. If nothing else, it's simpler... │ │ 134 */ │ │ 135 │ │ 136 #if USE_SIGTSTP │ │ 137 static void │ │ 138 handle_SIGTSTP(int dummy GCC_UNUSED) │ │B+>139 { │ │ 140 SCREEN *sp = CURRENT_SCREEN; │ │ 141 sigset_t mask, omask; │ │ 142 sigaction_t act, oact; │ │ 143 │ │ 144 #ifdef SIGTTOU │ │ 145 int sigttou_blocked; │ │ 146 #endif │ │ 147 │ │ 148 _nc_globals.have_sigtstp = 1; │ │ 149 T(("handle_SIGTSTP() called")); │ ┌────────────────────────────────────────────────────────────────────────────────────┐ │B+>0xf79d6660 <handle_SIGTSTP> push r14 │ │ 0xf79d6662 <handle_SIGTSTP+2> push r13 │ │ 0xf79d6664 <handle_SIGTSTP+4> push r12 │ │ 0xf79d6666 <handle_SIGTSTP+6> xor r12d,r12d │ │ 0xf79d6669 <handle_SIGTSTP+9> push rbp │ │ 0xf79d666a <handle_SIGTSTP+10> push rbx │ │ 0xf79d666b <handle_SIGTSTP+11> sub esp,0x220 │ │ 0xf79d6671 <handle_SIGTSTP+17> mov edx,DWORD PTR [rip+0x1b8d1] # 0xf79f1f48 │ │ 0xf79d6677 <handle_SIGTSTP+23> mov eax,DWORD PTR fs:0x18 │ │ 0xf79d667f <handle_SIGTSTP+31> mov DWORD PTR [rsp+0x21c],eax │ │ 0xf79d6686 <handle_SIGTSTP+38> xor eax,eax │ │ 0xf79d6688 <handle_SIGTSTP+40> mov eax,DWORD PTR [rip+0x1b92a] # 0xf79f1fb8 │ │ 0xf79d668e <handle_SIGTSTP+46> mov DWORD PTR [edx],0x1 │ │ 0xf79d6695 <handle_SIGTSTP+53> mov eax,DWORD PTR [eax] │ │ 0xf79d6698 <handle_SIGTSTP+56> test eax,eax │ │ 0xf79d669a <handle_SIGTSTP+58> je 0xf79d66ad <handle_SIGTSTP+77> │ │ 0xf79d669c <handle_SIGTSTP+60> cmp DWORD PTR [eax+0x2ac],0x1 │ │ 0xf79d66a4 <handle_SIGTSTP+68> mov r12d,eax │ │ 0xf79d66a7 <handle_SIGTSTP+71> je 0xf79d6830 <handle_SIGTSTP+464> │ │ 0xf79d66ad <handle_SIGTSTP+77> mov ebp,esp │ └────────────────────────────────────────────────────────────────────────────────────┘ multi-thre Thread 0xf6ff3740 ( In: handle_SIGTSTP L139 PC: 0xf79d6660 Program received signal SIGTSTP, Stopped (user). 0xf720f977 in kill () at ../sysdeps/unix/syscall-template.S:78 Continuing. Breakpoint 2, handle_SIGTSTP (dummy=20) at ../../ncurses/tty/lib_tstp.c:139 (gdb) bt #0 handle_SIGTSTP (dummy=20) at ../../ncurses/tty/lib_tstp.c:139 #1 <signal handler called> #2 0xf720f977 in kill () at ../sysdeps/unix/syscall-template.S:78 #3 0xf5e09f5b in gpm_suspend_hook (signum=<optimized out>) at lib/liblow.c:169 #4 <signal handler called> #5 0xf72c5383 in __GI___poll (fds=0xffffd1c0, nfds=2, timeout=timeout@entry=1500) at ../sysdeps/unix/sysv/linux/poll.c:29 #6 0xf72dea6c in __poll_chk (fds=fds@entry=0xffffd1c0, nfds=<optimized out>, timeout=timeout@entry=1500, fdslen=fdslen@entry=16) at poll_chk.c:27 #7 0xf79a5ce4 in poll (__timeout=1500, __nfds=<optimized out>, __fds=0xffffd1c0) at /usr/include/x86_64-linux-gnux32/bits/poll2.h:41 #8 _nc_timed_wait (sp=sp@entry=0xf2b03680, mode=mode@entry=3, milliseconds=1500, timeleft=timeleft@entry=0x0) at ../../ncurses/tty/lib_twait.c:271 #9 0xf79cb5fa in check_mouse_activity (delay=<optimized out>, sp=0xf2b03680) at ../../ncurses/base/lib_getch.c:496 #10 _nc_wgetch (win=win@entry=0xf3d034c8, result=result@entry=0xffffd328, use_meta=1) at ../../ncurses/base/lib_getch.c:496 #11 0xf79cc093 in wgetch (win=<optimized out>) at ../../ncurses/base/lib_getch.c:133 #12 0x5660fe58 in ScreenManager_run () #13 0x565e58d4 in main () -- >8 -- Note how the address for the frame above kill() is, as predicted 0xf5e09f5b. I now tried to get a deeper trace and fumbled it at -- >8 -- #0 _nc_mouse_wrap (sp=0xf2b03680) at ../../ncurses/base/lib_mouse.c:1689 #1 0xf79c8d0d in endwin_sp (sp=sp@entry=0xf2b03680) at ../../ncurses/base/lib_endwin.c:64 #2 0xf79d6809 in handle_SIGTSTP (dummy=<optimized out>) at ../../ncurses/tty/lib_tstp.c:195 -- >8 -- but making a fool of oneself is optional, and one can just cont having arrived here. This will lead to some variant of the following message -- >8 -- (gdb) n mouse_activate (sp=0xf2b03680, on=0) at ../../ncurses/base/lib_mouse.c:1350 warning: Temporarily disabling breakpoints for unloaded shared library "/lib/x86_64-linux-gnux32/libgpm.so.2" 0xf79c2af0 in _nc_flush_sp@plt () from /lib/x86_64-linux-gnux32/libncursesw.so.6 -- >8 -- At this point, gpm_suspend_hook() no longer exists, much like the page it's on: -- >8 -- (gdb) p gpm_suspend_hook $2 = {void (int)} 0xf5e09ec0 <gpm_suspend_hook> (gdb) p *(char *)0xf5e09f5b $3 = -115 '\215' # vs (gdb) p gpm_suspend_hook No symbol "gpm_suspend_hook" in current context. (gdb) p *(char *)0xf5e09f5b Cannot access memory at address 0xf5e09f5b -- >8 -- So if we continue once or twice again -- >8 -- └──────────────────────────────────────────────────────────────────────────────────────┘ │ >0xf720f977 <kill+7> cmp rax,0xfffffffffffff001 │ │ 0xf720f97d <kill+13> jae 0xf720f980 <kill+16> │ │ 0xf720f97f <kill+15> ret │ │ 0xf720f980 <kill+16> mov rcx,QWORD PTR [rip+0x17c4f1] # 0xf738be78 │ │ 0xf720f987 <kill+23> neg eax │ │ 0xf720f989 <kill+25> mov DWORD PTR fs:[rcx],eax │ │ 0xf720f98c <kill+28> or eax,0xffffffff │ │ 0xf720f98f <kill+31> ret │ │ 0xf720f990 <sigpending> mov esi,0x8 │ │ 0xf720f995 <sigpending+5> mov eax,0x4000020a │ │ 0xf720f99a <sigpending+10> syscall │ │ 0xf720f99c <sigpending+12> cmp eax,0xfffff000 │ │ 0xf720f9a1 <sigpending+17> ja 0xf720f9a8 <sigpending+24> │ │ 0xf720f9a3 <sigpending+19> ret │ │ 0xf720f9a4 <sigpending+20> nop DWORD PTR [rax+0x0] │ │ 0xf720f9a8 <sigpending+24> neg eax │ │ 0xf720f9aa <sigpending+26> mov edx,DWORD PTR fs:0x0 │ │ 0xf720f9b2 <sigpending+34> rex add edx,DWORD PTR [rip+0x17c4bf] # 0xf738be78 │ │ 0xf720f9b9 <sigpending+41> mov DWORD PTR [edx],eax │ │ 0xf720f9bc <sigpending+44> mov eax,0xffffffff │ multi-thre Thread 0xf6ff3740 ( In: handle_SIGTSTP L173 PC: 0xf79d66ad #9 0xf79cb5fa in check_mouse_activ mized out>, sp=0xf2b03680) at ../../ncurses/base/lib_getch.c:496 ?? 5e09f5b (gdb) bt #0 0xf720f977 in kill () at ../sysdeps/unix/syscall-template.S:78 #1 0xf79d677f in handle_SIGTSTP (dummy=<optimized out>) at ../../ncurses/tty/lib_tstp.c:216 #2 <signal handler called> #3 0xf720f977 in kill () at ../sysdeps/unix/syscall-template.S:78 #4 0xf5e09f5b in ?? () #5 0xffff0000 in ?? () #6 0x000029a4 in ?? () #7 0x00000000 in ?? () (gdb) cont Continuing. Program received signal SIGSEGV, Segmentation fault. 0xf5e09f5b in ?? () Cannot access memory at address 0xf5e09f5b (gdb) bt #0 0xf5e09f5b in ?? () #1 0xffff0000 in ?? () #2 0x000029a4 in ?? () #3 0x00000000 in ?? () (gdb) cont Continuing. warning: Temporarily disabling breakpoints for unloaded shared library "/lib/x86_64-linux-gnux32/libgpm.so.2" htop 2.2.0 aborting. Please report bug at http://hisham.hm/htop Program received signal SIGSEGV, Segmentation fault. uw_frame_state_for (context=<optimized out>, fs=<optimized out>) at ../../../src/libgcc/unwind-dw2.c:1271 ../../../src/libgcc/unwind-dw2.c: No such file or directory. Continuing. Program terminated with signal SIGSEGV, Segmentation fault. The program no longer exists. (gdb) quit nabijaczleweli@szarotka:/mnt/zest/htop/gpm/src$ -- >8 -- The top of the stack was 0xf5e09f5b, as the kill() call tried to return there, this yielded a SIGSEGV, this went into htop's handler, here we are. Breaking on dlclose() yields this backtrace, pointing at lib_mouse.c <https://sources.debian.org/src/ncurses/6.2-1/ncurses/base/lib_mouse.c#L474>: -- >8 -- Breakpoint 1, __interceptor_dlclose (handle=0xf4902e00) at ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:5925 5925 INTERCEPTOR(__sanitizer_FILE *, open_wmemstream, wchar_t **ptr, (gdb) bt #0 __interceptor_dlclose (handle=0xf4902e00) at ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:5925 #1 0xf79cd981 in unload_gpm_library (sp=0xf2b03680) at ../../ncurses/base/lib_mouse.c:480 #2 enable_gpm_mouse (sp=sp@entry=0xf2b03680, enable=enable@entry=false) at ../../ncurses/base/lib_mouse.c:567 #3 0xf79ce34a in mouse_activate (on=0, sp=0xf2b03680) at ../../ncurses/base/lib_mouse.c:1405 #4 mouse_activate (sp=<optimized out>, on=<optimized out>) at ../../ncurses/base/lib_mouse.c:1348 #5 0xf79c8d0d in endwin_sp (sp=sp@entry=0xf2b03680) at ../../ncurses/base/lib_endwin.c:64 #6 0xf79d6809 in handle_SIGTSTP (dummy=<optimized out>) at ../../ncurses/tty/lib_tstp.c:195 #7 <signal handler called> #8 0xf720f977 in kill () at ../sysdeps/unix/syscall-template.S:78 #9 0xf5e09f5b in gpm_suspend_hook (signum=<optimized out>) at lib/liblow.c:169 #10 <signal handler called> #11 0xf72c5383 in __GI___poll (fds=0xffffd1c0, nfds=2, timeout=timeout@entry=1500) at ../sysdeps/unix/sysv/linux/poll.c:29 #12 0xf72dea6c in __poll_chk (fds=fds@entry=0xffffd1c0, nfds=<optimized out>, timeout=timeout@entry=1500, fdslen=fdslen@entry=16) at poll_chk.c:27 #13 0xf79a5ce4 in poll (__timeout=1500, __nfds=<optimized out>, __fds=0xffffd1c0) at /usr/include/x86_64-linux-gnux32/bits/poll2.h:41 #14 _nc_timed_wait (sp=sp@entry=0xf2b03680, mode=mode@entry=3, milliseconds=1500, timeleft=timeleft@entry=0x0) at ../../ncurses/tty/lib_twait.c:271 #15 0xf79cb5fa in check_mouse_activity (delay=<optimized out>, sp=0xf2b03680) at ../../ncurses/base/lib_getch.c:496 #16 _nc_wgetch (win=win@entry=0xf3d034c8, result=result@entry=0xffffd328, use_meta=1) at ../../ncurses/base/lib_getch.c:496 #17 0xf79cc093 in wgetch (win=<optimized out>) at ../../ncurses/base/lib_getch.c:133 #18 0x5660fe58 in ScreenManager_run () #19 0x565e58d4 in main () -- >8 -- I don't really see a downside to Just™ not calling dlclose() there, seeing as doing the equivalent by overriding it with a nop makes it work just as well (or, well, better, since it doesn't crash; granted, the selection start is still stuck to the end of the Quit htop field when suspended, but that's something for a different time), and the two outcomes are dlopen()ning it again or being murdered anyway, but this is not my area. Updating severity to reflect the new reality as well ("htop fails on a port" => "ncurses+gpm segfaults all mouse programs"). Triumphantly, наб
signature.asc
Description: PGP signature