* MAX3420 UDC: out-of-bounds read/write via unvalidated wIndex in USB SETUP packet
@ 2026-04-11 17:58 Pavitra Jha
2026-04-11 18:00 ` [PATCH] usb: gadget: udc: max3420: validate endpoint index before array access Pavitra Jha
0 siblings, 1 reply; 2+ messages in thread
From: Pavitra Jha @ 2026-04-11 17:58 UTC (permalink / raw)
To: linux-usb; +Cc: Greg KH, stable, linux-kernel
Hello,
I am reporting an out-of-bounds read and write in the MAX3420 USB
Device Controller driver. The issue arises from using a
host-controlled wIndex field from a USB SETUP packet as a direct index
into a fixed-size endpoint array without validating that the index is
within bounds.
The driver handles USB control requests originating from an external
USB host and therefore must treat all request fields as untrusted.
1. VERSION AND ENVIRONMENT
Research kernel: v7.0.0-rc7
Architecture: x86_64
Observed unpatched as of April 11, 2026
2. VULNERABILITY PATH
USB host (attacker-controlled SETUP packet)
->
MAX3420 hardware (SUDFIFO register)
->
max3420_handle_irqs()
->
max3420_handle_setup()
->
spi_rd_buf(..., MAX3420_REG_SUDFIFO, ...)
->
udc->setup = setup
udc->setup.wIndex = cpu_to_le16(setup.wIndex)
->
dispatch:
-> max3420_getstatus()
-> max3420_set_clear_feature()
->
wIndex masked and used as endpoint index
->
out-of-bounds access into udc->ep[] when the derived index exceeds the
array bounds
No validation of the derived index is observed along this path.
3. VULNERABILITY DESCRIPTION
The endpoint index is derived as:
id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
USB_ENDPOINT_NUMBER_MASK is 0x0f, producing values in the range 0–15.
The backing storage is:
struct max3420_ep ep[MAX3420_MAX_EPS];
where:
MAX3420_MAX_EPS = 4
Valid indices are therefore 0–3. Values >= 4 will index beyond the
bounds of the array.
4. SOURCE CODE ANALYSIS
4.1 Untrusted input propagation
spi_rd_buf(udc, MAX3420_REG_SUDFIFO, (void *)&setup, 8);
udc->setup = setup;
udc->setup.wIndex = cpu_to_le16(setup.wIndex);
The SETUP packet contents are populated by the USB host and copied
into driver state without validation. The wIndex field is later used
in control flow and memory access decisions.
4.2 Direct indexing into fixed-size array
ep = &udc->ep[udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK];
and:
id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
ep = &udc->ep[id];
There is no check ensuring the derived index is less than
MAX3420_MAX_EPS before dereferencing.
4.3 Affected call sites
The pattern appears in:
- max3420_getstatus()
- max3420_set_clear_feature()
Both paths derive an index from wIndex and use it to access udc->ep[].
4.4 Existing checks are not sufficient
if (udc->setup.wIndex & USB_DIR_IN)
This condition only evaluates the direction bit and does not constrain
the endpoint number. It does not prevent out-of-bounds indexing.
4.5 Invariant violation
Regardless of protocol expectations, memory safety requires that any
externally influenced index used for array access be validated against
the array bounds.
Here, the code assumes the masked value is valid without enforcing
that constraint, which can lead to out-of-bounds access if unexpected
values are received.
5. IMPACT
Out-of-bounds read:
In max3420_getstatus(), fields are read from ep. If the index is
out-of-bounds, this may result in reading adjacent memory.
Out-of-bounds write:
In max3420_set_clear_feature(), fields within ep are modified
(including locking and state flags). If ep is out-of-bounds, this may
result in writes to unrelated memory.
Memory corruption characteristics:
Because ep[] is embedded within struct max3420_udc as a fixed-size array:
- out-of-bounds indexing accesses memory beyond the array within the same struct
- this may overlap adjacent struct members depending on layout
- further out-of-bounds accesses may extend beyond the struct into
surrounding memory depending on allocation context
Trigger:
A malformed USB control request can supply an out-of-range wIndex
value, for example:
bmRequestType = 0x02 (TYPE_STANDARD | RECIP_ENDPOINT)
bRequest = SET_FEATURE
wValue = ENDPOINT_HALT
wIndex = 0x0007
wLength = 0
This produces an index of 7, which exceeds the valid range [0–3].
6. VALIDATION EXPECTATION
The driver processes externally supplied USB control requests and
should validate fields before using them in memory access operations.
Other UDC drivers typically constrain endpoint indices or validate
request parameters before indexing fixed-size arrays. The absence of
such validation here suggests a missing bounds check.
7. SUGGESTED FIX
Validate the derived endpoint index before accessing the array:
id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
if (id >= MAX3420_MAX_EPS)
goto stall;
This check should be applied consistently in both:
- max3420_getstatus()
- max3420_set_clear_feature()
8. AFFECTED VERSIONS
Affected: all kernels including the MAX3420 UDC driver
Fixed in: not yet patched
9. REACHABILITY ANALYSIS (CODE-LEVEL)
The vulnerable access is reachable through the standard USB control
request handling path.
9.1 Interrupt entry point
USB SETUP packet handling is triggered from the IRQ path:
max3420_handle_irqs()
Relevant code:
if (epirq & SUDAVIRQ) {
spi_wr8(udc, MAX3420_REG_EPIRQ, SUDAVIRQ);
max3420_handle_setup(udc);
return true;
}
Receipt of a SETUP packet (SUDAVIRQ) directly invokes max3420_handle_setup().
9.2 SETUP packet ingestion from hardware
The SETUP packet is read directly from hardware:
spi_rd_buf(udc, MAX3420_REG_SUDFIFO, (void *)&setup, 8);
udc->setup = setup;
udc->setup.wValue = cpu_to_le16(setup.wValue);
udc->setup.wIndex = cpu_to_le16(setup.wIndex);
udc->setup.wLength = cpu_to_le16(setup.wLength);
No validation or bounds checking is performed on wIndex before storing it.
9.3 Dispatch into request handlers
Standard USB requests are dispatched as follows:
case USB_REQ_GET_STATUS:
return max3420_getstatus(udc);
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
return max3420_set_clear_feature(udc);
This dispatch occurs without validating the endpoint index derived from wIndex.
9.4 Out-of-bounds access in max3420_getstatus()
ep = &udc->ep[udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK];
No bounds check is performed before dereferencing ep.
9.5 Out-of-bounds access in max3420_set_clear_feature()
id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
ep = &udc->ep[id];
No validation ensures id < MAX3420_MAX_EPS.
9.6 Array bounds vs index range
Array definition:
struct max3420_ep ep[MAX3420_MAX_EPS];
#define MAX3420_MAX_EPS 4
Valid indices: 0–3. Index derivation produces values 0–15. Values >= 4
result in out-of-bounds access.
9.7 End-to-end reachability
USB host → SETUP packet
→ MAX3420_REG_SUDFIFO
→ spi_rd_buf(...)
→ udc->setup.wIndex (unvalidated)
→ max3420_getstatus() / max3420_set_clear_feature()
→ ep = &udc->ep[id]
→ out-of-bounds access when id >= 4
This path requires only a valid USB control request targeting an
endpoint and does not rely on undefined behavior or malformed packet
structure.
10. RUNTIME EVIDENCE (USERSPACE HARNESS)
Hardware emulation for the MAX3420 SPI controller is unavailable in
QEMU. A userspace harness was constructed that reconstructs the
vulnerable struct layout and control flow from max3420_udc.c verbatim,
feeding attacker-controlled SETUP packet fields directly into the
dispatch path. AddressSanitizer confirms the out-of-bounds write at
the exact offset predicted by the struct layout analysis.
Build:
gcc -fsanitize=address,undefined -g -O0 -o max3420_poc max3420_oob_harness.c
Trigger packet (wIndex=0x0007, derived id=7):
bmRequestType = 0x02
bRequest = SET_FEATURE
wValue = ENDPOINT_HALT
wIndex = 0x0007
wLength = 0x0000
ASan output:
max3420_oob_harness.c:154:26: runtime error: index 7 out of bounds
for type 'max3420_ep [4]'
ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7c78019e0680
WRITE of size 1 at 0x7c78019e0680 thread T0
#0 max3420_set_clear_feature max3420_oob_harness.c:163
#1 run_test max3420_oob_harness.c:223
#2 main max3420_oob_harness.c:282
0x7c78019e0680 is located 168 bytes after 280-byte region
SUMMARY: AddressSanitizer: heap-buffer-overflow in max3420_set_clear_feature
Struct layout confirms ep[4] overlaps irq_registered at offset 256.
ep[7] lands 168 bytes past the struct boundary. The harness source is
available on request.
Regards,
Pavitra Jha
jhapavitra98@gmail.com
^ permalink raw reply [flat|nested] 2+ messages in thread
* [PATCH] usb: gadget: udc: max3420: validate endpoint index before array access
2026-04-11 17:58 MAX3420 UDC: out-of-bounds read/write via unvalidated wIndex in USB SETUP packet Pavitra Jha
@ 2026-04-11 18:00 ` Pavitra Jha
0 siblings, 0 replies; 2+ messages in thread
From: Pavitra Jha @ 2026-04-11 18:00 UTC (permalink / raw)
To: linux-usb; +Cc: gregkh, stable, linux-kernel, Pavitra Jha
USB SETUP packets originate from an external USB host and must be
treated as untrusted input.
The endpoint index is derived from wIndex masked with
USB_ENDPOINT_NUMBER_MASK, producing values in the range 0-15. However,
udc->ep[] contains only MAX3420_MAX_EPS (4) entries.
Using this unvalidated value as an array index allows out-of-bounds
read and write access to memory beyond the ep[] array when wIndex >= 4.
Add bounds checks in max3420_getstatus() and
max3420_set_clear_feature() before using the derived index.
Cc: stable@vger.kernel.org
Reported-by: Pavitra Jha <jhapavitra98@gmail.com>
Signed-off-by: Pavitra Jha <jhapavitra98@gmail.com>
---
drivers/usb/gadget/udc/max3420_udc.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/gadget/udc/max3420_udc.c b/drivers/usb/gadget/udc/max3420_udc.c
index 11fd61cba..070893723 100644
--- a/drivers/usb/gadget/udc/max3420_udc.c
+++ b/drivers/usb/gadget/udc/max3420_udc.c
@@ -548,7 +548,10 @@ static void max3420_getstatus(struct max3420_udc *udc)
goto stall;
break;
case USB_RECIP_ENDPOINT:
- ep = &udc->ep[udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK];
+ id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ if (id >= MAX3420_MAX_EPS)
+ goto stall;
+ ep = &udc->ep[id];
if (udc->setup.wIndex & USB_DIR_IN) {
if (!ep->ep_usb.caps.dir_in)
goto stall;
@@ -596,6 +599,8 @@ static void max3420_set_clear_feature(struct max3420_udc *udc)
break;
id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ if (id >= MAX3420_MAX_EPS)
+ goto stall;
ep = &udc->ep[id];
spin_lock_irqsave(&ep->lock, flags);
--
2.53.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-04-11 18:01 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-11 17:58 MAX3420 UDC: out-of-bounds read/write via unvalidated wIndex in USB SETUP packet Pavitra Jha
2026-04-11 18:00 ` [PATCH] usb: gadget: udc: max3420: validate endpoint index before array access Pavitra Jha
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox