diff options
| author | Pei Xiao <xiaopei01@kylinos.cn> | 2026-07-01 10:01:10 +0800 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-07-17 16:05:02 +0200 |
| commit | 7d3a708af7f4e2af9e114d731bd60d30c1ec884e (patch) | |
| tree | f356cc57354453efa8deed2f06d7fee6db53e228 /drivers/ipack | |
| parent | b6b5d64cb161a28347d64dc3168a636c4abb68d5 (diff) | |
| download | linux-next-7d3a708af7f4e2af9e114d731bd60d30c1ec884e.tar.gz linux-next-7d3a708af7f4e2af9e114d731bd60d30c1ec884e.zip | |
ipack: ipoctal: add rwsem to guard against TOCTOU in remove path
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@kylinos.cn>
Link: https://patch.msgid.link/fbce75010a0f0a3a3709a5e06fd0ffd19ca0a0ed.1782870760.git.xiaopei01@kylinos.cn
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/ipack')
| -rw-r--r-- | drivers/ipack/devices/ipoctal.c | 16 |
1 files 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); |
