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 v4 2/2] Input: add OnePlus/OPPO Pad pogo-pin keyboard serdev driver
Date: Thu, 15 Oct 2026 11:19:07 +0000	[thread overview]
Message-ID: <sashiko-outbox-172734@kernel.org> (raw)
In-Reply-To: <20261015110859.203989-3-wyibo6109@gmail.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] input: pogo: Use-After-Free in async paths due to lockless TOCTOU and broken synchronization
- [Medium] serdev: data race and buffer overwrite in pogo_xfer() due to lockless completion handling
- [Medium] serdev: unprotected link state modifications in RX thread cause software/hardware state desync
- [Low] sysfs: custom battery_level attribute bypasses power_supply subsystem UAPI
--

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

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

This commit adds a 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.
It introduces a custom protocol implementation over a half-duplex UART
to support touch, keys, backlight, and power management.

> diff --git a/drivers/input/keyboard/oneplus-pogo-kbd.c b/drivers/input/keyboard/oneplus-pogo-kbd.c
> --- a/drivers/input/keyboard/oneplus-pogo-kbd.c
> +++ b/drivers/input/keyboard/oneplus-pogo-kbd.c
[ ... ]
>  		/*
>  		 * Drop any late echo/reply of the previous attempt: with
>  		 * tx_pending cleared the RX path ignores frames until the
>  		 * transfer below re-arms, so a delayed reply cannot complete
>  		 * the fresh iteration or race the resp buffer.
>  		 */
>  		WRITE_ONCE(kb->tx_pending, false);
>  		reinit_completion(&kb->echo_done);
>  		reinit_completion(&kb->resp_done);
>  		kb->tx_cmd = cmd;
>  		WRITE_ONCE(kb->tx_pending, true);

[Severity: Medium]
Is it possible for a delayed response from the previous timeout to trigger a
spurious completion here?

When the retry happens, the same cmd is set. If the RX thread was preempted
just before copying the payload, could it wake up and call
complete(&kb->resp_done) using the stale payload for this new iteration?
Explicit lock synchronization between the timeout path and the RX thread might
be needed.

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

[Severity: Medium]
Does this locklessly update the link state variables while they are also
managed by pogo_power_off() under state_lock?

If a delayed heartbeat frame is processed exactly as pogo_hb_work() determines
a timeout and calls pogo_power_off(), could pogo_power_off() clear the state
just before this unsynchronized block unconditionally sets kb->connected = true
again?

>  		if (!READ_ONCE(kb->quitting))
>  			schedule_work(&kb->plug_work);
>  	}

[Severity: High]
Can this lockless check of kb->quitting lead to a use-after-free during driver
removal?

The serdev RX path is devres-managed and can run concurrently with
pogo_remove(). If the RX thread evaluates quitting as false just before
pogo_remove() sets it to true, it could queue plug_work after
cancel_work_sync(&kb->plug_work) has already finished, causing the work to run
on freed memory.

[ ... ]
>  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);
>  			complete(&kb->resp_done);
>  		}

[Severity: Medium]
Since the check for tx_pending and tx_cmd is lockless, could the RX thread be
preempted right after this condition evaluates to true?

If pogo_xfer() times out, resets tx_pending, and starts a retry with the same
command, the stalled RX thread could resume, overwrite kb->resp with the old
payload, and signal completion for the new transfer.

[ ... ]
>  static void pogo_wake_irq_enable(struct pogo_kbd *kb, bool enable)
>  {
>  	if (enable == kb->wake_irq_enabled)
>  		return;

[Severity: High]
Could this optimization skip synchronizing with a running ISR?

If pogo_wake_isr() sets kb->wake_irq_enabled = false, this check will return
immediately without calling disable_irq(). During pogo_remove(), this allows
the teardown to proceed without waiting for the ISR, which might then queue
detect_work after it has been cancelled.

[ ... ]
>  static void pogo_power_off(struct pogo_kbd *kb)
>  {
>  	bool was_connected;
>  
>  	mutex_lock(&kb->state_lock);
>  	was_connected = kb->connected;
>  	kb->connected = false;
>  	kb->powered = false;
>  	mutex_unlock(&kb->state_lock);

[Severity: Medium]
As mentioned above, if this safe transition runs concurrently with a delayed
frame processed by pogo_handle_sync(), could the hardware state become
desynchronized?

The software might incorrectly reflect a connected device while the hardware
is unpowered, causing subsequent sysfs accesses (like setting the backlight)
to time out waiting for the MCU.

[ ... ]
>  static ssize_t battery_level_show(struct device *dev,
>  				  struct device_attribute *attr, char *buf)
>  {
>  	struct pogo_kbd *kb = dev_get_drvdata(dev);
>  
>  	if (!kb->connected)
>  		return -ENODEV;
>  	/* only keyboards with a battery push 0x0E status frames */
>  	if (kb->battery_level < 0)
>  		return -ENODATA;
>  	return sysfs_emit(buf, "%d\n", kb->battery_level);
>  }
>  static DEVICE_ATTR_RO(battery_level);

[Severity: Low]
Does exposing battery_level as a custom sysfs attribute bypass the standard
power_supply subsystem?

Standard Linux userspace battery monitors (like UPower) only scan for devices
registered under the standard power_supply class. Would it be better to
register a POWER_SUPPLY_TYPE_BATTERY device using the power supply API so
desktop environments can seamlessly read the keyboard's battery status?

[ ... ]
>  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]
Are these sequential cancellations safe against background works queuing each
other?

Since detect_work is cancelled before hb_work, if hb_work evaluates !quitting
right before it is set to true here, it could queue detect_work again after
cancel_delayed_work_sync(&kb->detect_work) has already completed. This would
leave a timer active on the device structure after it is freed.

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

      reply	other threads:[~2026-10-15 11:19 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-09 12:20 [PATCH 0/2] Input: add support for OnePlus/OPPO Pad pogo-pin keyboards wyibo6109
2026-10-09 12:20 ` [PATCH 1/2] dt-bindings: input: add OnePlus pogo-pin keyboard binding wyibo6109
2026-10-09 12:28   ` sashiko-bot
2026-10-09 13:22   ` Krzysztof Kozlowski
2026-10-09 12:20 ` [PATCH 2/2] Input: add OnePlus/OPPO Pad pogo-pin keyboard serdev driver wyibo6109
2026-10-09 12:38   ` sashiko-bot
2026-10-10 14:38 ` [PATCH v2 0/2] Input: add support for OnePlus/OPPO Pad pogo-pin keyboards wyibo6109
2026-10-10 14:38   ` [PATCH v2 1/2] dt-bindings: input: add OnePlus pogo-pin keyboard wyibo6109
2026-10-10 14:47     ` sashiko-bot
2026-10-12  6:24     ` Krzysztof Kozlowski
2026-10-14 13:47       ` wyibo6109
2026-10-15  8:52         ` Krzysztof Kozlowski
2026-10-15 11:08           ` wyibo6109
2026-10-10 14:38   ` [PATCH v2 2/2] Input: add OnePlus/OPPO Pad pogo-pin keyboard serdev driver wyibo6109
2026-10-10 14:48     ` sashiko-bot
2026-10-14 13:52   ` [PATCH v3 0/2] Input: add support for OnePlus/OPPO Pad pogo-pin keyboards wyibo6109
2026-10-14 13:52     ` [PATCH v3 1/2] dt-bindings: input: add OnePlus pogo-pin keyboard wyibo6109
2026-10-15  8:50       ` Krzysztof Kozlowski
2026-10-14 13:52     ` [PATCH v3 2/2] Input: add OnePlus/OPPO Pad pogo-pin keyboard serdev driver wyibo6109
2026-10-14 14:03       ` sashiko-bot
2026-10-15 11:08     ` [PATCH v4 0/2] Input: add support for OnePlus/OPPO Pad pogo-pin keyboards wyibo6109
2026-10-15 11:08       ` [PATCH v4 1/2] dt-bindings: input: add OnePlus pogo-pin keyboard wyibo6109
2026-10-15 11:08       ` [PATCH v4 2/2] Input: add OnePlus/OPPO Pad pogo-pin keyboard serdev driver wyibo6109
2026-10-15 11:19         ` 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-172734@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.