From: Sang-Hoon Choi <csh0052@gmail.com>
To: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
Cc: Sang-Hoon Choi <csh0052@gmail.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Jiri Slaby <jirislaby@kernel.org>,
linux-remoteproc@vger.kernel.org, linux-serial@vger.kernel.org,
linux-kernel@vger.kernel.org, Changyul Lee <lcy8047@gmail.com>
Subject: [PATCH] tty: rpmsg: close port lookup-to-get race
Date: Wed, 23 Sep 2026 00:17:41 +0900 [thread overview]
Message-ID: <20260923001647.1337001-1-csh0052@gmail.com> (raw)
In-Reply-To: <2026092154-anger-sensually-aae8@gregkh>
rpmsg_tty_install() obtains cport from tty_idr before taking a port
reference. rpmsg_tty_destruct_port() removes the entry under idr_lock
and frees cport. If channel removal drops the last reference between
idr_find() and tty_port_get(), the install path dereferences freed
memory.
The first-open path and channel removal can run concurrently. tty_mutex
serializes TTY initialization, but rpmsg_tty_remove() does not take that
mutex. tty_unregister_device() prevents later opens through cdev_del(),
but cdev_del() does not wait for an open which has already entered the
driver. Before rpmsg_tty_install() finishes, the port is not attached to
the new TTY, so tty_port_tty_hangup() does not close this interval.
RPMsg channel removal may be initiated asynchronously by the remote
processor or transport. In the test, the local process only needs
permission to open the TTY node; channel removal is initiated
independently.
I reproduced this with a UML kernel built with KASAN and a synthetic
RPMsg device using the real rpmsg_tty probe and remove paths. Test-only
synchronization forces removal after idr_find() and before
tty_port_get(). The opening process drops to UID 1000 and GID 1000 first.
The unpatched kernel reports:
BUG: KASAN: slab-use-after-free in rpmsg_tty_install
Read of size 4 ... by task init/23
CPU: 0 UID: 1000 PID: 23
The allocation stack ends in rpmsg_tty_probe(). The free stack is
rpmsg_tty_remove() -> tty_port_put() -> rpmsg_tty_destruct_port(), and
the invalid read is in rpmsg_tty_install().
Take idr_lock across idr_find() and tty_port_get(). If the entry is gone
or its reference count has reached zero, fail the installation with
-ENODEV. With the same forced overlap, the UID 1000 open returns ENODEV
and KASAN stays quiet.
The test uses a synthetic transport and deliberately widens the race
window. It demonstrates the lifetime bug and an unprivileged opener, but
does not show that an unprivileged user can deliberately cause channel
removal on every RPMsg platform. I am therefore reporting this as a
normal lifetime bug.
Fixes: 7c0408d80579 ("tty: add rpmsg driver")
Reported-by: Changyul Lee <lcy8047@gmail.com>
Link: https://lore.kernel.org/all/179000811428.1227592.8003229121862460039.idr-bug-84@gmail.com/
Assisted-by: LLM
Signed-off-by: Sang-Hoon Choi <csh0052@gmail.com>
---
Greg, thanks for the feedback. I went back and reproduced the race under
KASAN before preparing this patch. I can also provide the test-only
instrumentation and the complete KASAN log if useful.
drivers/tty/rpmsg_tty.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/tty/rpmsg_tty.c b/drivers/tty/rpmsg_tty.c
index c5fd6d9b3..b2765bae2 100644
--- a/drivers/tty/rpmsg_tty.c
+++ b/drivers/tty/rpmsg_tty.c
@@ -49,12 +49,19 @@ static int rpmsg_tty_cb(struct rpmsg_device *rpdev, void *data, int len, void *p
static int rpmsg_tty_install(struct tty_driver *driver, struct tty_struct *tty)
{
- struct rpmsg_tty_port *cport = idr_find(&tty_idr, tty->index);
- struct tty_port *port;
+ struct rpmsg_tty_port *cport;
+ struct tty_port *port = NULL;
- tty->driver_data = cport;
+ mutex_lock(&idr_lock);
+ cport = idr_find(&tty_idr, tty->index);
+ if (cport)
+ port = tty_port_get(&cport->port);
+ mutex_unlock(&idr_lock);
+
+ if (!port)
+ return -ENODEV;
- port = tty_port_get(&cport->port);
+ tty->driver_data = cport;
return tty_port_install(port, driver, tty);
}
--
2.43.0
next prev parent reply other threads:[~2026-09-22 15:17 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-21 16:28 [RFC] tty: rpmsg: possible lookup-to-reference lifetime race Sang-Hoon Choi
2026-09-21 18:50 ` Greg Kroah-Hartman
2026-09-22 15:17 ` Sang-Hoon Choi [this message]
2026-09-23 9:42 ` [PATCH] tty: rpmsg: close port lookup-to-get race Greg Kroah-Hartman
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=20260923001647.1337001-1-csh0052@gmail.com \
--to=csh0052@gmail.com \
--cc=arnaud.pouliquen@foss.st.com \
--cc=gregkh@linuxfoundation.org \
--cc=jirislaby@kernel.org \
--cc=lcy8047@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-remoteproc@vger.kernel.org \
--cc=linux-serial@vger.kernel.org \
/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
all inboxes | Powered by JetHome®