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
next prev parent reply other threads:[~2026-11-08 7:48 UTC|newest]
Thread overview: 18+ 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 ` [PATCH v5 1/8] dt-bindings: mailbox: apple: Add DockChannel mailbox Michael Reeves via B4 Relay
2026-11-08 7:25 ` [PATCH v5 2/8] dt-bindings: input: apple: Add DockChannel HID transport Michael Reeves via B4 Relay
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: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 ` [PATCH v5 5/8] HID: apple: Add support for DockChannel HID keyboards Michael Reeves via B4 Relay
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: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: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
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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox