usb: typec: tcpm: bound altmode_desc[] per iteration in svdm_consume_modes()

svdm_consume_modes() checks pmdata->altmodes against the array size once
before the loop over the count, but forgot to check the bound at every
point in the loop.

In the well-behaved SVDM discovery flow this is harmless because each of
at most SVID_DISCOVERY_MAX SVIDs contributes at most MODE_DISCOVERY_MAX
modes, exactly filling altmode_desc[ALTMODE_DISCOVERY_MAX].  But the
CMDT_RSP_ACK handler in tcpm_pd_svdm() does not correlate an incoming
ACK with any request the port actually sent.  Once port->partner is set,
an unsolicited Discover Modes ACK is consumed unconditionally.  A broken
or malicious port partner can therefore drive altmodes to
ALTMODE_DISCOVERY_MAX - 1 via the normal flow, and then send one extra
Discover Modes ACK with seven VDOs.  Because the pre-loop check passes,
the loop could then writes up to five entries past altmode_desc[].  For
mode_data_prime the next field in struct tcpm_port is the
partner_altmode[] pointer array, which then receives partner-chosen
SVID/VDO bytes.

Move the bound check inside the loop so the array can never be indexed
past ALTMODE_DISCOVERY_MAX regardless of how many VDOs the partner
supplies or how the function was reached.

Assisted-by: gkh_clanker_t1000
Cc: Badhri Jagan Sridharan <badhri@google.com>
Cc: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Cc: stable <stable@kernel.org>
Link: https://patch.msgid.link/2026051351-reshuffle-skillful-90af@gregkh
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Greg Kroah-Hartman 2026-05-13 17:52:53 +02:00
parent 8fbc349e83
commit 3389c149c6

View File

@ -1992,23 +1992,19 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt,
switch (rx_sop_type) {
case TCPC_TX_SOP_PRIME:
pmdata = &port->mode_data_prime;
if (pmdata->altmodes >= ARRAY_SIZE(port->plug_prime_altmode)) {
/* Already logged in svdm_consume_svids() */
return;
}
break;
case TCPC_TX_SOP:
pmdata = &port->mode_data;
if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) {
/* Already logged in svdm_consume_svids() */
return;
}
break;
default:
return;
}
for (i = 1; i < cnt; i++) {
if (pmdata->altmodes >= ALTMODE_DISCOVERY_MAX) {
/* Already logged in svdm_consume_svids() */
return;
}
paltmode = &pmdata->altmode_desc[pmdata->altmodes];
memset(paltmode, 0, sizeof(*paltmode));