Skip to content

Commit 3249eae

Browse files
committed
net: ethtool: fix leaking netdev ref if ethnl_default_parse() failed
Ido spotted that I made a mistake in commit under Fixes, ethnl_default_parse() may acquire a dev reference even when it returns an error. This may have been driven by the code structure in dumps (which unconditionally release dev before handling errors), but it's too much of a trap. Functions should undo what they did before returning an error, rather than expecting caller to clean up. Rather than fixing ethnl_default_set_doit() directly make ethnl_default_parse() clean up errors. Reported-by: Ido Schimmel <[email protected]> Link: https://lore.kernel.org/aGEPszpq9eojNF4Y@shredder Fixes: 963781b ("net: ethtool: call .parse_request for SET handlers") Reviewed-by: Ido Schimmel <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent ca89962 commit 3249eae

File tree

1 file changed

+12
-6
lines changed

1 file changed

+12
-6
lines changed

net/ethtool/netlink.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -455,10 +455,15 @@ static int ethnl_default_parse(struct ethnl_req_info *req_info,
455455
if (request_ops->parse_request) {
456456
ret = request_ops->parse_request(req_info, tb, info->extack);
457457
if (ret < 0)
458-
return ret;
458+
goto err_dev;
459459
}
460460

461461
return 0;
462+
463+
err_dev:
464+
netdev_put(req_info->dev, &req_info->dev_tracker);
465+
req_info->dev = NULL;
466+
return ret;
462467
}
463468

464469
/**
@@ -508,7 +513,7 @@ static int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info)
508513

509514
ret = ethnl_default_parse(req_info, info, ops, !ops->allow_nodev_do);
510515
if (ret < 0)
511-
goto err_dev;
516+
goto err_free;
512517
ethnl_init_reply_data(reply_data, ops, req_info->dev);
513518

514519
rtnl_lock();
@@ -554,6 +559,7 @@ static int ethnl_default_doit(struct sk_buff *skb, struct genl_info *info)
554559
ops->cleanup_data(reply_data);
555560
err_dev:
556561
netdev_put(req_info->dev, &req_info->dev_tracker);
562+
err_free:
557563
kfree(reply_data);
558564
kfree(req_info);
559565
return ret;
@@ -656,6 +662,8 @@ static int ethnl_default_start(struct netlink_callback *cb)
656662
}
657663

658664
ret = ethnl_default_parse(req_info, &info->info, ops, false);
665+
if (ret < 0)
666+
goto free_reply_data;
659667
if (req_info->dev) {
660668
/* We ignore device specification in dump requests but as the
661669
* same parser as for non-dump (doit) requests is used, it
@@ -664,8 +672,6 @@ static int ethnl_default_start(struct netlink_callback *cb)
664672
netdev_put(req_info->dev, &req_info->dev_tracker);
665673
req_info->dev = NULL;
666674
}
667-
if (ret < 0)
668-
goto free_reply_data;
669675

670676
ctx->ops = ops;
671677
ctx->req_info = req_info;
@@ -714,13 +720,13 @@ static int ethnl_perphy_start(struct netlink_callback *cb)
714720
* the dev's ifindex, .dumpit() will grab and release the netdev itself.
715721
*/
716722
ret = ethnl_default_parse(req_info, &info->info, ops, false);
723+
if (ret < 0)
724+
goto free_reply_data;
717725
if (req_info->dev) {
718726
phy_ctx->ifindex = req_info->dev->ifindex;
719727
netdev_put(req_info->dev, &req_info->dev_tracker);
720728
req_info->dev = NULL;
721729
}
722-
if (ret < 0)
723-
goto free_reply_data;
724730

725731
ctx->ops = ops;
726732
ctx->req_info = req_info;

0 commit comments

Comments
 (0)