All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Michael Zaidman" <michael.zaidman@gmail.com>
Cc: linux-input@vger.kernel.org, linux-serial@vger.kernel.org,
	linux-gpio@vger.kernel.org, dmitry.torokhov@gmail.com
Subject: Re: [PATCH v3 02/16] HID: ft260: uart: fix the serial driver
Date: Sat, 14 Nov 2026 23:37:15 +0000	[thread overview]
Message-ID: <sashiko-outbox-215115@kernel.org> (raw)
In-Reply-To: <20261114230800.1344401-3-michael.zaidman@gmail.com>

Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [High] HID: ft260: undrained xmit_fifo on TX error or remove hangs tty
  drain and vhangup
- [High] HID: ft260: race between tty open/reopen and ft260_remove()
  causes hdev UAF
- [High] HID: ft260: premature list_del() in ft260_uart_port_remove()
  causes index reuse
- [Medium] HID: ft260: missing UART_COUNT_MAX check in
  ft260_uart_add_port()
--

commit 4759b9dbc3d5fe28cb8dc19ab0c19093471e2b92
Author: Michael Zaidman <michael.zaidman@gmail.com>

HID: ft260: uart: fix the serial driver

This commit fixes buffer sharing, power-save wakeup handling, input report
validation, TX work queueing, and port lifecycle management in the FT260
UART driver.

> diff --git a/drivers/hid/hid-ft260.c b/drivers/hid/hid-ft260.c
> index 982e2ca7a2de6..8d72a22d7a079 100644
> --- a/drivers/hid/hid-ft260.c
> +++ b/drivers/hid/hid-ft260.c

[ ... ]

> @@ -1123,8 +1161,8 @@ static int ft260_uart_add_port(struct ft260_device *port)
>  	int index = 0, ret = 0;
>  	struct ft260_device *dev;
>  
> -	spin_lock_init(&port->write_lock);
> -	if (kfifo_alloc(&port->xmit_fifo, FIFO_SIZE, GFP_KERNEL))
> +	spin_lock_init(&port->xmit_fifo_lock);
> +	if (kfifo_alloc(&port->xmit_fifo, XMIT_FIFO_SIZE, GFP_KERNEL))
>  		return -ENOMEM;
>  
>  	mutex_lock(&ft260_uart_list_lock);
> @@ -1135,7 +1173,8 @@ static int ft260_uart_add_port(struct ft260_device *port)
>  	}
>  
>  	port->index = index;
> -	list_add_tail(&port->device_list, &ft260_uart_device_list);
> +	/* Before the first gap, or at the end: the list stays sorted */
> +	list_add_tail(&port->device_list, &dev->device_list);
>  	mutex_unlock(&ft260_uart_list_lock);
>  
>  	return ret;

[Severity: Medium]
Can ft260_uart_add_port() assign port->index >= UART_COUNT_MAX when more
than four FT260 UART interfaces are probed?

ft260_tty_driver is allocated with UART_COUNT_MAX (4) ports, but
ft260_uart_add_port() never checks index against UART_COUNT_MAX before
adding the port and returning 0.  ft260_uart_probe() then passes
dev->index to tty_port_register_device_attr() -> tty_port_link_device(),
which hits WARN_ON(index >= driver->num).

> @@ -1148,7 +1187,12 @@ static void ft260_uart_port_put(struct ft260_device *port)
>  
>  static void ft260_uart_port_remove(struct ft260_device *port)
>  {
> -	timer_delete_sync(&port->wakeup_timer);
> +	/*
> +	 * Shut down, not delete: a tty call before the hangup below
> +	 * must not re-arm the timer.
> +	 */
> +	timer_shutdown_sync(&port->wakeup_timer);
> +	cancel_work_sync(&port->wakeup_work);
>  
>  	mutex_lock(&ft260_uart_list_lock);
>  	list_del(&port->device_list);
>  	mutex_unlock(&ft260_uart_list_lock);

[Severity: High]
Does removing port->device_list in ft260_uart_port_remove() allow a newly
probed FT260 to reuse port->index while an open file descriptor still
keeps the old tty_struct in ft260_tty_driver->ttys[index]?

Until the last file descriptor is closed and release_tty() runs,
ft260_tty_driver->ttys[index] still points to the old hung-up tty_struct.
If the device is re-plugged before that close, ft260_uart_add_port() sees
the index as free and assigns it to the new device.  Opening the new
/dev/ttyFTx then finds the old tty_struct via tty_driver_lookup_tty() and
calls tty_reopen() -> ft260_uart_open() -> ft260_uart_port_activate() on
the old removed ft260_device, dereferencing its stopped and freed
port->hdev.

Moving list_del(&port->device_list) to ft260_uart_port_destroy() would keep
the index reserved until ft260_tty_driver->ttys[index] has been cleared.

[ ... ]

> @@ -1207,61 +1254,51 @@ static int ft260_uart_transmit_chars(struct ft260_device *port)
>  	struct kfifo *xmit = &port->xmit_fifo;
>  	struct tty_struct *tty;
>  	struct ft260_uart_write_request_report *rep;
> -	int len, data_len, ret = 0;
> +	int len, ret = 0;
>  
>  	tty = tty_port_tty_get(&port->port);
> +	if (!tty)
> +		return -EINVAL;
>  
> -	data_len = kfifo_len(xmit);
> -	if (!tty || !data_len) {
> -		ret = -EINVAL;
> -		goto tty_out;
> -	}
> -
> -	rep = (struct ft260_uart_write_request_report *)port->write_buf;
> -
> -	do {
> -		len = min(data_len, FT260_WR_DATA_MAX);
> +	rep = (struct ft260_uart_write_request_report *)port->uart_wr_buf;
>  
> +	while ((len = kfifo_out_spinlocked(xmit, rep->data,
> +					   FT260_WR_UART_DATA_MAX,
> +					   &port->xmit_fifo_lock)) > 0) {
>  		rep->report = FT260_UART_DATA_REPORT_ID(len);
>  		rep->length = len;
>  
> -		len = kfifo_out_locked(xmit, rep->data, len, &port->write_lock);
> -
> -		ret = ft260_hid_output_report(hdev, (u8 *)rep, len + sizeof(*rep));
> -		if (ret < 0) {
> -			hid_err(hdev, "Failed to start transfer, ret %d\n", ret);
> -			goto tty_out;
> -		}
> +		ret = ft260_hid_output_report(hdev, (u8 *)rep, len + 2);
> +		if (ret < 0)
> +			break;
>  
> -		data_len -= len;
>  		port->icount.tx += len;
> -	} while (data_len > 0);
> -
> -	len = kfifo_len(xmit);
> -	if ((FIFO_SIZE - len) > TTY_WAKEUP_WATERMARK)
> -		tty_wakeup(tty);
> -
> -	ret = 0;
> +	}
>  
> -tty_out:
> +	tty_wakeup(tty);
>  	tty_kref_put(tty);
> -	return ret;
> +	return ret < 0 ? ret : 0;
>  }

[Severity: High]
If ft260_hid_output_report() fails when more than 60 bytes are queued in
xmit_fifo, does breaking out of this loop leave xmit_fifo non-empty with
nothing to drain or flush it?

ft260_uart_chars_in_buffer() then continues returning a non-zero
kfifo_len(&port->xmit_fifo) forever, and ft260_uart_ops has no
.flush_buffer callback.  As a result, tcdrain() and
set_termios(TCSADRAIN/TCSAFLUSH) sleep indefinitely in
wait_event_interruptible(tty->write_wait, !tty_chars_in_buffer(tty)), and
if the device is unplugged while set_termios() is waiting with an ldisc
reference held, ft260_uart_port_remove() -> tty_port_tty_vhangup() ->
tty_ldisc_hangup() blocks forever in tty_ldisc_lock() before reaching
kfifo_free(&port->xmit_fifo):

CPU0                                  CPU1
tty_ioctl() [TCSETSW]
  ld = tty_ldisc_ref_wait(tty)
  ...
  set_termios()
    wait_event_interruptible(
      tty->write_wait,
      !tty_chars_in_buffer(tty))
                                      ft260_uart_port_remove()
                                        tty_port_tty_vhangup(&port->port)
                                          ...
                                          tty_ldisc_hangup()
                                            tty_driver_flush_buffer(tty)
                                            wake_up_interruptible_poll(
                                              &tty->write_wait, EPOLLOUT)
    /* xmit_fifo still non-empty,
       goes back to sleep */
                                            tty_ldisc_lock(tty,
                                              MAX_SCHEDULE_TIMEOUT)
                                              <-- hangs forever

[ ... ]

> @@ -1503,53 +1548,69 @@ static void ft260_uart_start_wakeup(struct timer_list *t)

[ ... ]

> -static int ft260_uart_activate(struct tty_port *tport, struct tty_struct *tty)
> +static int ft260_uart_port_activate(struct tty_port *tport, struct tty_struct *tty)
>  {
> +	int ret;
> +	int baudrate;
> +	struct ft260_get_uart_settings_report cfg;
> +	struct ft260_device *port = container_of(tport, struct ft260_device, port);
> +
>  	set_bit(TTY_IO_ERROR, &tty->flags);
>  
> -	spin_lock(&port->write_lock);
> +	spin_lock(&port->xmit_fifo_lock);
>  	kfifo_reset(&port->xmit_fifo);
> -	spin_unlock(&port->write_lock);
> +	spin_unlock(&port->xmit_fifo_lock);
>  
> -	ft260_uart_change_speed(port, &tty->termios, NULL);
>  	clear_bit(TTY_IO_ERROR, &tty->flags);
>  
> -	if (port->reschedule_work) {
> +	/*
> +	 * The port setting may remain intact after session termination.
> +	 * Then, when reopening the port without configuring the port
> +	 * setting, we need to retrieve the baud rate from the device to
> +	 * reactivate the wakeup workaround if needed.
> +	 */
> +	ret = ft260_get_uart_settings(port->hdev, &cfg);

[Severity: High]
Can ft260_uart_port_activate() race with ft260_remove() and dereference
port->hdev after it has been stopped and freed?

CPU0                                  CPU1
tty_open()
  tty_open_by_driver()
    tty_init_dev()
      ft260_uart_install()
        ft260_uart_port_get()
    mutex_unlock(&tty_mutex)
                                      ft260_remove()
                                        ...
                                        ft260_uart_port_remove()
                                          list_del(&port->device_list)
                                          tty_port_tty_vhangup(&port->port)
                                            /* port.tty is still NULL */
                                          kfifo_free(&port->xmit_fifo)
                                        hid_hw_close(hdev)
                                        hid_hw_stop(hdev)
                                        /* hdev freed by HID/USB core */
  ft260_uart_open()
    tty_port_open()
      ft260_uart_port_activate()
        ft260_get_uart_settings(port->hdev, ...)
          <-- use-after-free on port->hdev

The same race also happens on reopen when a second opener waits on
tty_lock_interruptible(tty) in tty_open_by_driver() while
tty_port_tty_vhangup() runs: vhangup clears the tty_port's initialized
flag, and tty_reopen() then proceeds to ft260_uart_open() ->
tty_port_open() -> ft260_uart_port_activate() after ft260_remove() has
finished.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261114230800.1344401-1-michael.zaidman@gmail.com?part=2

  reply	other threads:[~2026-11-14 23:37 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-14 23:07 [PATCH v3 00/16] HID: ft260: add UART and GPIO support, plus I2C fixes Michael Zaidman
2026-11-14 23:07 ` [PATCH v3 01/16] HID: ft260: add serial driver Michael Zaidman
2026-11-14 23:39   ` sashiko-bot
2026-11-14 23:07 ` [PATCH v3 02/16] HID: ft260: uart: fix the " Michael Zaidman
2026-11-14 23:37   ` sashiko-bot [this message]
2026-11-14 23:07 ` [PATCH v3 03/16] HID: ft260: add GPIO support Michael Zaidman
2026-11-14 23:25   ` sashiko-bot
2026-11-14 23:07 ` [PATCH v3 04/16] HID: ft260: i2c: reduce driver module loading time Michael Zaidman
2026-11-14 23:12   ` sashiko-bot
2026-11-14 23:07 ` [PATCH v3 05/16] HID: ft260: fix sysfs store failure notice for big numbers Michael Zaidman
2026-11-14 23:17   ` sashiko-bot
2026-11-14 23:07 ` [PATCH v3 06/16] HID: ft260: i2c: reduce bus-error message severity Michael Zaidman
2026-11-14 23:12   ` sashiko-bot
2026-11-14 23:07 ` [PATCH v3 07/16] HID: ft260: uart: enable flow control Michael Zaidman
2026-11-14 23:19   ` sashiko-bot
2026-11-14 23:07 ` [PATCH v3 08/16] HID: ft260: uart: add modem pins control via ioctl Michael Zaidman
2026-11-14 23:20   ` sashiko-bot
2026-11-14 23:07 ` [PATCH v3 09/16] HID: ft260: group sysfs attrs per HID interface Michael Zaidman
2026-11-14 23:14   ` sashiko-bot
2026-11-14 23:07 ` [PATCH v3 10/16] HID: ft260: i2c: fix large write transaction failure Michael Zaidman
2026-11-14 23:12   ` sashiko-bot
2026-11-14 23:07 ` [PATCH v3 11/16] HID: ft260: workaround for TN_189 errata endpoint STALL after enumeration Michael Zaidman
2026-11-14 23:27   ` sashiko-bot
2026-11-14 23:07 ` [PATCH v3 12/16] HID: ft260: i2c: abort in-flight transfers with STOP before reset Michael Zaidman
2026-11-14 23:16   ` sashiko-bot
2026-11-14 23:07 ` [PATCH v3 13/16] HID: ft260: i2c: reject an I2C clock outside the supported range Michael Zaidman
2026-11-14 23:15   ` sashiko-bot
2026-11-14 23:07 ` [PATCH v3 14/16] HID: ft260: i2c: fix i2c_reset sysfs store return value Michael Zaidman
2026-11-14 23:15   ` sashiko-bot
2026-11-14 23:07 ` [PATCH v3 15/16] HID: ft260: i2c: recover a bus held by a slave Michael Zaidman
2026-11-14 23:25   ` sashiko-bot
2026-11-14 23:08 ` [PATCH v3 16/16] HID: ft260: use lock guards Michael Zaidman
2026-11-14 23:21   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-215115@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-gpio@vger.kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=michael.zaidman@gmail.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.