| From 06ed6a6bf25a22902846097d6b6c97e070c2c326 Mon Sep 17 00:00:00 2001 | 
 | From: Seiichi Ishitsuka <ishitsuka.sc@ncos.nec.co.jp> | 
 | Date: Fri, 1 Jun 2018 14:27:35 +0900 | 
 | Subject: [PATCH] telnetd: Fix deadlock on cleanup | 
 |  | 
 | The cleanup function in telnetd is called both directly and on SIGCHLD | 
 | signals. This, unfortunately, triggered a deadlock in eglibc 2.9 while | 
 | running on a 2.6.31.11 kernel. | 
 |  | 
 | What we were seeing is hangs like these: | 
 |  | 
 |   (gdb) bt | 
 |   #0  0xb7702424 in __kernel_vsyscall () | 
 |   #1  0xb7658e61 in __lll_lock_wait_private () from ./lib/libc.so.6 | 
 |   #2  0xb767e7b5 in _L_lock_15 () from ./lib/libc.so.6 | 
 |   #3  0xb767e6e0 in utmpname () from ./lib/libc.so.6 | 
 |   #4  0xb76bcde7 in logout () from ./lib/libutil.so.1 | 
 |   #5  0x0804c827 in cleanup () | 
 |   #6  <signal handler called> | 
 |   #7  0xb7702424 in __kernel_vsyscall () | 
 |   #8  0xb7641003 in __fcntl_nocancel () from ./lib/libc.so.6 | 
 |   #9  0xb767e0c3 in getutline_r_file () from ./lib/libc.so.6 | 
 |   #10 0xb767d675 in getutline_r () from ./lib/libc.so.6 | 
 |   #11 0xb76bce42 in logout () from ./lib/libutil.so.1 | 
 |   #12 0x0804c827 in cleanup () | 
 |   #13 0x0804a0b5 in telnet () | 
 |   #14 0x0804a9c3 in main () | 
 |  | 
 | and what has happened here is that the user closes the telnet session | 
 | via the escape character. This causes telnetd to call cleanup in frame | 
 | the SIGCHLD signal is delivered while telnetd is executing cleanup. | 
 |  | 
 | Telnetd then calls the signal handler for SIGCHLD, which is cleanup(). | 
 | Ouch. The actual deadlock is in libc. getutline_r in frame #10 gets the | 
 | __libc_utmp_lock lock, and utmpname above does the same thing in frame | 
 |  | 
 | The fix registers the SIGCHLD handler as cleanup_sighandler, and makes | 
 | cleanup disable the SIGCHLD signal before calling cleanup_sighandler. | 
 |  | 
 | Signed-off-by: Simon Kagstrom <simon.kagstrom@netinsight.net> | 
 |  | 
 | The patch was imported from the Ubuntu netkit-telnet package. | 
 | (https://bugs.launchpad.net/ubuntu/+source/netkit-telnet/+bug/507455) | 
 |  | 
 | A previous patch declaring attributes of functions, but it is not used | 
 | in upstream. | 
 |  | 
 | Signed-off-by: Seiichi Ishitsuka <ishitsuka.sc@ncos.nec.co.jp> | 
 | --- | 
 |  telnetd/ext.h      |  1 + | 
 |  telnetd/sys_term.c | 17 ++++++++++++++++- | 
 |  telnetd/telnetd.c  |  2 +- | 
 |  3 files changed, 18 insertions(+), 2 deletions(-) | 
 |  | 
 | diff --git a/telnetd/ext.h b/telnetd/ext.h | 
 | index b98d6ec..08f9d07 100644 | 
 | --- a/telnetd/ext.h | 
 | +++ b/telnetd/ext.h | 
 | @@ -97,6 +97,7 @@ void add_slc(int, int, int); | 
 |  void check_slc(void); | 
 |  void change_slc(int, int, int); | 
 |  void cleanup(int); | 
 | +void cleanup_sighandler(int); | 
 |  void clientstat(int, int, int); | 
 |  void copy_termbuf(char *, int); | 
 |  void deferslc(void); | 
 | diff --git a/telnetd/sys_term.c b/telnetd/sys_term.c | 
 | index 5b4aa84..c4fb0f7 100644 | 
 | --- a/telnetd/sys_term.c | 
 | +++ b/telnetd/sys_term.c | 
 | @@ -719,7 +719,7 @@ static void addarg(struct argv_stuff *avs, const char *val) { | 
 |   * This is the routine to call when we are all through, to | 
 |   * clean up anything that needs to be cleaned up. | 
 |   */ | 
 | -void cleanup(int sig) { | 
 | +void cleanup_sighandler(int sig) { | 
 |      char *p; | 
 |      (void)sig; | 
 |   | 
 | @@ -742,3 +742,18 @@ void cleanup(int sig) { | 
 |      shutdown(net, 2); | 
 |      exit(0); | 
 |  } | 
 | + | 
 | +void cleanup(int sig) { | 
 | +    sigset_t mask, oldmask; | 
 | + | 
 | +    /* Set up the mask of signals to temporarily block. */ | 
 | +    sigemptyset (&mask); | 
 | +    sigaddset (&mask, SIGCHLD); | 
 | + | 
 | +    /* Block SIGCHLD while running cleanup */ | 
 | +    sigprocmask (SIG_BLOCK, &mask, &oldmask); | 
 | + | 
 | +    cleanup_sighandler(sig); | 
 | +    /* Technically not needed since cleanup_sighandler exits */ | 
 | +    sigprocmask (SIG_UNBLOCK, &mask, NULL); | 
 | +} | 
 | diff --git a/telnetd/telnetd.c b/telnetd/telnetd.c | 
 | index 9ace838..788919c 100644 | 
 | --- a/telnetd/telnetd.c | 
 | +++ b/telnetd/telnetd.c | 
 | @@ -833,7 +833,7 @@ void telnet(int f, int p) | 
 |      signal(SIGTTOU, SIG_IGN); | 
 |  #endif | 
 |       | 
 | -    signal(SIGCHLD, cleanup); | 
 | +    signal(SIGCHLD, cleanup_sighandler); | 
 |       | 
 |  #ifdef TIOCNOTTY | 
 |      { | 
 | --  | 
 | 2.7.4 | 
 |  |