From: sashiko-bot@kernel.org
To: "Laserology OSS" <development@laserology.net>
Cc: dmitry.torokhov@gmail.com, linux-input@vger.kernel.org
Subject: Re: [PATCH v4] Input: synaptics - add transparent pass-through mode for TrackPoint
Date: Mon, 10 Aug 2026 00:10:30 +0000 [thread overview]
Message-ID: <20260810001030.B92AF1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260809234611.25655-1-development@laserology.net>
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [High] TOCTOU races in synaptics_pt_stop and synaptics_set_transparent_mode cause hardware and software state desynchronization.
- [High] synaptics_pt_write bypasses ps2_cmd_mutex in transparent mode, corrupting the PS/2 bus byte stream.
- [High] synaptics_reconnect lacks pt_mutex synchronization, causing races with concurrent guest writes.
- [High] Delayed NULL pointer dereference (or Use-After-Free) in synaptics_reconnect due to a TOCTOU read of priv->pt_port.
Pre-existing issues:
- [High] Ineffective driver type check allows type confusion and out-of-bounds access in pass-through port handling.
- [High] Incomplete fix for the child port leak: child port leaks if input_register_device fails, leading to UAF.
--
--- Patch [1]: [PATCH v4] Input: synaptics - add transparent pass-through mode for TrackPoint ---
commit 7d4fbfed011585417421459058126e46e98ff9df
Author: Evan Lawrence <development@laserology.net>
Input: synaptics - add transparent pass-through mode for TrackPoint
This commit introduces a transparent pass-through mode for Synaptics touchpads,
allowing a pass-through guest to operate at a higher poll rate. It adds a sysfs
attribute to toggle the mode, handles hardware state switches, and adjusts the
protocol handler dynamically.
> diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
> index 2170bbe4c5897..2a004ae34158d 100644
> --- a/drivers/input/mouse/synaptics.c
> +++ b/drivers/input/mouse/synaptics.c
[ ... ]
> static int synaptics_pt_write(struct serio *serio, u8 c)
> {
> struct psmouse *parent = psmouse_from_serio(serio->parent);
> + struct synaptics_data *priv = parent->private;
> u8 rate_param = SYN_PS_CLIENT_CMD; /* indicates that we want pass-through port */
> int error;
>
> + guard(mutex)(&priv->pt_mutex);
> +
> + if (READ_ONCE(priv->transparent_mode))
> + return parent->ps2dev.serio->write(parent->ps2dev.serio, c);
[Severity: High]
Does bypassing ps2_cmd_mutex here allow raw guest bytes to interleave with host
commands on the PS/2 bus?
In drivers/input/mouse/synaptics.c:synaptics_pt_write():
When transparent mode is active, this fast-path directly writes to the
hardware under pt_mutex, but does not acquire ps2_cmd_mutex. If a sysfs
attribute on the parent touchpad is updated concurrently, it issues a
ps2_command(), potentially interleaving guest bytes and host commands and
corrupting communication with the touchpad.
> error = ps2_sliced_command(&parent->ps2dev, c);
[ ... ]
> @@ -669,9 +749,30 @@ static void synaptics_pt_stop(struct serio *serio)
> {
> struct psmouse *parent = psmouse_from_serio(serio->parent);
> struct synaptics_data *priv = parent->private;
> + bool exit_mode = READ_ONCE(priv->transparent_mode);
>
> - guard(serio_pause_rx)(parent->ps2dev.serio);
> - priv->pt_port = NULL;
> + /*
> + * The guest is going away. Hold the pass-through mutex across
> + * the hardware exit and the software state update so a
> + * concurrent synaptics_pt_write() cannot observe the old
> + * transparent_mode after the hardware has already been
> + * switched back to normal operation.
> + */
> + guard(mutex)(&priv->pt_mutex);
> +
> + if (exit_mode) {
> + if (synaptics_exit_transparent_mode(parent))
[Severity: High]
Does reading exit_mode outside of pt_mutex allow the hardware and software
state to become desynchronized?
In drivers/input/mouse/synaptics.c:synaptics_pt_stop():
If synaptics_pt_stop() is called concurrently with
synaptics_set_transparent_mode(), exit_mode is cached before acquiring the lock.
The mode switch could enable hardware transparent mode, and then
synaptics_pt_stop() acquires the lock, skips the hardware exit because the
cached exit_mode was false, but forces the software state to normal mode.
> + psmouse_warn(parent,
> + "failed to exit transparent mode after pass-through port removal\n");
> + }
[ ... ]
> @@ -709,7 +822,7 @@ static void synaptics_pass_pt_packet(struct synaptics_data *priv, u8 *packet)
>
> serio_interrupt(ptport, packet[1], 0);
>
> - if (priv->pt_port_open) {
> + if (priv->pt_port_open && ptport->id.type == SERIO_PS_PSTHRU) {
> struct psmouse *child = psmouse_from_serio(ptport);
>
> if (child->state == PSMOUSE_ACTIVATED) {
[Severity: High]
This is a pre-existing issue, but does checking the hardware ID reliably
verify the bound software driver type?
In drivers/input/mouse/synaptics.c:synaptics_pass_pt_packet():
If a root user unbinds the psmouse driver from the pass-through child port
and binds a different driver like serio_raw, this check passes because it
evaluates the hardware ID. The foreign driver's private data is then
downcasted to struct psmouse, potentially allowing out-of-bounds writes to
child->state or child->oob_data_type.
> @@ -724,7 +837,14 @@ static void synaptics_pass_pt_packet(struct synaptics_data *priv, u8 *packet)
[ ... ]
> +static ssize_t synaptics_set_transparent_mode(struct psmouse *psmouse,
> + void *data, const char *buf,
> + size_t len)
> +{
> + struct synaptics_data *priv = psmouse->private;
> + unsigned int value;
> + int err;
> +
> + err = kstrtouint(buf, 10, &value);
> + if (err)
> + return err;
> +
> + if (value > 1)
> + return -EINVAL;
> +
> + if (value == READ_ONCE(priv->transparent_mode))
> + return len;
> +
> + /* Transparent mode only makes sense while the guest is attached */
> + if (value && !READ_ONCE(priv->pt_port))
> + return -ENODEV;
> +
> + /*
> + * Switch the hardware first and only commit the new mode and protocol
> + * handler once it succeeded, so a failing command cannot leave the
> + * driver with bookkeeping that does not match the device. The touchpad
> + * is deactivated for the duration of this write (see
> + * psmouse_attr_set_helper), so no live data can be misparsed in
> + * between. Hold the pass-through mutex so that a concurrent
> + * synaptics_pt_write() cannot inject a byte onto the bus in the
> + * middle of the mode-switch command sequence.
> + */
> + guard(mutex)(&priv->pt_mutex);
[Severity: High]
Does checking pt_port outside of pt_mutex create a race condition with
synaptics_pt_stop()?
In drivers/input/mouse/synaptics.c:synaptics_set_transparent_mode():
Since the pt_port check happens before acquiring pt_mutex,
synaptics_pt_stop() could concurrently destroy pt_port before the mode switch
executes, leading to misconfigured protocol handlers.
> +
> + if (value) {
> + err = synaptics_enter_transparent_mode(psmouse);
[ ... ]
> @@ -1478,8 +1681,35 @@ static int synaptics_reconnect(struct psmouse *psmouse)
> */
> ssleep(1);
> }
> - ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETID);
> - error = synaptics_detect(psmouse, 0);
> + if (READ_ONCE(priv->transparent_mode) && READ_ONCE(priv->pt_port)) {
> + /*
> + * psmouse_reset() above put the touchpad back into its
> + * power-on state, so run the full initialization before
> + * re-entering transparent mode. Otherwise the device
> + * would come back missing the configuration set up by
> + * synaptics_set_mode() (e.g. Advanced Gesture Mode) and
> + * would stay uninitialized once transparent mode is
> + * disabled again.
> + */
> + error = synaptics_query_hardware(psmouse, &info);
> + if (!error &&
> + (info.identity != priv->info.identity ||
> + info.model_id != priv->info.model_id ||
> + info.capabilities != priv->info.capabilities ||
> + info.ext_cap != priv->info.ext_cap))
> + error = -ENXIO;
> + if (!error)
> + error = synaptics_set_mode(psmouse);
> + if (!error)
> + error = synaptics_enter_transparent_mode(psmouse);
[Severity: High]
Can concurrent guest writes interleave with the hardware initialization
sequence here?
In drivers/input/mouse/synaptics.c:synaptics_reconnect():
The entire re-initialization sequence runs without holding pt_mutex. If the
child port driver writes data concurrently, synaptics_pt_write() will see
transparent_mode as true, lock pt_mutex, and inject bytes directly onto the
bus between the PS/2 commands issued here.
> + if (!error) {
> + serio_reconnect(priv->pt_port);
[Severity: High]
Can priv->pt_port be NULL or point to freed memory by the time it is accessed
here?
In drivers/input/mouse/synaptics.c:synaptics_reconnect():
priv->pt_port is checked before the lengthy hardware query sequence. If the
child port is concurrently unbound during this time, synaptics_pt_stop() could
nullify and free the port structure, causing a NULL pointer dereference or
use-after-free when passed to serio_reconnect().
> + return 0;
> + }
[ ... ]
> @@ -1691,9 +1913,35 @@ static int synaptics_init_ps2(struct psmouse *psmouse,
[ ... ]
> + /*
> + * Register the pass-through port only once all initialization steps
> + * that can fail have succeeded, so that a failure in one of the steps
> + * above cannot leak a registered child port whose callbacks would
> + * dereference the freed synaptics_data.
> + */
> + if (SYN_CAP_PASS_THROUGH(info->capabilities))
> + synaptics_pt_create(psmouse);
> +
> return 0;
[Severity: High]
This is a pre-existing issue, but does this still leak the child port if
the parent's input_register_device() fails later?
In drivers/input/mouse/synaptics.c:synaptics_init_ps2():
Even though synaptics_pt_create() is moved to the end of this function, if
the caller (psmouse_connect()) subsequently fails in input_register_device(),
it aborts and frees the psmouse instance via synaptics_disconnect(). The child
port is never unregistered, and if it is bound later, its handlers will
dereference the freed parent private data.
>
> init_fail:
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260809234611.25655-1-development@laserology.net?part=1
prev parent reply other threads:[~2026-08-10 0:10 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-09 23:46 [PATCH v4] Input: synaptics - add transparent pass-through mode for TrackPoint Laserology OSS
2026-08-10 0:10 ` 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=20260810001030.B92AF1F000E9@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=development@laserology.net \
--cc=dmitry.torokhov@gmail.com \
--cc=linux-input@vger.kernel.org \
--cc=sashiko-reviews@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 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.