All of lore.kernel.org
 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 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.