All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Vicki Pfau" <vi@endrift.com>
Cc: linux-input@vger.kernel.org, dmitry.torokhov@gmail.com
Subject: Re: [PATCH v7 07/12] Input: xbox_gip - Add security implementation from xone
Date: Sat, 12 Sep 2026 03:21:20 +0000	[thread overview]
Message-ID: <20260912032121.0F5411F000FF@smtp.kernel.org> (raw)
In-Reply-To: <20260912030426.2997003-8-vi@endrift.com>

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:

New issues:
- [High] Use-After-Free during device disconnect due to asynchronous cancellation of security workqueues.
- [High] Dynamic `INIT_WORK()` calls in `gip_security_start_handshake()` can corrupt workqueue state if re-entered.
- [High] Data race on shared security state (`transcript`, `random_client`, `pubkey_client`) due to lack of synchronization.
- [High] Heap variables embedded directly in a dynamically allocated struct are passed to scatterlists, risking DMA memory corruption.
- [Medium] Transcript hash incorrectly includes the message trailer, causing a protocol mismatch.
- [Medium] Sensitive session key is left on the kernel stack without explicit zeroization.

Pre-existing issues:
- [High] Index typo in `gip_disconnect()` causes out_fragments delayed works to not be cancelled, leading to a Use-After-Free.
--

commit 90b08fdfc4b71ac21c6c33cfb71aa69cf4b03d4c
Author: Vicki Pfau <vi@endrift.com>

Input: xbox_gip - Add security implementation from xone

This patch imports and adapts the security message implementation from the
out-of-tree xone driver. This message is necessary for most controllers
that use wireless dongles.

> diff --git a/drivers/input/joystick/gip/gip-core.c b/drivers/input/joystick/gip/gip-core.c
> index 1bfb559f5f7a2..ec21740bba50c 100644
> --- a/drivers/input/joystick/gip/gip-core.c
> +++ b/drivers/input/joystick/gip/gip-core.c

[ ... ]

This is a pre-existing issue, but I noticed a potential use-after-free bug
in the disconnect flow.

Looking at gip_disconnect() in drivers/input/joystick/gip/gip-core.c:

	mutex_lock(&gip->attachment_lock);
	for (i = 0; i < MAX_ATTACHMENTS; i++) {
		struct gip_attachment *attachment = gip->attachments[i];

		if (!attachment)
			continue;

		for (j = 0; j < MAX_OUT_FRAGMENTS; j++)
			cancel_delayed_work_sync(&attachment->out_fragments[i].timeout);
		devm_kfree(to_gip_device(attachment), attachment->uniq);
		devm_kfree(to_gip_device(attachment), attachment);
		gip->attachments[i] = NULL;
	}
	mutex_unlock(&gip->attachment_lock);

[Severity: High]
Does this code use the outer loop index `i` (attachment index) instead of the
inner loop index `j` (fragment index)?

If `i` is used, timeouts for fragments other than the one at index `i` are
never cancelled. The function then proceeds to free the `attachment` structure.
If any of the un-cancelled delayed works execute later, they will access the
freed `attachment`, leading to a use-after-free.

> diff --git a/drivers/input/joystick/gip/gip-security.c b/drivers/input/joystick/gip/gip-security.c
> new file mode 100644
> index 0000000000000..1850850b60e67
> --- /dev/null
> +++ b/drivers/input/joystick/gip/gip-security.c
> @@ -0,0 +1,810 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later

[ ... ]

> +static int gip_security_ecdh_get_secret(struct crypto_kpp *tfm, u8 *pubkey,
> +	int pubkey_len, u8 *secret, int secret_len)
> +{
> +	struct kpp_request *req;
> +	struct scatterlist src, dest;
> +	DECLARE_CRYPTO_WAIT(wait);
> +	int err;
> +
> +	req = kpp_request_alloc(tfm, GFP_KERNEL);
> +	if (!req)
> +		return -ENOMEM;
> +
> +	sg_init_one(&src, pubkey, pubkey_len);
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

[Severity: High]
Can passing these embedded, unaligned struct fields directly to
`sg_init_one()` cause DMA memory corruption?

The fields `security->pubkey_client2` and `security->pms` are embedded directly
inside `struct gip_security`, which itself is embedded in
`struct gip_attachment`. If the underlying crypto backend uses hardware DMA,
the DMA transfer will share a cacheline with adjacent fields in the
`gip_attachment` structure. A simultaneous CPU access to these adjacent fields
while the DMA is ongoing can corrupt either the DMA payload or the adjacent
struct fields.

[ ... ]

> +static void gip_security_complete_handshake(struct work_struct *work)
> +{
> +	struct gip_security *security = container_of(work, typeof(*security), work_complete);
> +	struct gip_attachment *attachment = container_of(security, struct gip_attachment, security);
> +	struct gip_security_header_control hdr = {
> +		.context = GIP_SECURITY_CTX_CONTROL,
> +		.control = GIP_SECURITY_CTRL_COMPLETE,
> +	};
> +	u8 random[GIP_SECURITY_RANDOM_LEN * 2];
> +	u8 key[GIP_SECURITY_SESSION_KEY_LEN];
> +	int err;
> +
> +	guard(mutex)(&attachment->lock);
> +	if (security->status != GIP_SECURITY_SENT)
> +		return;
> +
> +	memcpy(random, security->random_host, sizeof(security->random_host));
> +	memcpy(random + sizeof(security->random_host), security->random_client,
> +		sizeof(security->random_client));
> +
> +	gip_security_compute_prf(
> +		"EXPORTER DAWN data channel session key for controller",
> +		security->master_secret, sizeof(security->master_secret),
> +		random, sizeof(random), key, sizeof(key));
> +
> +	gip_dbg(security, "%s: key=%*phD\n", __func__, (int)sizeof(key), key);
> +
> +	err = gip_send_system_message(attachment, GIP_CMD_SECURITY,
> +		0, &hdr, sizeof(hdr));
> +	if (err) {
> +		gip_err(security, "%s: send complete failed: %d\n", __func__, err);
> +		return;
> +	}
> +
> +	security->status = GIP_SECURITY_DONE;
> +}
    ^^^

[Severity: Medium]
Should `memzero_explicit()` be used to wipe the sensitive session key
material from the stack?

The symmetric session key `key` and the pre-master secret/random data `random`
are generated into local stack arrays. After sending the completion message,
the function returns without zeroing out this sensitive cryptographic material,
which could be exposed if a subsequent kernel vulnerability allows reading
uninitialized stack memory.

[ ... ]

> +int gip_security_handle_message(struct gip_security *security, const void *bytes, int num_bytes)
> +{
> +	const struct gip_security_header_handshake *handshake = bytes;
> +	const struct gip_security_header_full *hdr;
> +	int err;
> +
> +	if (num_bytes < sizeof(*handshake))
> +		return -EINVAL;
> +
> +	if (security->status != GIP_SECURITY_SENT) {
> +		gip_err(security, "%s: security message received when none expected: 0x%02x\n",
> +			__func__, handshake->command);
> +		return -EPROTO;
> +	}
> +
> +	if (handshake->error)
> +		return -EPROTO;
> +
> +	if (handshake->options & GIP_SECURITY_OPT_ACKNOWLEDGE) {
> +		if (handshake->command == 0x01)
> +			return gip_security_handle_acknowledge(security);
> +
> +		gip_err(security, "%s: handshake failed: 0x%02x\n",
> +			__func__, handshake->command);
> +		return -EPROTO;
> +	}
> +
> +	if (num_bytes < sizeof(*hdr))
> +		return -EINVAL;
> +
> +	hdr = bytes;
> +	/* client uses v2 */
> +	if (num_bytes < sizeof(*hdr) || handshake->command != hdr->data.command) {
> +		/* reset transcript hash and restart handshake */
> +		gip_dbg(security, "%s: protocol upgrade\n", __func__);
> +		sha256_init(&security->transcript);
> +		return gip_security2_send_hello(security);
> +	}
> +
> +	err = gip_security_dispatch_message(security, hdr->data.command,
> +		bytes + sizeof(*hdr), num_bytes - sizeof(*hdr));
> +	if (err)
> +		return err;
> +
> +	sha256_update(&security->transcript, bytes + sizeof(hdr->handshake),
> +		num_bytes - sizeof(hdr->handshake));
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

[Severity: High]
Is there a data race on the shared security state (`transcript`,
`random_client`, `pubkey_client`) due to lack of synchronization?

Incoming messages are processed by `gip_receive_work`, which calls
`gip_security_handle_message()` without acquiring `attachment->lock`.
This function modifies shared state such as `security->transcript` and
`security->pubkey_client`.

Concurrently, asynchronous work items (like `gip_security_exchange_rsa`)
acquire `attachment->lock` and call `gip_security_send_message()`, which also
updates the `transcript` hash. Since one side does not hold the lock, these
threads can race and corrupt the internal state of the SHA256 context or
copy torn data.

[Severity: Medium]
Does this transcript hash incorrectly include the message trailer?

The transcript hash is updated using `num_bytes - sizeof(hdr->handshake)`.
If the received packet includes the 8-byte trailer, `num_bytes` encompasses it,
and the trailer gets hashed. However, the sender (`gip_security_send_message()`)
specifically excludes the trailer from the hash. This discrepancy causes a
transcript mismatch during the `Finished` step.

> +
> +	return 0;
> +}
> +
> +void gip_security_release(struct gip_security *security)
> +{
> +	if (security->status < GIP_SECURITY_SENT)
> +		return;
> +
> +	cancel_work(&security->work_exchange_rsa);
> +	cancel_work(&security->work_exchange_ecdh);
> +	cancel_work(&security->work_complete);
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

[Severity: High]
Is it safe to use the non-blocking `cancel_work()` instead of
`cancel_work_sync()` here?

During device disconnect, `gip_disconnect()` calls `gip_shutdown()` which calls
`gip_security_release()`. This uses the non-blocking `cancel_work()` to cancel
the security work items.

`gip_disconnect()` then explicitly frees the `attachment` structure using
`devm_kfree()`. If a security work item is actively executing on another CPU,
it will continue running and attempt to acquire the freed `attachment->lock` or
access other freed fields (such as inside `gip_security_exchange_rsa()`),
resulting in a use-after-free.

> +
> +	memzero_explicit(&security->transcript, sizeof(security->transcript));
> +	memzero_explicit(&security->pms, sizeof(security->pms));
> +	memzero_explicit(&security->master_secret, sizeof(security->master_secret));
> +
> +	security->status = GIP_SECURITY_PENDING;
> +}
> +
> +int gip_security_start_handshake(struct gip_security *security)
> +{
> +	int err;
> +
> +	sha256_init(&security->transcript);
> +
> +	INIT_WORK(&security->work_exchange_rsa, gip_security_exchange_rsa);
> +	INIT_WORK(&security->work_exchange_ecdh, gip_security2_exchange_ecdh);
> +	INIT_WORK(&security->work_complete, gip_security_complete_handshake);
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

[Severity: High]
Can dynamically re-initializing these work items corrupt the workqueue state
if they are re-entered?

`INIT_WORK()` is dynamically invoked every time `gip_security_start_handshake()`
runs. If the handshake fails or is reset (e.g., via `HELLO_DEVICE`),
`gip_security_release()` cancels the work asynchronously.

A subsequent `STATUS_DEVICE` message will call `gip_security_start_handshake()`
again. If the previous work item is still executing (because `cancel_work()` is
asynchronous), calling `INIT_WORK()` will overwrite its internal
`work_data_bits` and list pointers, leading to workqueue corruption.

> +
> +	err = gip_security_send_hello(security);
> +	if (err < 0)
> +		return err;
> +
> +	security->status = GIP_SECURITY_SENT;
> +	return 0;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260912030426.2997003-1-vi@endrift.com?part=7

  reply	other threads:[~2026-09-12  3:21 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-12  3:04 [PATCH v7 00/12] Input: xbox_gip - Add new driver for Xbox GIP Vicki Pfau
2026-09-12  3:04 ` [PATCH v7 01/12] " Vicki Pfau
2026-09-12  3:25   ` sashiko-bot
2026-09-12  3:04 ` [PATCH v7 02/12] Input: xpad - Remove Xbox One support Vicki Pfau
2026-09-12  3:22   ` sashiko-bot
2026-09-12  3:04 ` [PATCH v7 03/12] Input: xbox_gip - Add controllable LED support Vicki Pfau
2026-09-12  3:20   ` sashiko-bot
2026-09-12  3:04 ` [PATCH v7 04/12] Input: xbox_gip - Add HID relaying Vicki Pfau
2026-09-12  3:21   ` sashiko-bot
2026-09-12  3:04 ` [PATCH v7 05/12] Input: xbox_gip - Add battery support Vicki Pfau
2026-09-12  3:22   ` sashiko-bot
2026-09-12  3:04 ` [PATCH v7 06/12] Input: xbox_gip - Allow sending fragmented and ACME messages Vicki Pfau
2026-09-12  3:23   ` sashiko-bot
2026-09-12  3:04 ` [PATCH v7 07/12] Input: xbox_gip - Add security implementation from xone Vicki Pfau
2026-09-12  3:21   ` sashiko-bot [this message]
2026-09-12  3:04 ` [PATCH v7 08/12] Input: xbox_gip - Add arcade stick support Vicki Pfau
2026-09-12  3:16   ` sashiko-bot
2026-09-12  3:04 ` [PATCH v7 09/12] Input: xbox_gip - Add support for PDP guitar controllers Vicki Pfau
2026-09-12  3:23   ` sashiko-bot
2026-09-12  3:04 ` [PATCH v7 10/12] Input: Add ABS_CLUTCH, HANDBRAKE, and SHIFTER Vicki Pfau
2026-09-12  3:28   ` sashiko-bot
2026-09-12  3:04 ` [PATCH v7 11/12] HID: Map more automobile simulation inputs Vicki Pfau
2026-09-12  3:28   ` sashiko-bot
2026-09-12  3:04 ` [PATCH v7 12/12] Input: xbox_gip - Add wheel support Vicki Pfau
2026-09-12  3:33   ` sashiko-bot

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=20260912032121.0F5411F000FF@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=vi@endrift.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.