[PATCH 2/2] ipack: ipoctal: add rwsem to guard against TOCTOU in remove path
From: Pei Xiao
Date: Thu Jun 25 2026 - 22:33:44 EST
The "removed" flag check in each tty op has a TOCTOU race with
__ipoctal_remove(): the device could be removed between the flag
check and the subsequent access to hardware resources (channel
registers via iowrite8, or xmit_buf in write_tty).
Close this race by introducing a read-write semaphore (remove_sem).
The tty ops acquire the read lock via guard(rwsem_read) for the
full duration of the operation, while __ipoctal_remove() acquires
the write lock via scoped_guard(rwsem_write) when setting the
removed flag. This ensures that once removed is true, no in-flight
tty op can still be accessing resources that are about to be freed
by the remove path.
Signed-off-by: Pei Xiao <xiaopei01@xxxxxxxxxx>
---
drivers/ipack/devices/ipoctal.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c
index bf71b8952a7c..2169e4b75f98 100644
--- a/drivers/ipack/devices/ipoctal.c
+++ b/drivers/ipack/devices/ipoctal.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/kref.h>
+#include <linux/rwsem.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/serial.h>
@@ -54,6 +55,7 @@ struct ipoctal {
u8 __iomem *int_space;
struct kref kref;
struct module *carrier_owner;
+ struct rw_semaphore remove_sem;
bool removed;
};
@@ -81,7 +83,7 @@ static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty)
channel = dev_get_drvdata(tty->dev);
ipoctal = chan_to_ipoctal(channel, tty->index);
-
+ guard(rwsem_read)(&ipoctal->remove_sem);
if (ipoctal->removed)
return -ENODEV;
@@ -476,7 +478,7 @@ static ssize_t ipoctal_write_tty(struct tty_struct *tty, const u8 *buf,
struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index);
size_t char_copied;
-
+ guard(rwsem_read)(&ipoctal->remove_sem);
if (ipoctal->removed || !channel->tty_port.xmit_buf)
return 0;
@@ -522,7 +524,7 @@ static void ipoctal_set_termios(struct tty_struct *tty,
struct ipoctal *ipoctal = chan_to_ipoctal(channel, tty->index);
speed_t baud;
-
+ guard(rwsem_read)(&ipoctal->remove_sem);
if (ipoctal->removed)
return;
@@ -660,7 +662,7 @@ static void ipoctal_hangup(struct tty_struct *tty)
return;
ipoctal = chan_to_ipoctal(channel, tty->index);
-
+ guard(rwsem_read)(&ipoctal->remove_sem);
if (ipoctal->removed)
return;
@@ -686,7 +688,7 @@ static void ipoctal_shutdown(struct tty_struct *tty)
return;
ipoctal = chan_to_ipoctal(channel, tty->index);
-
+ guard(rwsem_read)(&ipoctal->remove_sem);
if (ipoctal->removed)
return;
@@ -736,6 +738,7 @@ static int ipoctal_probe(struct ipack_device *dev)
return -ENOMEM;
kref_init(&ipoctal->kref);
+ init_rwsem(&ipoctal->remove_sem);
ipoctal->dev = dev;
ipoctal->carrier_owner = dev->bus->owner;
@@ -755,7 +758,8 @@ static void __ipoctal_remove(struct ipoctal *ipoctal)
{
int i;
- ipoctal->removed = true;
+ scoped_guard(rwsem_write, &ipoctal->remove_sem)
+ ipoctal->removed = true;
ipoctal->dev->bus->ops->free_irq(ipoctal->dev);
--
2.25.1