--- /dev/null
+From f0992098cadb4c9c6a00703b66cafe604e178fea Mon Sep 17 00:00:00 2001
+From: Samuel Thibault <samuel.thibault@ens-lyon.org>
+Date: Sun, 29 Nov 2020 20:35:23 +0100
+Subject: speakup: Reject setting the speakup line discipline outside of speakup
+
+From: Samuel Thibault <samuel.thibault@ens-lyon.org>
+
+commit f0992098cadb4c9c6a00703b66cafe604e178fea upstream.
+
+Speakup exposing a line discipline allows userland to try to use it,
+while it is deemed to be useless, and thus uselessly exposes potential
+bugs. One of them is simply that in such a case if the line sends data,
+spk_ttyio_receive_buf2 is called and crashes since spk_ttyio_synth
+is NULL.
+
+This change restricts the use of the speakup line discipline to
+speakup drivers, thus avoiding such kind of issues altogether.
+
+Cc: stable@vger.kernel.org
+Reported-by: Shisong Qin <qinshisong1205@gmail.com>
+Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
+Tested-by: Shisong Qin <qinshisong1205@gmail.com>
+Link: https://lore.kernel.org/r/20201129193523.hm3f6n5xrn6fiyyc@function
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/staging/speakup/spk_ttyio.c | 38 +++++++++++++++++++++---------------
+ 1 file changed, 23 insertions(+), 15 deletions(-)
+
+--- a/drivers/staging/speakup/spk_ttyio.c
++++ b/drivers/staging/speakup/spk_ttyio.c
+@@ -46,28 +46,20 @@ static int spk_ttyio_ldisc_open(struct t
+ {
+ struct spk_ldisc_data *ldisc_data;
+
++ if (tty != speakup_tty)
++ /* Somebody tried to use this line discipline outside speakup */
++ return -ENODEV;
++
+ if (tty->ops->write == NULL)
+ return -EOPNOTSUPP;
+
+- mutex_lock(&speakup_tty_mutex);
+- if (speakup_tty) {
+- mutex_unlock(&speakup_tty_mutex);
+- return -EBUSY;
+- }
+- speakup_tty = tty;
+-
+ ldisc_data = kmalloc(sizeof(struct spk_ldisc_data), GFP_KERNEL);
+- if (!ldisc_data) {
+- speakup_tty = NULL;
+- mutex_unlock(&speakup_tty_mutex);
+- pr_err("speakup: Failed to allocate ldisc_data.\n");
++ if (!ldisc_data)
+ return -ENOMEM;
+- }
+
+ sema_init(&ldisc_data->sem, 0);
+ ldisc_data->buf_free = true;
+- speakup_tty->disc_data = ldisc_data;
+- mutex_unlock(&speakup_tty_mutex);
++ tty->disc_data = ldisc_data;
+
+ return 0;
+ }
+@@ -184,9 +176,25 @@ static int spk_ttyio_initialise_ldisc(st
+
+ tty_unlock(tty);
+
++ mutex_lock(&speakup_tty_mutex);
++ speakup_tty = tty;
+ ret = tty_set_ldisc(tty, N_SPEAKUP);
+ if (ret)
+- pr_err("speakup: Failed to set N_SPEAKUP on tty\n");
++ speakup_tty = NULL;
++ mutex_unlock(&speakup_tty_mutex);
++
++ if (!ret)
++ /* Success */
++ return 0;
++
++ pr_err("speakup: Failed to set N_SPEAKUP on tty\n");
++
++ tty_lock(tty);
++ if (tty->ops->close)
++ tty->ops->close(tty, NULL);
++ tty_unlock(tty);
++
++ tty_kclose(tty);
+
+ return ret;
+ }