All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Michael Reeves" <michael.reeves077@gmail.com>
Cc: devicetree@vger.kernel.org, linux-input@vger.kernel.org,
	robh@kernel.org, conor+dt@kernel.org, dmitry.torokhov@gmail.com
Subject: Re: [PATCH v5 6/8] HID: apple: Add DockChannel HID transport driver
Date: Sun, 08 Nov 2026 07:48:10 +0000	[thread overview]
Message-ID: <sashiko-outbox-203640@kernel.org> (raw)
In-Reply-To: <20261108-b4-apple-mtp-keyboard-v5-6-e2c97b82669a@gmail.com>

Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [High] HID: dockchannel: fix STM device_id race in
  dchid_create_interface_work()
- [High] HID: dockchannel: bound TX size and timeout in dchid_send() and
  dchid_probe()
- [Medium] HID: dockchannel: validate report descriptor and iface in
  dchid_handle_init()
--

commit 098cc69f724a6a1dd739b50990fad1fd9d489e3d
Author: Michael Reeves <michael.reeves077@gmail.com>

HID: apple: Add DockChannel HID transport driver

This commit adds a HID transport driver for Apple MTP keyboards and
trackpads connected over the DockChannel mailbox and RTKit.

> diff --git a/drivers/hid/dockchannel/apple-hid.c b/drivers/hid/dockchannel/apple-hid.c
> new file mode 100644
> index 0000000000000..3974b877236b5
> --- /dev/null
> +++ b/drivers/hid/dockchannel/apple-hid.c
[ ... ]
> +static int dchid_send(struct dchid_iface *iface, u32 flags, const void *msg,
> +		      size_t size)
> +{
> +	struct dchid_dev *dchid = iface->dchid;
> +	size_t payload_padded = round_up(size, sizeof(u32));
> +	size_t total_len = sizeof(struct dchid_hdr) + sizeof(struct dchid_subhdr) +
> +			   payload_padded + DCHID_CHECKSUM_LEN;
> +	struct apple_dockchannel_msg dc_msg;
> +	struct dchid_hdr *hdr;
> +	struct dchid_subhdr *sub;
> +	__le32 *checksum_ptr;
> +	u8 *buf;
> +	int ret;
> +
> +	if (total_len > DCHID_RX_BUF_SIZE)
> +		return -EINVAL;
> +
> +	buf = kzalloc(total_len, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	hdr = (struct dchid_hdr *)buf;
> +	sub = (struct dchid_subhdr *)(buf + sizeof(*hdr));
> +	checksum_ptr = (__le32 *)(buf + total_len - DCHID_CHECKSUM_LEN);
> +
> +	hdr->hdr_len = sizeof(*hdr);
> +	hdr->channel = DCHID_CHANNEL_CMD;
> +	hdr->length = cpu_to_le16(payload_padded + sizeof(*sub));
> +	hdr->seq = iface->tx_seq;
> +	hdr->iface = iface->index;
> +
> +	sub->flags = (u8)flags;
> +	sub->length = cpu_to_le16(size);
> +
> +	memcpy(buf + sizeof(*hdr) + sizeof(*sub), msg, size);
> +
> +	*checksum_ptr = cpu_to_le32(DCHID_CHECKSUM_SEED -
> +				   dchid_checksum(buf, total_len - DCHID_CHECKSUM_LEN));
> +
> +	dc_msg.data = buf;
> +	dc_msg.len = total_len;
> +	ret = mbox_send_message(dchid->dc_mbox, &dc_msg);
> +	kfree(buf);
> +
> +	return ret < 0 ? ret : 0;
> +}

[Severity: High]
Can a SET_REPORT larger than 2048 bytes wedge dchid->dc_mbox for an hour
and leave a dangling stack pointer queued in the mailbox channel?

dchid_send() checks total_len against DCHID_RX_BUF_SIZE (65547), while
dchid_ll leaves max_buffer_size unset (allowing raw output reports up to
HID_MAX_BUFFER_SIZE, 16384 bytes).  However, apple_dockchannel_send_data()
rejects any message larger than APPLE_DOCKCHANNEL_FIFO_SIZE (2048 bytes)
with -EMSGSIZE.

When msg_submit() fails in mbox_send_message(), the stack pointer &dc_msg
has already been added to chan->msg_data[] without setting
chan->active_req.  Because dchid_probe() sets dc_mbox_client.tx_block =
true while leaving tx_tout as 0, mbox_send_message() defaults to a
3600000 ms (1 hour) wait on chan->tx_complete while holding
dchid->cmd_lock.  Once that wait times out, dchid_send() frees buf and
returns while &dc_msg remains in chan->msg_data[], causing a stack and
heap use-after-free on later mailbox submissions.

Bounding total_len to APPLE_DOCKCHANNEL_FIFO_SIZE and setting a reasonable
dc_mbox_client.tx_tout in dchid_probe() would avoid this.

[ ... ]
> +static void dchid_create_interface_work(struct work_struct *ws)
> +{
> +	struct dchid_iface *iface = container_of(ws, struct dchid_iface, create_work);
> +	struct dchid_dev *dchid = iface->dchid;
> +	struct hid_device *hid;
> +	char cap_name[16];
> +	int ret;
> +
> +	if (READ_ONCE(dchid->stopping))
> +		goto done;
> +
> +	if (iface->hid) {
> +		dev_warn(dchid->dev, "interface %s already created\n", iface->name);
> +		goto done;
> +	}
> +
> +	ret = dchid_enable_interface(iface);
> +	if (ret < 0) {
> +		dev_warn(dchid->dev, "failed to enable %s: %d\n", iface->name, ret);
> +		goto done;
> +	}
> +
> +	iface->deferred = false;
> +
> +	hid = hid_allocate_device();
> +	if (IS_ERR(hid))
> +		goto done;
> +
> +	strscpy(cap_name, iface->name, sizeof(cap_name));
> +	if (cap_name[0])
> +		cap_name[0] = toupper(cap_name[0]);
> +	snprintf(hid->name, sizeof(hid->name), "Apple DockChannel %s", cap_name);
> +
> +	snprintf(hid->phys, sizeof(hid->phys), "%s.%d", dev_name(dchid->dev),
> +		 iface->index);
> +	strscpy(hid->uniq, dchid->serial, sizeof(hid->uniq));
> +
> +	hid->ll_driver = &dchid_ll;
> +	hid->bus = BUS_HOST;
> +	hid->vendor = le16_to_cpu(dchid->device_id.vendor_id);
> +	hid->product = le16_to_cpu(dchid->device_id.product_id);
> +	hid->version = le16_to_cpu(dchid->device_id.version_number);
[ ... ]
> +	ret = hid_add_device(hid);

[Severity: High]
On machines with apple,has-stm, doesn't dchid_create_interface_work()
register the stm HID device before or concurrently while
dchid_handle_ready() fetches dchid->device_id and dchid->serial?

dchid_handle_init() queues create_work for stm before dchid->id_ready is
set.  When CMD_ENABLE_INTERFACE completes, dchid_create_interface_work()
does not wait for EVENT_READY on stm, so it races with dchid_handle_ready()
running on dchid->comm->wq:

kworker (dchid->new_iface_wq)     kworker (dchid->comm->wq)
dchid_create_interface_work()
  dchid_enable_interface(stm)
                                  dchid_handle_ready()
                                    dchid_get_report_cmd(...,
                                      &dchid->device_id, ...)
                                    dchid_get_report_cmd(...,
                                      dchid->serial, ...)
  strscpy(hid->uniq,
          dchid->serial, ...)  <-- data race / uninitialized
  hid->vendor = ...            <-- data race / zero IDs
  hid_add_device(hid)
    ...
      dchid_open()
        !completion_done(&stm->ready)
        dchid_start_interface(stm)
          dchid_reset_interface() <-- resets stm mid-init
                                    dchid->serial[ret] = 0;
                                    dchid->id_ready = true;
                                    complete_all(&stm->ready);

As a result, the stm HID device is registered with zero or torn device IDs
and an uninitialized or unterminated serial string, and opening it before
dchid_handle_ready() reaches complete_all(&iface->ready) sends
CMD_RESET_INTERFACE while STM reports are being queried (or triggers a
second EVENT_READY that overwrites dchid->device_id and dchid->serial
while deferred interfaces are reading them).

[ ... ]
> +static void dchid_handle_descriptor(struct dchid_iface *iface, void *hid_desc,
> +				    size_t desc_len)
> +{
> +	u8 *rdesc;
> +	int i;
> +
> +	/* Keep the descriptor stable while creation and HID parsing run. */
> +	if (iface->hid_desc)
> +		return;
> +
> +	rdesc = devm_kmemdup(iface->dchid->dev, hid_desc, desc_len,
> +			     GFP_KERNEL);
> +	if (!rdesc)
> +		return;
[ ... ]
> +	iface->hid_desc = rdesc;
> +	iface->hid_desc_len = desc_len;
> +}
[ ... ]
> +static void dchid_handle_init(struct dchid_dev *dchid, void *data, size_t length)
> +{
> +	struct dchid_init_hdr *hdr = data;
> +	struct dchid_init_block_hdr *blk;
> +	struct dchid_iface *iface;
> +	u8 *p = data;
> +
> +	if (length < sizeof(*hdr))
> +		return;
> +
> +	if (!memchr(hdr->name, 0, sizeof(hdr->name)))
> +		return;
> +
> +	iface = dchid_get_interface(dchid, hdr->iface, hdr->name);
> +	if (!iface)
> +		return;
[ ... ]
> +	if (hdr->more_packets)
> +		return;
> +
> +	/* Wait for the shared device IDs only on machines with an STM. */
> +	if (dchid->id_ready || !strcmp(iface->name, "stm"))
> +		dchid_create_interface(iface);
> +	else
> +		iface->deferred = true;
> +}

[Severity: Medium]
What happens if devm_kmemdup() fails in dchid_handle_descriptor(), or if
an EVENT_INIT packet has no non-empty INIT_HID_DESCRIPTOR block?

dchid_handle_descriptor() returns without recording an error, and
dchid_handle_init() still queues dchid_create_interface(iface).
dchid_parse() then passes a NULL or zero-length iface->hid_desc to
hid_parse_report(), where kmemdup(NULL, 0, GFP_KERNEL) returns
ZERO_SIZE_PTR and registers a broken HID device with a zero-length report
descriptor.

Also, dchid_get_interface() accepts the name "comm" and returns any
existing dchid->ifaces[index] without checking that hdr->iface !=
IFACE_COMM or that the existing interface name matches hdr->name, which
would allow an EVENT_INIT for index 0 to queue HID device creation on
dchid->comm.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261108-b4-apple-mtp-keyboard-v5-0-e2c97b82669a@gmail.com?part=6

  reply	other threads:[~2026-11-08  7:48 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-08  7:25 [PATCH v5 0/8] Add support for Apple Silicon DockChannel internal keyboards Michael Reeves via B4 Relay
2026-11-08  7:25 ` Michael Reeves
2026-11-08  7:25 ` [PATCH v5 1/8] dt-bindings: mailbox: apple: Add DockChannel mailbox Michael Reeves via B4 Relay
2026-11-08  7:25   ` Michael Reeves
2026-11-08  7:25 ` [PATCH v5 2/8] dt-bindings: input: apple: Add DockChannel HID transport Michael Reeves via B4 Relay
2026-11-08  7:25   ` Michael Reeves
2026-11-10  9:20   ` Krzysztof Kozlowski
2026-11-10 11:26     ` Michael Reeves
2026-11-10 11:30       ` Krzysztof Kozlowski
2026-11-11  1:57         ` Michael Reeves
2026-11-08  7:25 ` [PATCH v5 3/8] mailbox: apple: Add DockChannel FIFO controller Michael Reeves via B4 Relay
2026-11-08  7:25   ` Michael Reeves
2026-11-08  7:45   ` sashiko-bot
2026-11-08  7:25 ` [PATCH v5 4/8] soc: apple: rtkit: Add tracekit endpoint Michael Reeves via B4 Relay
2026-11-08  7:25   ` Michael Reeves
2026-11-08  7:25 ` [PATCH v5 5/8] HID: apple: Add support for DockChannel HID keyboards Michael Reeves via B4 Relay
2026-11-08  7:25   ` Michael Reeves
2026-11-08  7:38   ` sashiko-bot
2026-11-08  7:25 ` [PATCH v5 6/8] HID: apple: Add DockChannel HID transport driver Michael Reeves via B4 Relay
2026-11-08  7:25   ` Michael Reeves
2026-11-08  7:48   ` sashiko-bot [this message]
2026-11-08  7:25 ` [PATCH v5 7/8] arm64: dts: apple: Add MTP DockChannel HID nodes Michael Reeves via B4 Relay
2026-11-08  7:25   ` Michael Reeves
2026-11-08  7:40   ` sashiko-bot
2026-11-08  7:48     ` Michael Reeves
2026-11-08  7:25 ` [PATCH v5 8/8] arm64: dts: apple: Enable DockChannel HID on M2 and M3 laptops Michael Reeves via B4 Relay
2026-11-08  7:25   ` Michael Reeves

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-203640@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=michael.reeves077@gmail.com \
    --cc=robh@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.