| 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 |
| |