mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
HID: hid-lenovo-go-s: Bound stale reply window before reusing send_cmd_complete
mcu_property_out() reinits send_cmd_complete immediately after a
timeout is detected, before the next command that reuses it is sent.
If the MCU's reply to the timed-out command arrives after this reinit
but before the next command's wait begins, it silently satisfies the
next, unrelated command's wait instead of the one it actually answers,
handing that caller stale data with no way to detect the mismatch.
Track when a command has timed out via cmd_orphaned. Before the next
command reuses the completion, wait a bounded 25ms for a stale reply
to arrive and be consumed, then unconditionally clear the flag and
reinit the completion. This does not fully eliminate the window in
which an unrelated reply could still be received, but bounds it to
a short interval right before a new command is sent. Behavior matches
the solution to the same problem in hid-msi.
Fixes: a23f3497bf ("HID: hid-lenovo-go-s: Add Lenovo Legion Go S Series HID Driver")
Cc: stable@vger.kernel.org
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
This commit is contained in:
parent
8ac42b2885
commit
6224755416
|
|
@ -37,6 +37,7 @@ static struct hid_gos_cfg {
|
|||
struct completion send_cmd_complete;
|
||||
struct led_classdev *led_cdev;
|
||||
struct hid_device *hdev;
|
||||
bool orphan_ack_pending;
|
||||
struct mutex cfg_mutex; /*ensure single synchronous output report*/
|
||||
int cmd_status;
|
||||
u8 gp_auto_sleep_time;
|
||||
|
|
@ -454,6 +455,19 @@ static int mcu_property_out(struct hid_device *hdev, u8 command, u8 index,
|
|||
return -EINVAL;
|
||||
|
||||
guard(mutex)(&drvdata.cfg_mutex);
|
||||
|
||||
/*
|
||||
* A reply to the previous command may still be in flight. Give it a
|
||||
* short window to arrive and be consumed before this call reinits the
|
||||
* completion, so a late reply can't be mistaken for this command's.
|
||||
*/
|
||||
if (drvdata.orphan_ack_pending) {
|
||||
wait_for_completion_timeout(&drvdata.send_cmd_complete, msecs_to_jiffies(25));
|
||||
drvdata.orphan_ack_pending = false;
|
||||
drvdata.cmd_status = -ETIMEDOUT;
|
||||
}
|
||||
reinit_completion(&drvdata.send_cmd_complete);
|
||||
|
||||
/* We can't use a devm_alloc reusable buffer without side effects during suspend */
|
||||
dmabuf = kzalloc(GO_S_PACKET_SIZE, GFP_KERNEL);
|
||||
if (!dmabuf)
|
||||
|
|
@ -479,7 +493,9 @@ static int mcu_property_out(struct hid_device *hdev, u8 command, u8 index,
|
|||
msecs_to_jiffies(timeout));
|
||||
ret = ret > 0 ? drvdata.cmd_status : ret ?: -EBUSY;
|
||||
|
||||
reinit_completion(&drvdata.send_cmd_complete);
|
||||
if (ret)
|
||||
drvdata.orphan_ack_pending = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user