Skip to content

Commit e0c1623

Browse files
Dan Carpenterkuba-moo
authored andcommitted
net: mscc: ocelot: fix error handling bugs in mscc_ocelot_init_ports()
There are several error handling bugs in mscc_ocelot_init_ports(). I went through the code, and carefully audited it and made fixes and cleanups. 1) The ocelot_probe_port() function didn't have a mirror release function so it was hard to follow. I created the ocelot_release_port() function. 2) In the ocelot_probe_port() function, if the register_netdev() call failed, then it lead to a double free_netdev(dev) bug. Fix this by setting "ocelot->ports[port] = NULL" on the error path. 3) I was concerned that the "port" which comes from of_property_read_u32() might be out of bounds so I added a check for that. 4) In the original code if ocelot_regmap_init() failed then the driver tried to continue but I think that should be a fatal error. 5) If ocelot_probe_port() failed then the most recent devlink was leaked. The fix for mostly came Vladimir Oltean. Get rid of "registered_ports" and just set a bit in "devlink_ports_registered" to say when the devlink port has been registered (and needs to be unregistered on error). There are fewer than 32 ports so a u32 is large enough for this purpose. 6) The error handling if the final ocelot_port_devlink_init() failed had two problems. The "while (port-- >= 0)" loop should have been "--port" pre-op instead of a post-op to avoid a buffer underflow. The "if (!registered_ports[port])" condition was reversed leading to resource leaks and double frees. Fixes: 6c30384 ("net: mscc: ocelot: register devlink ports") Signed-off-by: Dan Carpenter <[email protected]> Reviewed-by: Vladimir Oltean <[email protected]> Tested-by: Vladimir Oltean <[email protected]> Link: https://lore.kernel.org/r/YBkXhqRxHtRGzSnJ@mwanda Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 2d912da commit e0c1623

File tree

3 files changed

+33
-34
lines changed

3 files changed

+33
-34
lines changed

drivers/net/ethernet/mscc/ocelot.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ void ocelot_port_writel(struct ocelot_port *port, u32 val, u32 reg);
121121

122122
int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target,
123123
struct phy_device *phy);
124+
void ocelot_release_port(struct ocelot_port *ocelot_port);
124125
int ocelot_devlink_init(struct ocelot *ocelot);
125126
void ocelot_devlink_teardown(struct ocelot *ocelot);
126127
int ocelot_port_devlink_init(struct ocelot *ocelot, int port,

drivers/net/ethernet/mscc/ocelot_net.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1283,7 +1283,19 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target,
12831283
if (err) {
12841284
dev_err(ocelot->dev, "register_netdev failed\n");
12851285
free_netdev(dev);
1286+
ocelot->ports[port] = NULL;
1287+
return err;
12861288
}
12871289

1288-
return err;
1290+
return 0;
1291+
}
1292+
1293+
void ocelot_release_port(struct ocelot_port *ocelot_port)
1294+
{
1295+
struct ocelot_port_private *priv = container_of(ocelot_port,
1296+
struct ocelot_port_private,
1297+
port);
1298+
1299+
unregister_netdev(priv->dev);
1300+
free_netdev(priv->dev);
12891301
}

drivers/net/ethernet/mscc/ocelot_vsc7514.c

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,29 +1064,23 @@ static void mscc_ocelot_release_ports(struct ocelot *ocelot)
10641064
int port;
10651065

10661066
for (port = 0; port < ocelot->num_phys_ports; port++) {
1067-
struct ocelot_port_private *priv;
10681067
struct ocelot_port *ocelot_port;
10691068

10701069
ocelot_port = ocelot->ports[port];
10711070
if (!ocelot_port)
10721071
continue;
10731072

10741073
ocelot_deinit_port(ocelot, port);
1075-
1076-
priv = container_of(ocelot_port, struct ocelot_port_private,
1077-
port);
1078-
1079-
unregister_netdev(priv->dev);
1080-
free_netdev(priv->dev);
1074+
ocelot_release_port(ocelot_port);
10811075
}
10821076
}
10831077

10841078
static int mscc_ocelot_init_ports(struct platform_device *pdev,
10851079
struct device_node *ports)
10861080
{
10871081
struct ocelot *ocelot = platform_get_drvdata(pdev);
1082+
u32 devlink_ports_registered = 0;
10881083
struct device_node *portnp;
1089-
bool *registered_ports;
10901084
int port, err;
10911085
u32 reg;
10921086

@@ -1102,11 +1096,6 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev,
11021096
if (!ocelot->devlink_ports)
11031097
return -ENOMEM;
11041098

1105-
registered_ports = kcalloc(ocelot->num_phys_ports, sizeof(bool),
1106-
GFP_KERNEL);
1107-
if (!registered_ports)
1108-
return -ENOMEM;
1109-
11101099
for_each_available_child_of_node(ports, portnp) {
11111100
struct ocelot_port_private *priv;
11121101
struct ocelot_port *ocelot_port;
@@ -1123,14 +1112,22 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev,
11231112
continue;
11241113

11251114
port = reg;
1115+
if (port < 0 || port >= ocelot->num_phys_ports) {
1116+
dev_err(ocelot->dev,
1117+
"invalid port number: %d >= %d\n", port,
1118+
ocelot->num_phys_ports);
1119+
continue;
1120+
}
11261121

11271122
snprintf(res_name, sizeof(res_name), "port%d", port);
11281123

11291124
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
11301125
res_name);
11311126
target = ocelot_regmap_init(ocelot, res);
1132-
if (IS_ERR(target))
1133-
continue;
1127+
if (IS_ERR(target)) {
1128+
err = PTR_ERR(target);
1129+
goto out_teardown;
1130+
}
11341131

11351132
phy_node = of_parse_phandle(portnp, "phy-handle", 0);
11361133
if (!phy_node)
@@ -1147,15 +1144,14 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev,
11471144
of_node_put(portnp);
11481145
goto out_teardown;
11491146
}
1147+
devlink_ports_registered |= BIT(port);
11501148

11511149
err = ocelot_probe_port(ocelot, port, target, phy);
11521150
if (err) {
11531151
of_node_put(portnp);
11541152
goto out_teardown;
11551153
}
11561154

1157-
registered_ports[port] = true;
1158-
11591155
ocelot_port = ocelot->ports[port];
11601156
priv = container_of(ocelot_port, struct ocelot_port_private,
11611157
port);
@@ -1208,23 +1204,16 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev,
12081204

12091205
/* Initialize unused devlink ports at the end */
12101206
for (port = 0; port < ocelot->num_phys_ports; port++) {
1211-
if (registered_ports[port])
1207+
if (devlink_ports_registered & BIT(port))
12121208
continue;
12131209

12141210
err = ocelot_port_devlink_init(ocelot, port,
12151211
DEVLINK_PORT_FLAVOUR_UNUSED);
1216-
if (err) {
1217-
while (port-- >= 0) {
1218-
if (!registered_ports[port])
1219-
continue;
1220-
ocelot_port_devlink_teardown(ocelot, port);
1221-
}
1222-
1212+
if (err)
12231213
goto out_teardown;
1224-
}
1225-
}
12261214

1227-
kfree(registered_ports);
1215+
devlink_ports_registered |= BIT(port);
1216+
}
12281217

12291218
return 0;
12301219

@@ -1233,12 +1222,9 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev,
12331222
mscc_ocelot_release_ports(ocelot);
12341223
/* Tear down devlink ports for the registered network interfaces */
12351224
for (port = 0; port < ocelot->num_phys_ports; port++) {
1236-
if (!registered_ports[port])
1237-
continue;
1238-
1239-
ocelot_port_devlink_teardown(ocelot, port);
1225+
if (devlink_ports_registered & BIT(port))
1226+
ocelot_port_devlink_teardown(ocelot, port);
12401227
}
1241-
kfree(registered_ports);
12421228
return err;
12431229
}
12441230

0 commit comments

Comments
 (0)