[PATCH RFC] usb: typec: tcpm: reject type-mismatched source/sink PDO pairs

From: pip-izony

Date: Wed Sep 23 2026 - 13:04:18 EST


From: Seungjin Bae <eeodqql09@xxxxxxxxx>

The tcpm_pd_select_pdo() function matches a source PDO against a sink
PDO using their voltage ranges only, without checking that the two
PDOs are of the same type. A source PDO and a sink PDO of different
types (e.g. a Battery source PDO and a Fixed sink PDO) can therefore
be matched as long as their voltage ranges overlap.

tcpm_pd_build_request() then combines the matched pair with
min_power()/min_current(), which apply the same accessor to both
operands. Since pdo_max_current() and pdo_max_power() decode the same
bits (9:0) with different scaling (x10 mA vs x250 mW), a type-mismatched
sink operand is misinterpreted. This misreads the sink's capability and
weakens the min() bound intended to cap the request to the sink's limit.

Require the source and sink PDO types to match before a pair is
selected. This keeps the voltage-range matching introduced by commit
53fe0de9a35d ("usb: typec: tcpm: pdo matching optimization") and covers
both the min() computation and the mismatch branch in
tcpm_pd_build_request().

I found this by static analysis and have not observed it on hardware, so
I am sending it as RFC.

Fixes: 53fe0de9a35d ("usb: typec: tcpm: pdo matching optimization")
Signed-off-by: Seungjin Bae <eeodqql09@xxxxxxxxx>
---
drivers/usb/typec/tcpm/tcpm.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 2d6b14aa2085..4959872050ae 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -4514,8 +4514,9 @@ static int tcpm_pd_select_pdo(struct tcpm_port *port, int *sink_pdo,
continue;
}

- if (max_src_mv <= max_snk_mv &&
- min_src_mv >= min_snk_mv) {
+ if (pdo_type(port->source_caps[i]) == pdo_type(pdo) &&
+ max_src_mv <= max_snk_mv &&
+ min_src_mv >= min_snk_mv) {
/* Prefer higher voltages if available */
if ((src_mw == max_mw && min_src_mv > max_mv) ||
src_mw > max_mw) {
--
2.43.0