All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: wyibo6109@gmail.com, linux-input@vger.kernel.org
Cc: devicetree@vger.kernel.org, robh@kernel.org, conor+dt@kernel.org,
	dmitry.torokhov@gmail.com
Subject: Re: [PATCH v6 2/2] Input: add OnePlus/OPPO Pad pogo-pin keyboard serdev driver
Date: Fri, 16 Oct 2026 16:49:58 +0000	[thread overview]
Message-ID: <sashiko-outbox-174666@kernel.org> (raw)
In-Reply-To: <20261016163859.1070138-3-wyibo6109@gmail.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] input: use-after-free in work queues due to lockless removal races
- [Medium] input: silent truncation of maximum-length responses in kb->resp
- [Medium] input: oneplus-pogo: data race on kb->resp buffer in pogo_xfer
- [Medium] input: oneplus-pogo-kbd: Data race on kb->connected state variables
--

commit 55459b7da977a6dfb82c08f24907cc60a6e32cd0
Author: Yibo Wang <wyibo6109@gmail.com>

Input: add OnePlus/OPPO Pad pogo-pin keyboard serdev driver

Clean-room serdev driver for the Tinno keyboard MCU used by the OnePlus
Pad 3 Pro / Pad 4 and OPPO Pad 5 Pro smart keyboards, replacing the
vendor platform driver that depends on a downstream msm_geni_serial
callback and a user-space held tty fd.

> diff --git a/drivers/input/keyboard/oneplus-pogo-kbd.c b/drivers/input/keyboard/oneplus-pogo-kbd.c
> new file mode 100644
> index 0000000000000..afe87d9616174
> --- /dev/null
> +++ b/drivers/input/keyboard/oneplus-pogo-kbd.c
[ ... ]
> +static int pogo_xfer(struct pogo_kbd *kb, u8 cmd, const u8 *payload, u8 len,
> +		     u8 *resp, unsigned int resp_size)
> +{
[ ... ]
> +		if (!resp) {
> +			ret = 0;
> +			break;
> +		}
> +		if (wait_for_completion_timeout(&kb->resp_done,
> +				msecs_to_jiffies(POGO_RESP_TIMEOUT_MS))) {
> +			unsigned int n = min(kb->resp_len, resp_size);
> +
> +			/* stop the RX path before copying the reply out */
> +			WRITE_ONCE(kb->tx_pending, false);
> +			memcpy(resp, kb->resp, n);

[Severity: Medium]
Can this lead to a data race on the kb->resp buffer?

If a delayed reply from a previous attempt completes kb->resp_done early,
pogo_xfer() wakes up, sets tx_pending to false, and starts reading
kb->resp. Concurrently, the RX thread in pogo_handle_frame() may receive
the reply for the current attempt, evaluate tx_pending as true, and
overwrite kb->resp via memcpy() while pogo_xfer() is reading it.

This torn read could cause spurious -EPROTO initialization failures.

[ ... ]
> +static void pogo_handle_sync(struct pogo_kbd *kb, const u8 *p, unsigned int len)
> +{
> +	bool replug = false;
[ ... ]
> +	if (!kb->connected || replug) {
> +		kb->connected = true;
> +		kb->hb_seen = false;
> +		kb->retry_ms = POGO_RETRY_MIN_MS;
> +		if (!READ_ONCE(kb->quitting))
> +			schedule_work(&kb->plug_work);
> +	}

[Severity: Medium]
Is it safe to modify kb->connected here without holding kb->state_lock?

In pogo_power_off(), kb->connected and kb->powered are cleared under
kb->state_lock. If pogo_power_off() races with this RX path, this lockless
modification can leave the driver permanently desynchronized.

If pogo_power_off() sets powered = false and connected = false, and a delayed
UART sync frame is processed here concurrently, this path will locklessly set
connected = true. The subsequent schedule_work(&kb->plug_work) will exit
early because powered is false, leaving connected = true while powered is false.

[ ... ]
> +static void pogo_handle_frame(struct pogo_kbd *kb, const u8 *f, unsigned int n)
> +{
[ ... ]
> +		if (READ_ONCE(kb->tx_pending) && cmd == kb->tx_cmd + 1) {
> +			/* reply layout for callers: cmd, len, payload */
> +			kb->resp_len = min_t(unsigned int, len + 2, sizeof(kb->resp));
> +			memcpy(kb->resp, &f[3], kb->resp_len);

[Severity: Medium]
Does this truncate maximum-length responses?

The kb->resp buffer is sized to OWB_MAX_PAYLOAD (128 bytes). By adding 2 to
len (for cmd and len), a maximum payload of 128 bytes yields len + 2 = 130.
The min_t() will cap this at sizeof(kb->resp) (128), which silently truncates
the last 2 bytes of the payload.

[ ... ]
> +static void pogo_remove(struct serdev_device *serdev)
> +{
> +	struct pogo_kbd *kb = serdev_device_get_drvdata(serdev);
> +
> +	/*
> +	 * Stop the world first: with quitting set, the RX path and the
> +	 * works stop queueing each other, so the cancels below cannot be
> +	 * undone again.  The wake IRQ is disabled (and any in-flight ISR
> +	 * finished) before detect_work is cancelled.
> +	 */
> +	WRITE_ONCE(kb->quitting, true);
> +	pogo_wake_irq_enable(kb, false);
> +	cancel_delayed_work_sync(&kb->detect_work);
> +	cancel_delayed_work_sync(&kb->hb_work);
> +	cancel_work_sync(&kb->plug_work);

[Severity: High]
Can this teardown sequence lead to a use-after-free of the work queues?

If pogo_wake_isr() is currently executing and has already called
disable_irq_nosync(), kb->wake_irq_enabled will be false.

When pogo_remove() calls pogo_wake_irq_enable(kb, false), it will hit
the early return:

    if (enable == kb->wake_irq_enabled)
        return;

This bypasses the disable_irq() synchronization barrier. Because
pogo_wake_isr() checks kb->quitting without a lock, the ISR could observe
quitting == false before pogo_remove() sets it, but queue the delayed
work after pogo_remove() has already completed the cancellations, resulting
in a use-after-free.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261016163859.1070138-1-wyibo6109@gmail.com?part=2

      reply	other threads:[~2026-10-16 16:49 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-16 16:38 [PATCH v6 0/2] Input: add support for OnePlus/OPPO Pad pogo-pin keyboards wyibo6109
2026-10-16 16:38 ` [PATCH v6 1/2] dt-bindings: input: add OnePlus pogo-pin keyboard wyibo6109
2026-10-16 16:45   ` sashiko-bot
2026-10-16 16:38 ` [PATCH v6 2/2] Input: add OnePlus/OPPO Pad pogo-pin keyboard serdev driver wyibo6109
2026-10-16 16:49   ` sashiko-bot [this message]

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=sashiko-outbox-174666@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=wyibo6109@gmail.com \
    /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.