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,
наб

Attachment: signature.asc
Description: PGP signature

Reply via email to