Skip to content

Commit 71ee71d

Browse files
stellarhopperdjbw
authored andcommitted
cxl/region: Fix decoder allocation crash
When an intermediate port's decoders have been exhausted by existing regions, and creating a new region with the port in question in it's hierarchical path is attempted, cxl_port_attach_region() fails to find a port decoder (as would be expected), and drops into the failure / cleanup path. However, during cleanup of the region reference, a sanity check attempts to dereference the decoder, which in the above case didn't exist. This causes a NULL pointer dereference BUG. To fix this, refactor the decoder allocation and de-allocation into helper routines, and in this 'free' routine, check that the decoder, @cxld, is valid before attempting any operations on it. Cc: <[email protected]> Suggested-by: Dan Williams <[email protected]> Signed-off-by: Vishal Verma <[email protected]> Reviewed-by: Dave Jiang <[email protected]> Fixes: 384e624 ("cxl/region: Attach endpoint decoders") Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Dan Williams <[email protected]>
1 parent 24f0692 commit 71ee71d

File tree

1 file changed

+41
-26
lines changed

1 file changed

+41
-26
lines changed

drivers/cxl/core/region.c

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -687,18 +687,27 @@ static struct cxl_region_ref *alloc_region_ref(struct cxl_port *port,
687687
return cxl_rr;
688688
}
689689

690-
static void free_region_ref(struct cxl_region_ref *cxl_rr)
690+
static void cxl_rr_free_decoder(struct cxl_region_ref *cxl_rr)
691691
{
692-
struct cxl_port *port = cxl_rr->port;
693692
struct cxl_region *cxlr = cxl_rr->region;
694693
struct cxl_decoder *cxld = cxl_rr->decoder;
695694

695+
if (!cxld)
696+
return;
697+
696698
dev_WARN_ONCE(&cxlr->dev, cxld->region != cxlr, "region mismatch\n");
697699
if (cxld->region == cxlr) {
698700
cxld->region = NULL;
699701
put_device(&cxlr->dev);
700702
}
703+
}
701704

705+
static void free_region_ref(struct cxl_region_ref *cxl_rr)
706+
{
707+
struct cxl_port *port = cxl_rr->port;
708+
struct cxl_region *cxlr = cxl_rr->region;
709+
710+
cxl_rr_free_decoder(cxl_rr);
702711
xa_erase(&port->regions, (unsigned long)cxlr);
703712
xa_destroy(&cxl_rr->endpoints);
704713
kfree(cxl_rr);
@@ -729,6 +738,33 @@ static int cxl_rr_ep_add(struct cxl_region_ref *cxl_rr,
729738
return 0;
730739
}
731740

741+
static int cxl_rr_alloc_decoder(struct cxl_port *port, struct cxl_region *cxlr,
742+
struct cxl_endpoint_decoder *cxled,
743+
struct cxl_region_ref *cxl_rr)
744+
{
745+
struct cxl_decoder *cxld;
746+
747+
if (port == cxled_to_port(cxled))
748+
cxld = &cxled->cxld;
749+
else
750+
cxld = cxl_region_find_decoder(port, cxlr);
751+
if (!cxld) {
752+
dev_dbg(&cxlr->dev, "%s: no decoder available\n",
753+
dev_name(&port->dev));
754+
return -EBUSY;
755+
}
756+
757+
if (cxld->region) {
758+
dev_dbg(&cxlr->dev, "%s: %s already attached to %s\n",
759+
dev_name(&port->dev), dev_name(&cxld->dev),
760+
dev_name(&cxld->region->dev));
761+
return -EBUSY;
762+
}
763+
764+
cxl_rr->decoder = cxld;
765+
return 0;
766+
}
767+
732768
/**
733769
* cxl_port_attach_region() - track a region's interest in a port by endpoint
734770
* @port: port to add a new region reference 'struct cxl_region_ref'
@@ -795,12 +831,6 @@ static int cxl_port_attach_region(struct cxl_port *port,
795831
cxl_rr->nr_targets++;
796832
nr_targets_inc = true;
797833
}
798-
799-
/*
800-
* The decoder for @cxlr was allocated when the region was first
801-
* attached to @port.
802-
*/
803-
cxld = cxl_rr->decoder;
804834
} else {
805835
cxl_rr = alloc_region_ref(port, cxlr);
806836
if (IS_ERR(cxl_rr)) {
@@ -811,26 +841,11 @@ static int cxl_port_attach_region(struct cxl_port *port,
811841
}
812842
nr_targets_inc = true;
813843

814-
if (port == cxled_to_port(cxled))
815-
cxld = &cxled->cxld;
816-
else
817-
cxld = cxl_region_find_decoder(port, cxlr);
818-
if (!cxld) {
819-
dev_dbg(&cxlr->dev, "%s: no decoder available\n",
820-
dev_name(&port->dev));
821-
goto out_erase;
822-
}
823-
824-
if (cxld->region) {
825-
dev_dbg(&cxlr->dev, "%s: %s already attached to %s\n",
826-
dev_name(&port->dev), dev_name(&cxld->dev),
827-
dev_name(&cxld->region->dev));
828-
rc = -EBUSY;
844+
rc = cxl_rr_alloc_decoder(port, cxlr, cxled, cxl_rr);
845+
if (rc)
829846
goto out_erase;
830-
}
831-
832-
cxl_rr->decoder = cxld;
833847
}
848+
cxld = cxl_rr->decoder;
834849

835850
rc = cxl_rr_ep_add(cxl_rr, cxled);
836851
if (rc) {

0 commit comments

Comments
 (0)