]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
usb: typec: tcpm: bound altmode_desc[] per iteration in svdm_consume_modes()
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 13 May 2026 15:52:53 +0000 (17:52 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 19 May 2026 10:25:54 +0000 (12:25 +0200)
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>
drivers/usb/typec/tcpm/tcpm.c

index 44dab6c32c33600a445c7b17507420388f52a51b..ed5f745a823134c017778e019b72d4d5b5d003af 100644 (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));