drivers/tty/ipwireless/hardware.c | 1 + 1 file changed, 1 insertion(+)
When IPWireless PCMCIA card is being detached, the ipw_hardware is
deallocated in ipwireless_hardware_free(). However, the hw->tasklet may
still be running or pending, leading to use-after-free bugs when the
already freed ipw_hardware is accessed again in ipwireless_do_tasklet().
One race condition scenario is as follows:
CPU 0 (cleanup) | CPU 1 (interrupt)
ipwireless_hardware_free() | ipwireless_interrupt()
ipwireless_stop_interrupts()| ipwireless_handle_v1_interrupt()
do_close_hardware() | tasklet_schedule()
synchronize_irq() |
kfree(hw) //FREE | ipwireless_do_tasklet() //handler
| hw = from_tasklet() //USE
| hw-> //USE
Fix this by ensuring hw->tasklet is properly canceled before ipw_hardware
is released. Add tasklet_kill() in ipwireless_stop_interrupts() to
synchronize with any pending or running tasklet. Since do_close_hardware()
could prevent further interrupts, place tasklet_kill() after it to avoid
the tasklet being rescheduled by ipwireless_interrupt().
Fixes: 099dc4fb6265 ("ipwireless: driver for PC Card 3G/UMTS modem")
Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
---
drivers/tty/ipwireless/hardware.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/tty/ipwireless/hardware.c b/drivers/tty/ipwireless/hardware.c
index e18848267be..c736cba751f 100644
--- a/drivers/tty/ipwireless/hardware.c
+++ b/drivers/tty/ipwireless/hardware.c
@@ -1725,6 +1725,7 @@ void ipwireless_stop_interrupts(struct ipw_hardware *hw)
/* Prevent the hardware from sending any more interrupts */
do_close_hardware(hw);
+ tasklet_kill(&hw->tasklet);
}
}
--
2.34.1
On Sun, Feb 08, 2026 at 02:25:38PM +0800, Duoming Zhou wrote:
> When IPWireless PCMCIA card is being detached, the ipw_hardware is
> deallocated in ipwireless_hardware_free(). However, the hw->tasklet may
> still be running or pending, leading to use-after-free bugs when the
> already freed ipw_hardware is accessed again in ipwireless_do_tasklet().
Nice, do you have this hardware to test this with?
>
> One race condition scenario is as follows:
>
> CPU 0 (cleanup) | CPU 1 (interrupt)
> ipwireless_hardware_free() | ipwireless_interrupt()
> ipwireless_stop_interrupts()| ipwireless_handle_v1_interrupt()
> do_close_hardware() | tasklet_schedule()
> synchronize_irq() |
> kfree(hw) //FREE | ipwireless_do_tasklet() //handler
> | hw = from_tasklet() //USE
> | hw-> //USE
>
> Fix this by ensuring hw->tasklet is properly canceled before ipw_hardware
> is released. Add tasklet_kill() in ipwireless_stop_interrupts() to
> synchronize with any pending or running tasklet. Since do_close_hardware()
> could prevent further interrupts, place tasklet_kill() after it to avoid
> the tasklet being rescheduled by ipwireless_interrupt().
How was this issue found and tested?
> Fixes: 099dc4fb6265 ("ipwireless: driver for PC Card 3G/UMTS modem")
> Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
No CC: stable? Why not?
thanks,
greg k-h
On Sun, 8 Feb 2026 07:38:00 +0100 Greg KH wrote:
> > When IPWireless PCMCIA card is being detached, the ipw_hardware is
> > deallocated in ipwireless_hardware_free(). However, the hw->tasklet may
> > still be running or pending, leading to use-after-free bugs when the
> > already freed ipw_hardware is accessed again in ipwireless_do_tasklet().
>
> Nice, do you have this hardware to test this with?
I don't have the real hardware. In order to reproduce the bug, I simulate
the IPWireless PCMCIA card in the qemu by allocating and configuring the
necessary resources(I/O ports, memory regions, interrupts and so on) to
correspond with the hardware expected by the driver in the initialization
code of the virtual device.
> >
> > One race condition scenario is as follows:
> >
> > CPU 0 (cleanup) | CPU 1 (interrupt)
> > ipwireless_hardware_free() | ipwireless_interrupt()
> > ipwireless_stop_interrupts()| ipwireless_handle_v1_interrupt()
> > do_close_hardware() | tasklet_schedule()
> > synchronize_irq() |
> > kfree(hw) //FREE | ipwireless_do_tasklet() //handler
> > | hw = from_tasklet() //USE
> > | hw-> //USE
> >
> > Fix this by ensuring hw->tasklet is properly canceled before ipw_hardware
> > is released. Add tasklet_kill() in ipwireless_stop_interrupts() to
> > synchronize with any pending or running tasklet. Since do_close_hardware()
> > could prevent further interrupts, place tasklet_kill() after it to avoid
> > the tasklet being rescheduled by ipwireless_interrupt().
>
> How was this issue found and tested?
The issue was found by static analysis. I test it through the following steps:
1. Simulating the IPWireless PCMCIA device in the qemu and enable it to trigger interrupts.
2. Controlling the removal and attachment of device via sysfs.
3. Triggering interrupts by writing data to device registers via /dev/mem memory mapping
in userspace.
4. In order to ensure that there are unfinished tasklet during the removal process, I
manually inject delays such as mdelay() into tasklet handler.
> > Fixes: 099dc4fb6265 ("ipwireless: driver for PC Card 3G/UMTS modem")
> > Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
>
> No CC: stable? Why not?
Thanks for checking, You are right, it should go to the stable.
Best regards,
Duoming Zhou
On Sun, Feb 08, 2026 at 06:28:19PM +0800, duoming@zju.edu.cn wrote:
> On Sun, 8 Feb 2026 07:38:00 +0100 Greg KH wrote:
> > > When IPWireless PCMCIA card is being detached, the ipw_hardware is
> > > deallocated in ipwireless_hardware_free(). However, the hw->tasklet may
> > > still be running or pending, leading to use-after-free bugs when the
> > > already freed ipw_hardware is accessed again in ipwireless_do_tasklet().
> >
> > Nice, do you have this hardware to test this with?
>
> I don't have the real hardware. In order to reproduce the bug, I simulate
> the IPWireless PCMCIA card in the qemu by allocating and configuring the
> necessary resources(I/O ports, memory regions, interrupts and so on) to
> correspond with the hardware expected by the driver in the initialization
> code of the virtual device.
I wonder if this device even is still around, given that pcmcia is all
but dead for a very long time.
> > > One race condition scenario is as follows:
> > >
> > > CPU 0 (cleanup) | CPU 1 (interrupt)
> > > ipwireless_hardware_free() | ipwireless_interrupt()
> > > ipwireless_stop_interrupts()| ipwireless_handle_v1_interrupt()
> > > do_close_hardware() | tasklet_schedule()
> > > synchronize_irq() |
> > > kfree(hw) //FREE | ipwireless_do_tasklet() //handler
> > > | hw = from_tasklet() //USE
> > > | hw-> //USE
> > >
> > > Fix this by ensuring hw->tasklet is properly canceled before ipw_hardware
> > > is released. Add tasklet_kill() in ipwireless_stop_interrupts() to
> > > synchronize with any pending or running tasklet. Since do_close_hardware()
> > > could prevent further interrupts, place tasklet_kill() after it to avoid
> > > the tasklet being rescheduled by ipwireless_interrupt().
> >
> > How was this issue found and tested?
>
> The issue was found by static analysis. I test it through the following steps:
> 1. Simulating the IPWireless PCMCIA device in the qemu and enable it to trigger interrupts.
> 2. Controlling the removal and attachment of device via sysfs.
So this is with the bind/unbind logic, or some other way? If you are
unloading the driver, that is something that only root can do, and this
is a debugging facility, not a "real" way to control drivers and devices
(yes, the virt drivers abuse this to no end, every time I see this I
laugh...)
> 3. Triggering interrupts by writing data to device registers via /dev/mem memory mapping
> in userspace.
Interrupts would not happen if the device is removed. Or is this only
if the driver is unbound?
> 4. In order to ensure that there are unfinished tasklet during the removal process, I
> manually inject delays such as mdelay() into tasklet handler.
That's a lot of work for a piece of obsolete hardware, but hey, thanks
for doing this!
> > > Fixes: 099dc4fb6265 ("ipwireless: driver for PC Card 3G/UMTS modem")
> > > Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
> >
> > No CC: stable? Why not?
>
> Thanks for checking, You are right, it should go to the stable.
Let's see what the maintainers of this driver say.
thanks,
greg k-h
On Sun, 8 Feb 2026, Greg KH wrote: > > I don't have the real hardware. In order to reproduce the bug, I simulate > > the IPWireless PCMCIA card in the qemu by allocating and configuring the > > necessary resources(I/O ports, memory regions, interrupts and so on) to > > correspond with the hardware expected by the driver in the initialization > > code of the virtual device. > > I wonder if this device even is still around, given that pcmcia is all > but dead for a very long time. I doubt that this device is still around anywhere where reasonably new kernels (including LTS) would matter. I don't think I've seen this device (which was back then donated to me by T-Mobile CZ in order to get it supported in Linux, and I am not sure how much global adoption it got afterwards) for, let's say, past 15 years :) I think (let's see what David, ho took the maintainership over for me afterwards, has to say) we'd better deprecate and drop the whole thing, rather than trying to pretend that it's still actively being taken care of. -- Jiri Kosina SUSE Labs
On Date: Sun, 8 Feb 2026 12:00:08 +0100 Greg KH wrote: > > > > When IPWireless PCMCIA card is being detached, the ipw_hardware is > > > > deallocated in ipwireless_hardware_free(). However, the hw->tasklet may > > > > still be running or pending, leading to use-after-free bugs when the > > > > already freed ipw_hardware is accessed again in ipwireless_do_tasklet(). > > > > > > Nice, do you have this hardware to test this with? > > > > I don't have the real hardware. In order to reproduce the bug, I simulate > > the IPWireless PCMCIA card in the qemu by allocating and configuring the > > necessary resources(I/O ports, memory regions, interrupts and so on) to > > correspond with the hardware expected by the driver in the initialization > > code of the virtual device. > > I wonder if this device even is still around, given that pcmcia is all > but dead for a very long time. > > > > > One race condition scenario is as follows: > > > > > > > > CPU 0 (cleanup) | CPU 1 (interrupt) > > > > ipwireless_hardware_free() | ipwireless_interrupt() > > > > ipwireless_stop_interrupts()| ipwireless_handle_v1_interrupt() > > > > do_close_hardware() | tasklet_schedule() > > > > synchronize_irq() | > > > > kfree(hw) //FREE | ipwireless_do_tasklet() //handler > > > > | hw = from_tasklet() //USE > > > > | hw-> //USE > > > > > > > > Fix this by ensuring hw->tasklet is properly canceled before ipw_hardware > > > > is released. Add tasklet_kill() in ipwireless_stop_interrupts() to > > > > synchronize with any pending or running tasklet. Since do_close_hardware() > > > > could prevent further interrupts, place tasklet_kill() after it to avoid > > > > the tasklet being rescheduled by ipwireless_interrupt(). > > > > > > How was this issue found and tested? > > > > The issue was found by static analysis. I test it through the following steps: > > 1. Simulating the IPWireless PCMCIA device in the qemu and enable it to trigger interrupts. > > 2. Controlling the removal and attachment of device via sysfs. > > So this is with the bind/unbind logic, or some other way? If you are > unloading the driver, that is something that only root can do, and this > is a debugging facility, not a "real" way to control drivers and devices > (yes, the virt drivers abuse this to no end, every time I see this I > laugh...) When the PCMCIA device is attached, we can operate the file /sys/bus/pcmcia/devices/.../remove to detach the device. > > 3. Triggering interrupts by writing data to device registers via /dev/mem memory mapping > > in userspace. > > Interrupts would not happen if the device is removed. Or is this only > if the driver is unbound? The interrupts should be triggered before the deivce is removed. Best regards, Duoming Zhou
On Sun, Feb 08, 2026 at 09:57:32PM +0800, duoming@zju.edu.cn wrote: > On Date: Sun, 8 Feb 2026 12:00:08 +0100 Greg KH wrote: > > > > > When IPWireless PCMCIA card is being detached, the ipw_hardware is > > > > > deallocated in ipwireless_hardware_free(). However, the hw->tasklet may > > > > > still be running or pending, leading to use-after-free bugs when the > > > > > already freed ipw_hardware is accessed again in ipwireless_do_tasklet(). > > > > > > > > Nice, do you have this hardware to test this with? > > > > > > I don't have the real hardware. In order to reproduce the bug, I simulate > > > the IPWireless PCMCIA card in the qemu by allocating and configuring the > > > necessary resources(I/O ports, memory regions, interrupts and so on) to > > > correspond with the hardware expected by the driver in the initialization > > > code of the virtual device. > > > > I wonder if this device even is still around, given that pcmcia is all > > but dead for a very long time. > > > > > > > One race condition scenario is as follows: > > > > > > > > > > CPU 0 (cleanup) | CPU 1 (interrupt) > > > > > ipwireless_hardware_free() | ipwireless_interrupt() > > > > > ipwireless_stop_interrupts()| ipwireless_handle_v1_interrupt() > > > > > do_close_hardware() | tasklet_schedule() > > > > > synchronize_irq() | > > > > > kfree(hw) //FREE | ipwireless_do_tasklet() //handler > > > > > | hw = from_tasklet() //USE > > > > > | hw-> //USE > > > > > > > > > > Fix this by ensuring hw->tasklet is properly canceled before ipw_hardware > > > > > is released. Add tasklet_kill() in ipwireless_stop_interrupts() to > > > > > synchronize with any pending or running tasklet. Since do_close_hardware() > > > > > could prevent further interrupts, place tasklet_kill() after it to avoid > > > > > the tasklet being rescheduled by ipwireless_interrupt(). > > > > > > > > How was this issue found and tested? > > > > > > The issue was found by static analysis. I test it through the following steps: > > > 1. Simulating the IPWireless PCMCIA device in the qemu and enable it to trigger interrupts. > > > 2. Controlling the removal and attachment of device via sysfs. > > > > So this is with the bind/unbind logic, or some other way? If you are > > unloading the driver, that is something that only root can do, and this > > is a debugging facility, not a "real" way to control drivers and devices > > (yes, the virt drivers abuse this to no end, every time I see this I > > laugh...) > > When the PCMCIA device is attached, we can operate the file > /sys/bus/pcmcia/devices/.../remove to detach the device. 'remove' should be removing the driver from the device, something that is only allowed by root and is not a normal operation at all. race conditions there are "at your own risk" for all drivers as it's pretty much the same as unloading the module, it is there for developer ease only. > > > 3. Triggering interrupts by writing data to device registers via /dev/mem memory mapping > > > in userspace. > > > > Interrupts would not happen if the device is removed. Or is this only > > if the driver is unbound? > > The interrupts should be triggered before the deivce is removed. But when the device is physically removed from the system, no more interrupts will happen. thanks, greg k-h
On Sun, 8 Feb 2026 15:34:49 +0100 Greg KH wrote: > > > > > > When IPWireless PCMCIA card is being detached, the ipw_hardware is > > > > > > deallocated in ipwireless_hardware_free(). However, the hw->tasklet may > > > > > > still be running or pending, leading to use-after-free bugs when the > > > > > > already freed ipw_hardware is accessed again in ipwireless_do_tasklet(). > > > > > > > > > > Nice, do you have this hardware to test this with? > > > > > > > > I don't have the real hardware. In order to reproduce the bug, I simulate > > > > the IPWireless PCMCIA card in the qemu by allocating and configuring the > > > > necessary resources(I/O ports, memory regions, interrupts and so on) to > > > > correspond with the hardware expected by the driver in the initialization > > > > code of the virtual device. > > > > > > I wonder if this device even is still around, given that pcmcia is all > > > but dead for a very long time. > > > > > > > > > One race condition scenario is as follows: > > > > > > > > > > > > CPU 0 (cleanup) | CPU 1 (interrupt) > > > > > > ipwireless_hardware_free() | ipwireless_interrupt() > > > > > > ipwireless_stop_interrupts()| ipwireless_handle_v1_interrupt() > > > > > > do_close_hardware() | tasklet_schedule() > > > > > > synchronize_irq() | > > > > > > kfree(hw) //FREE | ipwireless_do_tasklet() //handler > > > > > > | hw = from_tasklet() //USE > > > > > > | hw-> //USE > > > > > > > > > > > > Fix this by ensuring hw->tasklet is properly canceled before ipw_hardware > > > > > > is released. Add tasklet_kill() in ipwireless_stop_interrupts() to > > > > > > synchronize with any pending or running tasklet. Since do_close_hardware() > > > > > > could prevent further interrupts, place tasklet_kill() after it to avoid > > > > > > the tasklet being rescheduled by ipwireless_interrupt(). > > > > > > > > > > How was this issue found and tested? > > > > > > > > The issue was found by static analysis. I test it through the following steps: > > > > 1. Simulating the IPWireless PCMCIA device in the qemu and enable it to trigger interrupts. > > > > 2. Controlling the removal and attachment of device via sysfs. > > > > > > So this is with the bind/unbind logic, or some other way? If you are > > > unloading the driver, that is something that only root can do, and this > > > is a debugging facility, not a "real" way to control drivers and devices > > > (yes, the virt drivers abuse this to no end, every time I see this I > > > laugh...) > > > > When the PCMCIA device is attached, we can operate the file > > /sys/bus/pcmcia/devices/.../remove to detach the device. > > 'remove' should be removing the driver from the device, something that > is only allowed by root and is not a normal operation at all. race > conditions there are "at your own risk" for all drivers as it's pretty > much the same as unloading the module, it is there for developer ease > only. I did this only to verify the existence of the bug. In real word scenarios, the device removal code can be triggered by removing the real pcmcia hardware. > > > > 3. Triggering interrupts by writing data to device registers via /dev/mem memory mapping > > > > in userspace. > > > > > > Interrupts would not happen if the device is removed. Or is this only > > > if the driver is unbound? > > > > The interrupts should be triggered before the deivce is removed. > > But when the device is physically removed from the system, no more > interrupts will happen. I think the tasklet is a deferred mechanism. Although interrupts cannot happen after device is removed, the tasklet handler may still be executing or pending. This is why tasklet_kill() needs to be added. Best regards, Duoming Zhou
On Sun, Feb 08, 2026 at 10:53:26PM +0800, duoming@zju.edu.cn wrote: > On Sun, 8 Feb 2026 15:34:49 +0100 Greg KH wrote: > > > > > > > When IPWireless PCMCIA card is being detached, the ipw_hardware is > > > > > > > deallocated in ipwireless_hardware_free(). However, the hw->tasklet may > > > > > > > still be running or pending, leading to use-after-free bugs when the > > > > > > > already freed ipw_hardware is accessed again in ipwireless_do_tasklet(). > > > > > > > > > > > > Nice, do you have this hardware to test this with? > > > > > > > > > > I don't have the real hardware. In order to reproduce the bug, I simulate > > > > > the IPWireless PCMCIA card in the qemu by allocating and configuring the > > > > > necessary resources(I/O ports, memory regions, interrupts and so on) to > > > > > correspond with the hardware expected by the driver in the initialization > > > > > code of the virtual device. > > > > > > > > I wonder if this device even is still around, given that pcmcia is all > > > > but dead for a very long time. > > > > > > > > > > > One race condition scenario is as follows: > > > > > > > > > > > > > > CPU 0 (cleanup) | CPU 1 (interrupt) > > > > > > > ipwireless_hardware_free() | ipwireless_interrupt() > > > > > > > ipwireless_stop_interrupts()| ipwireless_handle_v1_interrupt() > > > > > > > do_close_hardware() | tasklet_schedule() > > > > > > > synchronize_irq() | > > > > > > > kfree(hw) //FREE | ipwireless_do_tasklet() //handler > > > > > > > | hw = from_tasklet() //USE > > > > > > > | hw-> //USE > > > > > > > > > > > > > > Fix this by ensuring hw->tasklet is properly canceled before ipw_hardware > > > > > > > is released. Add tasklet_kill() in ipwireless_stop_interrupts() to > > > > > > > synchronize with any pending or running tasklet. Since do_close_hardware() > > > > > > > could prevent further interrupts, place tasklet_kill() after it to avoid > > > > > > > the tasklet being rescheduled by ipwireless_interrupt(). > > > > > > > > > > > > How was this issue found and tested? > > > > > > > > > > The issue was found by static analysis. I test it through the following steps: > > > > > 1. Simulating the IPWireless PCMCIA device in the qemu and enable it to trigger interrupts. > > > > > 2. Controlling the removal and attachment of device via sysfs. > > > > > > > > So this is with the bind/unbind logic, or some other way? If you are > > > > unloading the driver, that is something that only root can do, and this > > > > is a debugging facility, not a "real" way to control drivers and devices > > > > (yes, the virt drivers abuse this to no end, every time I see this I > > > > laugh...) > > > > > > When the PCMCIA device is attached, we can operate the file > > > /sys/bus/pcmcia/devices/.../remove to detach the device. > > > > 'remove' should be removing the driver from the device, something that > > is only allowed by root and is not a normal operation at all. race > > conditions there are "at your own risk" for all drivers as it's pretty > > much the same as unloading the module, it is there for developer ease > > only. > > I did this only to verify the existence of the bug. In real word scenarios, > the device removal code can be triggered by removing the real pcmcia hardware. How? Doesn't the interrupt not happen, and the device get cleaned up by the bus when it is noticed and then removed? ipwireless_hardware_free() should not be called if ipwireless_interrupt() is ever happening in that case just by virtue of the fact that the interrupt will not be there. > > > > > 3. Triggering interrupts by writing data to device registers via /dev/mem memory mapping > > > > > in userspace. > > > > > > > > Interrupts would not happen if the device is removed. Or is this only > > > > if the driver is unbound? > > > > > > The interrupts should be triggered before the deivce is removed. > > > > But when the device is physically removed from the system, no more > > interrupts will happen. > > I think the tasklet is a deferred mechanism. Although interrupts cannot > happen after device is removed, the tasklet handler may still be executing > or pending. This is why tasklet_kill() needs to be added. Ok, there might be a small race, but given that no one has ever seen this with real hardware (and you need physical access for it), it's pretty small. But again, I'll let the maintainers here decide if it should be accepted or not. thanks, greg k-h
© 2016 - 2026 Red Hat, Inc.