Linux Serial subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Markus Probst" <markus.probst@posteo.de>
Cc: ojeda@kernel.org, linux-serial@vger.kernel.org, gary@garyguo.net
Subject: Re: [PATCH v3 1/4] tty: serdev: Add mutex lock
Date: Wed, 30 Sep 2026 17:50:26 +0000	[thread overview]
Message-ID: <20260930175026.C983C1F000FF@smtp.kernel.org> (raw)
In-Reply-To: <20260930-rust_serdev_probe_refactor-v3-1-d1b6e77135e0@posteo.de>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] tty: serdev: ABBA deadlock between serport->lock and TTY workqueue
- [Medium] pm: serdev: cancelled async pm_runtime_put leaks power state
--

commit f8d69b3702358f814be05474626c0ebf27b17939
Author: Markus Probst <markus.probst@posteo.de>

    tty: serdev: Add mutex lock
    
    Besides more predictable behaviour, this allows for several hardened
    behaviour changes:
    
    Return -EALREADY in serdev_device_open if the device is already open
    instead of causing undefined behaviour.

> diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
> index 7500efcdfc218..77e8e1d4d2a66 100644
> --- a/drivers/tty/serdev/core.c
> +++ b/drivers/tty/serdev/core.c
> @@ -142,6 +142,11 @@ void serdev_device_remove(struct serdev_device *serdev)
>  	struct serdev_controller *ctrl = serdev->ctrl;
>  
>  	device_unregister(&serdev->dev);
> +
> +	/* Warn if driver did not close the serial device. */
> +	if (ctrl->ops->close && WARN_ON(ctrl->ops->close(ctrl)))
> +		pm_runtime_put(&ctrl->dev);
> +

[Severity: Medium]
When a driver fails to close the device, this calls an asynchronous
pm_runtime_put(). If this removal occurs during controller teardown,
serdev_controller_remove() will immediately call
pm_runtime_disable(&ctrl->dev).

Disabling runtime PM explicitly cancels any pending asynchronous idle/suspend
work. Does this leave the device permanently powered on? Would a synchronous
pm_runtime_put_sync() prevent this leak?

>  	ctrl->serdev = NULL;
>  }
>  EXPORT_SYMBOL_GPL(serdev_device_remove);
[ ... ]
> diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
> index bab1b143b8a68..c11908f5e1ce3 100644
> --- a/drivers/tty/serdev/serdev-ttyport.c
> +++ b/drivers/tty/serdev/serdev-ttyport.c
[ ... ]
> @@ -29,6 +30,8 @@ static size_t ttyport_receive_buf(struct tty_port *port, const u8 *cp,
>  	struct serport *serport = serdev_controller_get_drvdata(ctrl);
>  	size_t ret;
>  
> +	guard(mutex)(&serport->lock);
> +

[Severity: High]
This is a pre-existing issue, but does attempting to acquire the lock here
trigger a use-after-free if the device is actively receiving data when
unregistered?

When serdev_tty_port_unregister() drops the reference to ctrl, it frees both
ctrl and serport without synchronously cancelling the flush_to_ldisc TTY
workqueue. If that workqueue is executing concurrently, it will call
ttyport_receive_buf() and try to acquire serport->lock on the freed memory.

Also, does this lock attempt cause an ABBA deadlock?

ttyport_close() acquires serport->lock, then calls tty_release_struct().
tty_release_struct() synchronously waits for the flush_to_ldisc workqueue
to drain via cancel_work_sync(). If that workqueue is running concurrently,
it calls ttyport_receive_buf() and blocks here trying to acquire the same
serport->lock, deadlocking the system.

>  	if (!test_bit(SERPORT_ACTIVE, &serport->flags))
>  		return 0;
>  
[ ... ]
> @@ -130,23 +142,30 @@ static int ttyport_open(struct serdev_controller *ctrl)
>  
>  	set_bit(SERPORT_ACTIVE, &serport->flags);
>  
> +	mutex_unlock(&serport->lock);
> +
>  	return 0;
>  
>  err_close:
>  	tty->ops->close(tty, NULL);
> -err_unlock:
> +err_tty_unlock:
>  	tty_unlock(tty);
>  	tty_release_struct(tty, serport->tty_idx);
> +err_flags_unlock:
> +	mutex_unlock(&serport->lock);

[Severity: High]
Does this error path trigger the same deadlock described above? It calls
tty_release_struct() while holding serport->lock.

>  
>  	return ret;
>  }
>  
> -static void ttyport_close(struct serdev_controller *ctrl)
> +static bool ttyport_close(struct serdev_controller *ctrl)
>  {
>  	struct serport *serport = serdev_controller_get_drvdata(ctrl);
>  	struct tty_struct *tty = serport->tty;
>  
> -	clear_bit(SERPORT_ACTIVE, &serport->flags);
> +	guard(mutex)(&serport->lock);
> +

[Severity: High]
This is a pre-existing issue, but does reading serport->tty into a local
variable before acquiring the lock bypass the lock's protection?

If ttyport_close() runs concurrently with ttyport_open(), it may read a stale
or NULL tty pointer before acquiring the lock. After ttyport_open() finishes
and releases the lock, ttyport_close() acquires it, passes the
SERPORT_ACTIVE check, and then dereferences the stale tty pointer.

> +	if (!__test_and_clear_bit(SERPORT_ACTIVE, &serport->flags))
> +		return false;
>  
>  	tty_lock(tty);
>  	if (tty->ops->close)
> @@ -154,6 +173,8 @@ static void ttyport_close(struct serdev_controller *ctrl)
>  	tty_unlock(tty);
>  
>  	tty_release_struct(tty, serport->tty_idx);
> +
> +	return true;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260930-rust_serdev_probe_refactor-v3-0-d1b6e77135e0@posteo.de?part=1

  reply	other threads:[~2026-09-30 17:50 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-30 17:32 [PATCH v3 0/4] rust: serdev: Refactor Markus Probst
2026-09-30 17:32 ` [PATCH v3 1/4] tty: serdev: Add mutex lock Markus Probst
2026-09-30 17:50   ` sashiko-bot [this message]
2026-09-30 17:32 ` [PATCH v3 2/4] tty: serdev: Export functions to pause receive_buf callback calls Markus Probst
2026-09-30 17:41   ` Markus Probst
2026-09-30 17:43   ` sashiko-bot
2026-09-30 17:48     ` Markus Probst
2026-09-30 17:32 ` [PATCH v3 3/4] rust: serdev: Replace `active` mutex with receive pause Markus Probst
2026-09-30 17:42   ` sashiko-bot
2026-09-30 17:32 ` [PATCH v3 4/4] rust: serdev: Simplify callbacks Markus Probst
2026-09-30 17:53   ` 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=20260930175026.C983C1F000FF@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=gary@garyguo.net \
    --cc=linux-serial@vger.kernel.org \
    --cc=markus.probst@posteo.de \
    --cc=ojeda@kernel.org \
    --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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox