[RFC PATCH 6.6.y 4/4] misc: ti-st: synchronize TTY teardown with transport removal

From: Hongyan Xu

Date: Fri Jul 24 2026 - 01:30:59 EST


Unregistering the line discipline does not close an instance which is
already attached to a TTY. Its callbacks and work_write_wakeup can keep
using the transport after st_core_exit() frees it.

Protect the attached TTY pointer with the core lock. On removal, take a
TTY reference and synchronously hang it up, then cancel the wakeup work
before releasing the core. Also cancel the work in normal close and clear
disc_data only after the close cleanup is complete.

Fixes: 53618cc1e51e ("Staging: sources for ST core")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Hongyan Xu <getshell@xxxxxxxxxx>
---
drivers/misc/ti-st/st_core.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c
index 3aed6c3..7610656 100644
--- a/drivers/misc/ti-st/st_core.c
+++ b/drivers/misc/ti-st/st_core.c
@@ -723,13 +723,18 @@ EXPORT_SYMBOL_GPL(st_unregister);
static int st_tty_open(struct tty_struct *tty)
{
struct st_data_s *st_gdata __free(st_kim) = NULL;
+ unsigned long flags;
+
pr_info("%s ", __func__);

st_kim_ref(&st_gdata, 0);
if (!st_gdata)
return -ENODEV;
+
+ spin_lock_irqsave(&st_gdata->lock, flags);
st_gdata->tty = tty;
tty->disc_data = st_gdata;
+ spin_unlock_irqrestore(&st_gdata->lock, flags);

/* don't do an wakeup for now */
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
@@ -757,6 +762,7 @@ static void st_tty_close(struct tty_struct *tty)
struct st_data_s *st_gdata = tty->disc_data;

pr_info("%s ", __func__);
+ cancel_work_sync(&st_gdata->work_write_wakeup);

/*
* TODO:
@@ -777,7 +783,6 @@ static void st_tty_close(struct tty_struct *tty)
* N_TI_WL ldisc is un-installed
*/
st_kim_complete(st_gdata->kim_data);
- st_gdata->tty = NULL;
/* Flush any pending characters in the driver and discipline. */
tty_ldisc_flush(tty);
tty_driver_flush_buffer(tty);
@@ -791,6 +796,8 @@ static void st_tty_close(struct tty_struct *tty)
st_gdata->rx_state = ST_W4_PACKET_TYPE;
kfree_skb(st_gdata->rx_skb);
st_gdata->rx_skb = NULL;
+ st_gdata->tty = NULL;
+ tty->disc_data = NULL;
spin_unlock_irqrestore(&st_gdata->lock, flags);

pr_debug("%s: done ", __func__);
@@ -905,6 +912,8 @@ err_unreg_ldisc:

void st_core_exit(struct st_data_s *st_gdata)
{
+ struct tty_struct *tty;
+ unsigned long flags;
long err;

if (!st_gdata)
@@ -914,6 +923,14 @@ void st_core_exit(struct st_data_s *st_gdata)
wait_event(st_gdata->users_wait,
!atomic_read(&st_gdata->active_users));

+ spin_lock_irqsave(&st_gdata->lock, flags);
+ tty = tty_kref_get(st_gdata->tty);
+ spin_unlock_irqrestore(&st_gdata->lock, flags);
+ if (tty)
+ tty_vhangup(tty);
+ cancel_work_sync(&st_gdata->work_write_wakeup);
+ tty_kref_put(tty);
+
/* internal module cleanup */
err = st_ll_deinit(st_gdata);
if (err)
--
2.50.1.windows.1