mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Yaohan Li <shunsheng1218@gmail.com>
To: amit@kernel.org, arnd@arndb.de, gregkh@linuxfoundation.org
Cc: rusty@rustcorp.com.au, virtualization@lists.linux.dev,
	linux-kernel@vger.kernel.org
Subject: [PATCH v2] virtio_console: defer HVC polling out of the virtqueue callback
Date: Thu,  5 Nov 2026 19:44:50 +0800	[thread overview]
Message-ID: <20261105114450.971521-1-shunsheng1218@gmail.com> (raw)

The input virtqueue callback calls hvc_poll() on port->cons.hvc
without any synchronization against HVC adapter removal.  A port
removed via a VIRTIO_CONSOLE_PORT_REMOVE control message runs
unplug_port() -> hvc_remove(), which drops the adapter's last
tty_port reference and frees it, while the callback may still be
about to dereference the freed adapter:

    BUG: KASAN: slab-use-after-free in _raw_spin_lock_irqsave
    BUG: KASAN: slab-use-after-free in __hvc_poll

Both sides hold only the port kref; nothing protects
struct hvc_struct between the callback's pointer read and its use.

Do not touch the adapter from hard IRQ context at all.  Only wake
the HVC worker with hvc_kick(): khvcd polls registered adapters
while holding hvc_structs_mutex, which also serializes
hvc_port_destruct() before it removes the adapter from the list
and frees it, so the worker can never poll a removed adapter.  The
callback now only reads the adapter pointer value (never
dereferences it) to decide whether to kick; a stale value can cause
at most a harmless extra kick.  The IS_ERR_OR_NULL() check also
avoids waking the worker for a port whose hvc_alloc() failed.

The price is one extra scheduling latency for console input
delivery.  A 64-byte echo benchmark over a virtio console port
shows no significant difference (RTT p50 0.089 ms vs 0.083 ms,
855 vs 767 msg/s).

The race and the fix were verified with an injection-assisted QEMU
setup: stalling the callback between the waitqueue wakeup and the
adapter access while a QMP device_del removes the port produces six
slab-use-after-free reports per run on the unpatched kernel and
zero reports with this change.

This issue was investigated and the fix was developed with AI
assistance (ZCode/GLM-4).

Fixes: 203baab8ba319 ("virtio: console: Introduce function to hand off data from host to readers")
Signed-off-by: Yaohan Li <shunsheng1218@gmail.com>
---

Changes since v1:
- v1 protected the in-flight hvc_poll() call with an RCU read-side
  critical section and published a NULL adapter before removal.  This
  version drops the RCU scheme entirely: the virtqueue callback no
  longer touches the HVC adapter at all and only wakes the HVC worker,
  which polls adapters under hvc_structs_mutex - the same mutex that
  serializes adapter destruction - so it can never poll a removed
  adapter, and the unplug side needs no change.
- Verified with an injection-assisted A/B run at the same stall point
  (in_intr+0x18b, between the waitqueue wakeup and the adapter
  access): the unpatched kernel reports six slab-use-after-frees per
  run, this patch reports none.  Round-trip latency and throughput on
  a console port are unchanged (p50 0.089 ms vs 0.083 ms).
- resize_console() and the control-message HVC accesses remain out of
  scope, as in v1.
v1: https://lore.kernel.org/r/179378553951.72222.12851179500568566393@gmail.com/

 drivers/char/virtio_console.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 019bcae81af5c..c5b3a6c0ff3d7 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1820,7 +1820,14 @@ static void in_intr(struct virtqueue *vq)
 
 	wake_up_interruptible(&port->waitqueue);
 
-	if (is_console_port(port) && hvc_poll(port->cons.hvc))
+	/*
+	 * Leave HVC polling to the khvcd thread: it walks its adapter list
+	 * under hvc_structs_mutex, which also serializes adapter
+	 * destruction, so it cannot poll a removed adapter.  Do not touch
+	 * the adapter here: hvc_remove() can free it concurrently during
+	 * port unplug and this callback holds no reference to it.
+	 */
+	if (!IS_ERR_OR_NULL(READ_ONCE(port->cons.hvc)))
 		hvc_kick();
 
 	kref_put(&port->kref, remove_port);

base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
-- 
2.34.1


                 reply	other threads:[~2026-11-05 11:45 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20261105114450.971521-1-shunsheng1218@gmail.com \
    --to=shunsheng1218@gmail.com \
    --cc=amit@kernel.org \
    --cc=arnd@arndb.de \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=rusty@rustcorp.com.au \
    --cc=virtualization@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

all inboxes | Powered by JetHome®