[PATCH 2/2] usb: gadget: f_tcm: validate UAS command IU length before parsing
Radhey Shyam Pandey <[email protected]>
| Newsgroups | org.kernel.vger.linux-usb,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
usbg_submit_command() parsed Command and Task Management IUs from a buffer sized to ep_cmd->maxpacket without checking req->actual. A host could supply a short IU or inflated cmd_iu->len and trigger an out-of-bounds read when copying the CDB. Validate the received length before parsing, matching the BOT path, and re-queue the command endpoint request when validation fails. Assisted-by: claude-mythos-preview-high Signed-off-by: Radhey Shyam Pandey <[email protected]> --- drivers/usb/gadget/function/f_tcm.c | 33 ++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index 565ad1934755..4b30b23ddac0 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -862,6 +862,7 @@ static int usbg_submit_command(struct f_uas *, struct usb_request *); static void uasp_cmd_complete(struct usb_ep *ep, struct usb_request *req) { struct f_uas *fu = req->context; + int ret; if (req->status == -ESHUTDOWN) return; @@ -871,7 +872,9 @@ static void uasp_cmd_complete(struct usb_ep *ep, struct usb_request *req) return; } - usbg_submit_command(fu, req); + ret = usbg_submit_command(fu, req); + if (ret) + usb_ep_queue(fu->ep_cmd, req, GFP_ATOMIC); } static int uasp_alloc_stream_res(struct f_uas *fu, struct uas_stream *stream) @@ -1382,6 +1385,27 @@ static int usbg_submit_command(struct f_uas *fu, struct usb_request *req) return -EINVAL; } + cmd_iu = (struct command_iu *)iu; + + if (req->actual < offsetof(struct command_iu, cdb)) { + pr_err("Wrong length for UAS command IU\n"); + return -EINVAL; + } + + if (iu->iu_id == IU_ID_COMMAND) { + cmd_len = (cmd_iu->len & ~0x3) + 16; + if (cmd_len > USBG_MAX_CMD || + req->actual < offsetof(struct command_iu, cdb) + cmd_len) { + pr_err("Wrong length for UAS command IU\n"); + return -EINVAL; + } + } else if (iu->iu_id == IU_ID_TASK_MGMT) { + if (req->actual < sizeof(struct task_mgmt_iu)) { + pr_err("Wrong length for UAS task management IU\n"); + return -EINVAL; + } + } + scsi_tag = be16_to_cpup(&iu->tag); cmd = usbg_get_cmd(fu, tv_nexus, scsi_tag); if (IS_ERR(cmd)) { @@ -1398,8 +1422,6 @@ static int usbg_submit_command(struct f_uas *fu, struct usb_request *req) cmd->flags = 0; cmd->data_len = 0; - cmd_iu = (struct command_iu *)iu; - /* Command and Task Management IUs share the same LUN offset */ cmd->unpacked_lun = scsilun_to_int(&cmd_iu->lun); @@ -1434,11 +1456,6 @@ static int usbg_submit_command(struct f_uas *fu, struct usb_request *req) } cmd_len = (cmd_iu->len & ~0x3) + 16; - if (cmd_len > USBG_MAX_CMD) { - target_free_tag(tv_nexus->tvn_se_sess, &cmd->se_cmd); - hash_del(&stream->node); - return -EINVAL; - } memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len); switch (cmd_iu->prio_attr & 0x7) { -- 2.44.4