mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2] virtio_console: defer HVC polling out of the virtqueue callback
@ 2026-11-05 11:44 Yaohan Li
  0 siblings, 0 replies; only message in thread
From: Yaohan Li @ 2026-11-05 11:44 UTC (permalink / raw)
  To: amit, arnd, gregkh; +Cc: rusty, virtualization, linux-kernel

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


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-11-05 11:45 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-05 11:44 [PATCH v2] virtio_console: defer HVC polling out of the virtqueue callback Yaohan Li

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®