diff options
| author | Alison Schofield <alison.schofield@intel.com> | 2026-08-07 17:05:51 -0700 |
|---|---|---|
| committer | Dave Jiang <dave.jiang@intel.com> | 2026-09-16 09:05:11 -0700 |
| commit | 4e42c398af136d62fac8c19c9f410ec83a8dd34a (patch) | |
| tree | d4c8d975a1d6c09bb96524ea04d4bbbdc15fb01c | |
| parent | 7dae18c39e99813461db6ef3fc13f077452109ce (diff) | |
| download | linux-next-4e42c398af136d62fac8c19c9f410ec83a8dd34a.tar.gz linux-next-4e42c398af136d62fac8c19c9f410ec83a8dd34a.zip | |
cxl/port: Bound switch decoder target array access by nr_targets
Switch decoder target lookups walk the target array using interleave
ways as the bound. The array is allocated with nr_targets entries, so a
decoder whose interleave ways exceeds nr_targets reads and writes past
the end of it. That happens during dport activation, during decoder
target population, and when userspace reads the target_list attribute.
Bounding by nr_targets alone is not correct either. Commit d6488fee6647
("cxl/port: Fix decoder initialization when nr_targets > interleave_ways")
moved target population to interleave ways precisely so that targets a
decoder does not use are left unpopulated. Neither field alone is a safe
bound, so add a helper that returns the minimum of the two and use it at
every walk of the target array.
Valid configurations keep interleave ways within nr_targets, so this is
not a behavior change for them.
Fixes: 40ba17afdfab ("cxl/acpi: Introduce cxl_decoder objects")
Fixes: d6488fee6647 ("cxl/port: Fix decoder initialization when nr_targets > interleave_ways")
Signed-off-by: Alison Schofield <alison.schofield@intel.com>
Reviewed-by: Li Ming <ming.li@zohomail.com>
Link: https://patch.msgid.link/d7577652cc6d414e1de0ee4045d4d6b2808172ce.1786143520.git.alison.schofield@intel.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
| -rw-r--r-- | drivers/cxl/core/port.c | 25 |
1 files changed, 18 insertions, 7 deletions
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c index 04a000f6bf89..5baa367e7913 100644 --- a/drivers/cxl/core/port.c +++ b/drivers/cxl/core/port.c @@ -148,20 +148,29 @@ static ssize_t target_type_show(struct device *dev, } static DEVICE_ATTR_RO(target_type); +/* + * Interleave ways selects how many targets a decoder uses, but the target + * array is only nr_targets long. Bound array access by both. + */ +static int cxlsd_nr_used_targets(struct cxl_switch_decoder *cxlsd) +{ + return min(cxlsd->cxld.interleave_ways, cxlsd->nr_targets); +} + static ssize_t emit_target_list(struct cxl_switch_decoder *cxlsd, char *buf) { - struct cxl_decoder *cxld = &cxlsd->cxld; + int nr_used = cxlsd_nr_used_targets(cxlsd); ssize_t offset = 0; int i, rc = 0; - for (i = 0; i < cxld->interleave_ways; i++) { + for (i = 0; i < nr_used; i++) { struct cxl_dport *dport = cxlsd->target[i]; struct cxl_dport *next = NULL; if (!dport) break; - if (i + 1 < cxld->interleave_ways) + if (i + 1 < nr_used) next = cxlsd->target[i + 1]; rc = sysfs_emit_at(buf, offset, "%d%s", dport->port_id, next ? "," : ""); @@ -1611,7 +1620,7 @@ static int update_decoder_targets(struct device *dev, void *data) struct cxl_dport *dport = data; struct cxl_switch_decoder *cxlsd; struct cxl_decoder *cxld; - int i; + int i, nr_used; if (!is_switch_decoder(dev)) return 0; @@ -1619,8 +1628,9 @@ static int update_decoder_targets(struct device *dev, void *data) cxlsd = to_cxl_switch_decoder(dev); cxld = &cxlsd->cxld; guard(rwsem_write)(&cxl_rwsem.region); + nr_used = cxlsd_nr_used_targets(cxlsd); - for (i = 0; i < cxld->interleave_ways; i++) { + for (i = 0; i < nr_used; i++) { if (cxld->target_map[i] == dport->port_id) { cxlsd->target[i] = dport; dev_dbg(dev, "dport%d found in target list, index %d\n", @@ -1918,7 +1928,7 @@ static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd, struct cxl_port *port) { struct cxl_decoder *cxld = &cxlsd->cxld; - int i; + int i, nr_used; device_lock_assert(&port->dev); @@ -1926,7 +1936,8 @@ static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd, return 0; guard(rwsem_write)(&cxl_rwsem.region); - for (i = 0; i < cxlsd->cxld.interleave_ways; i++) { + nr_used = cxlsd_nr_used_targets(cxlsd); + for (i = 0; i < nr_used; i++) { struct cxl_dport *dport = find_dport(port, cxld->target_map[i]); if (!dport) { |
