mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-06-11 16:59:59 +00:00
telnetd: Fix deadlock on cleanup
The cleanup function in telnetd is called both directly and on SIGCHLD signals. This triggered a deadlock in glibc and was reproduced in glibc 2.27 while running on a 4.14.30 kernel. Signed-off-by: Seiichi Ishitsuka <ishitsuka.sc@ncos.nec.co.jp> Signed-off-by: Khem Raj <raj.khem@gmail.com>
This commit is contained in:
committed by
Khem Raj
parent
d3bd6dac44
commit
d9e257abbe
+114
@@ -0,0 +1,114 @@
|
|||||||
|
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
|
||||||
|
|
||||||
@@ -11,6 +11,7 @@ SRC_URI = "ftp://ftp.uk.linux.org/pub/linux/Networking/netkit/${BP}.tar.gz \
|
|||||||
file://telnet-xinetd \
|
file://telnet-xinetd \
|
||||||
file://cross-compile.patch \
|
file://cross-compile.patch \
|
||||||
file://0001-telnet-telnetd-Fix-print-format-strings.patch \
|
file://0001-telnet-telnetd-Fix-print-format-strings.patch \
|
||||||
|
file://0001-telnet-telnetd-Fix-deadlock-on-cleanup.patch \
|
||||||
"
|
"
|
||||||
|
|
||||||
UPSTREAM_CHECK_URI = "${DEBIAN_MIRROR}/main/n/netkit-telnet/"
|
UPSTREAM_CHECK_URI = "${DEBIAN_MIRROR}/main/n/netkit-telnet/"
|
||||||
|
|||||||
Reference in New Issue
Block a user