usb: gadget: f_uac1_legacy: validate control request size

f_audio_complete() copies req->length bytes into a 4-byte stack
variable:

  u32 data = 0;
  memcpy(&data, req->buf, req->length);

req->length is derived from the host-controlled USB request path,
which can lead to a stack out-of-bounds write.

Validate req->actual against the expected payload size for the
supported control selectors and decode only the expected amount
of data.

This avoids copying a host-influenced length into a fixed-size
stack object.

Signed-off-by: Taegu Ha <hataegu0826@gmail.com>
Cc: stable <stable@kernel.org>
Link: https://patch.msgid.link/20260401191311.3604898-1-hataegu0826@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Taegu Ha
2026-04-02 04:13:11 +09:00
committed by Greg Kroah-Hartman
parent 01af542392
commit 6e0e34d85c

View File

@@ -360,19 +360,46 @@ static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req)
static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_audio *audio = req->context;
int status = req->status;
u32 data = 0;
struct usb_ep *out_ep = audio->out_ep;
switch (status) {
case 0: /* normal completion? */
if (ep == out_ep)
switch (req->status) {
case 0:
if (ep == out_ep) {
f_audio_out_ep_complete(ep, req);
else if (audio->set_con) {
memcpy(&data, req->buf, req->length);
audio->set_con->set(audio->set_con, audio->set_cmd,
le16_to_cpu(data));
} else if (audio->set_con) {
struct usb_audio_control *con = audio->set_con;
u8 type = con->type;
u32 data;
bool valid_request = false;
switch (type) {
case UAC_FU_MUTE: {
u8 value;
if (req->actual == sizeof(value)) {
memcpy(&value, req->buf, sizeof(value));
data = value;
valid_request = true;
}
break;
}
case UAC_FU_VOLUME: {
__le16 value;
if (req->actual == sizeof(value)) {
memcpy(&value, req->buf, sizeof(value));
data = le16_to_cpu(value);
valid_request = true;
}
break;
}
}
if (valid_request)
con->set(con, audio->set_cmd, data);
else
usb_ep_set_halt(ep);
audio->set_con = NULL;
}
break;