Skip to content

Commit 719f678

Browse files
kuba-moogregkh
authored andcommitted
net: shaper: protect from late creation of hierarchy
[ Upstream commit d75ec7e8ba1979a1eb0b9211d94d749cdce849c8 ] We look up a netdev during prep of Netlink ops (pre- callbacks) and take a ref to it. Then later in the body of the callback we take its lock or RCU which are the actual protections. The netdev may get unregistered in between the time we take the ref and the time we lock it. We may allocate the hierarchy after flush has already run, which would lead to a leak. Take the instance lock in pre- already, this saves us from the race and removes the need for dedicated lock/unlock callbacks completely. After all, if there's any chance of write happening concurrently with the flush - we're back to leaking the hierarchy. We may take the lock for devices which don't support shapers but we're only dealing with SET operations here, not taking the lock would be optimizing for an error case. Fixes: 93954b4 ("net-shapers: implement NL set and delete operations") Link: https://lore.kernel.org/20260309173450.538026-1-p@1g4.org Signed-off-by: Jakub Kicinski <kuba@kernel.org> Link: https://patch.msgid.link/20260317161014.779569-2-kuba@kernel.org Signed-off-by: Paolo Abeni <pabeni@redhat.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 581eee0 commit 719f678

4 files changed

Lines changed: 89 additions & 74 deletions

File tree

Documentation/netlink/specs/net_shaper.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,8 @@ operations:
247247
flags: [admin-perm]
248248

249249
do:
250-
pre: net-shaper-nl-pre-doit
251-
post: net-shaper-nl-post-doit
250+
pre: net-shaper-nl-pre-doit-write
251+
post: net-shaper-nl-post-doit-write
252252
request:
253253
attributes:
254254
- ifindex
@@ -278,8 +278,8 @@ operations:
278278
flags: [admin-perm]
279279

280280
do:
281-
pre: net-shaper-nl-pre-doit
282-
post: net-shaper-nl-post-doit
281+
pre: net-shaper-nl-pre-doit-write
282+
post: net-shaper-nl-post-doit-write
283283
request:
284284
attributes: *ns-binding
285285

@@ -309,8 +309,8 @@ operations:
309309
flags: [admin-perm]
310310

311311
do:
312-
pre: net-shaper-nl-pre-doit
313-
post: net-shaper-nl-post-doit
312+
pre: net-shaper-nl-pre-doit-write
313+
post: net-shaper-nl-post-doit-write
314314
request:
315315
attributes:
316316
- ifindex

net/shaper/shaper.c

Lines changed: 72 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -36,24 +36,6 @@ static struct net_shaper_binding *net_shaper_binding_from_ctx(void *ctx)
3636
return &((struct net_shaper_nl_ctx *)ctx)->binding;
3737
}
3838

39-
static void net_shaper_lock(struct net_shaper_binding *binding)
40-
{
41-
switch (binding->type) {
42-
case NET_SHAPER_BINDING_TYPE_NETDEV:
43-
netdev_lock(binding->netdev);
44-
break;
45-
}
46-
}
47-
48-
static void net_shaper_unlock(struct net_shaper_binding *binding)
49-
{
50-
switch (binding->type) {
51-
case NET_SHAPER_BINDING_TYPE_NETDEV:
52-
netdev_unlock(binding->netdev);
53-
break;
54-
}
55-
}
56-
5739
static struct net_shaper_hierarchy *
5840
net_shaper_hierarchy(struct net_shaper_binding *binding)
5941
{
@@ -219,12 +201,49 @@ static int net_shaper_ctx_setup(const struct genl_info *info, int type,
219201
return 0;
220202
}
221203

204+
/* Like net_shaper_ctx_setup(), but for "write" handlers (never for dumps!)
205+
* Acquires the lock protecting the hierarchy (instance lock for netdev).
206+
*/
207+
static int net_shaper_ctx_setup_lock(const struct genl_info *info, int type,
208+
struct net_shaper_nl_ctx *ctx)
209+
{
210+
struct net *ns = genl_info_net(info);
211+
struct net_device *dev;
212+
int ifindex;
213+
214+
if (GENL_REQ_ATTR_CHECK(info, type))
215+
return -EINVAL;
216+
217+
ifindex = nla_get_u32(info->attrs[type]);
218+
dev = netdev_get_by_index_lock(ns, ifindex);
219+
if (!dev) {
220+
NL_SET_BAD_ATTR(info->extack, info->attrs[type]);
221+
return -ENOENT;
222+
}
223+
224+
if (!dev->netdev_ops->net_shaper_ops) {
225+
NL_SET_BAD_ATTR(info->extack, info->attrs[type]);
226+
netdev_unlock(dev);
227+
return -EOPNOTSUPP;
228+
}
229+
230+
ctx->binding.type = NET_SHAPER_BINDING_TYPE_NETDEV;
231+
ctx->binding.netdev = dev;
232+
return 0;
233+
}
234+
222235
static void net_shaper_ctx_cleanup(struct net_shaper_nl_ctx *ctx)
223236
{
224237
if (ctx->binding.type == NET_SHAPER_BINDING_TYPE_NETDEV)
225238
netdev_put(ctx->binding.netdev, &ctx->dev_tracker);
226239
}
227240

241+
static void net_shaper_ctx_cleanup_unlock(struct net_shaper_nl_ctx *ctx)
242+
{
243+
if (ctx->binding.type == NET_SHAPER_BINDING_TYPE_NETDEV)
244+
netdev_unlock(ctx->binding.netdev);
245+
}
246+
228247
static u32 net_shaper_handle_to_index(const struct net_shaper_handle *handle)
229248
{
230249
return FIELD_PREP(NET_SHAPER_SCOPE_MASK, handle->scope) |
@@ -278,7 +297,7 @@ net_shaper_lookup(struct net_shaper_binding *binding,
278297
}
279298

280299
/* Allocate on demand the per device shaper's hierarchy container.
281-
* Called under the net shaper lock
300+
* Called under the lock protecting the hierarchy (instance lock for netdev)
282301
*/
283302
static struct net_shaper_hierarchy *
284303
net_shaper_hierarchy_setup(struct net_shaper_binding *binding)
@@ -697,6 +716,22 @@ void net_shaper_nl_post_doit(const struct genl_split_ops *ops,
697716
net_shaper_generic_post(info);
698717
}
699718

719+
int net_shaper_nl_pre_doit_write(const struct genl_split_ops *ops,
720+
struct sk_buff *skb, struct genl_info *info)
721+
{
722+
struct net_shaper_nl_ctx *ctx = (struct net_shaper_nl_ctx *)info->ctx;
723+
724+
BUILD_BUG_ON(sizeof(*ctx) > sizeof(info->ctx));
725+
726+
return net_shaper_ctx_setup_lock(info, NET_SHAPER_A_IFINDEX, ctx);
727+
}
728+
729+
void net_shaper_nl_post_doit_write(const struct genl_split_ops *ops,
730+
struct sk_buff *skb, struct genl_info *info)
731+
{
732+
net_shaper_ctx_cleanup_unlock((struct net_shaper_nl_ctx *)info->ctx);
733+
}
734+
700735
int net_shaper_nl_pre_dumpit(struct netlink_callback *cb)
701736
{
702737
struct net_shaper_nl_ctx *ctx = (struct net_shaper_nl_ctx *)cb->ctx;
@@ -824,45 +859,38 @@ int net_shaper_nl_set_doit(struct sk_buff *skb, struct genl_info *info)
824859

825860
binding = net_shaper_binding_from_ctx(info->ctx);
826861

827-
net_shaper_lock(binding);
828862
ret = net_shaper_parse_info(binding, info->attrs, info, &shaper,
829863
&exists);
830864
if (ret)
831-
goto unlock;
865+
return ret;
832866

833867
if (!exists)
834868
net_shaper_default_parent(&shaper.handle, &shaper.parent);
835869

836870
hierarchy = net_shaper_hierarchy_setup(binding);
837-
if (!hierarchy) {
838-
ret = -ENOMEM;
839-
goto unlock;
840-
}
871+
if (!hierarchy)
872+
return -ENOMEM;
841873

842874
/* The 'set' operation can't create node-scope shapers. */
843875
handle = shaper.handle;
844876
if (handle.scope == NET_SHAPER_SCOPE_NODE &&
845-
!net_shaper_lookup(binding, &handle)) {
846-
ret = -ENOENT;
847-
goto unlock;
848-
}
877+
!net_shaper_lookup(binding, &handle))
878+
return -ENOENT;
849879

850880
ret = net_shaper_pre_insert(binding, &handle, info->extack);
851881
if (ret)
852-
goto unlock;
882+
return ret;
853883

854884
ops = net_shaper_ops(binding);
855885
ret = ops->set(binding, &shaper, info->extack);
856886
if (ret) {
857887
net_shaper_rollback(binding);
858-
goto unlock;
888+
return ret;
859889
}
860890

861891
net_shaper_commit(binding, 1, &shaper);
862892

863-
unlock:
864-
net_shaper_unlock(binding);
865-
return ret;
893+
return 0;
866894
}
867895

868896
static int __net_shaper_delete(struct net_shaper_binding *binding,
@@ -1091,35 +1119,26 @@ int net_shaper_nl_delete_doit(struct sk_buff *skb, struct genl_info *info)
10911119

10921120
binding = net_shaper_binding_from_ctx(info->ctx);
10931121

1094-
net_shaper_lock(binding);
10951122
ret = net_shaper_parse_handle(info->attrs[NET_SHAPER_A_HANDLE], info,
10961123
&handle);
10971124
if (ret)
1098-
goto unlock;
1125+
return ret;
10991126

11001127
hierarchy = net_shaper_hierarchy(binding);
1101-
if (!hierarchy) {
1102-
ret = -ENOENT;
1103-
goto unlock;
1104-
}
1128+
if (!hierarchy)
1129+
return -ENOENT;
11051130

11061131
shaper = net_shaper_lookup(binding, &handle);
1107-
if (!shaper) {
1108-
ret = -ENOENT;
1109-
goto unlock;
1110-
}
1132+
if (!shaper)
1133+
return -ENOENT;
11111134

11121135
if (handle.scope == NET_SHAPER_SCOPE_NODE) {
11131136
ret = net_shaper_pre_del_node(binding, shaper, info->extack);
11141137
if (ret)
1115-
goto unlock;
1138+
return ret;
11161139
}
11171140

1118-
ret = __net_shaper_delete(binding, shaper, info->extack);
1119-
1120-
unlock:
1121-
net_shaper_unlock(binding);
1122-
return ret;
1141+
return __net_shaper_delete(binding, shaper, info->extack);
11231142
}
11241143

11251144
static int net_shaper_group_send_reply(struct net_shaper_binding *binding,
@@ -1168,21 +1187,17 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info)
11681187
if (!net_shaper_ops(binding)->group)
11691188
return -EOPNOTSUPP;
11701189

1171-
net_shaper_lock(binding);
11721190
leaves_count = net_shaper_list_len(info, NET_SHAPER_A_LEAVES);
11731191
if (!leaves_count) {
11741192
NL_SET_BAD_ATTR(info->extack,
11751193
info->attrs[NET_SHAPER_A_LEAVES]);
1176-
ret = -EINVAL;
1177-
goto unlock;
1194+
return -EINVAL;
11781195
}
11791196

11801197
leaves = kcalloc(leaves_count, sizeof(struct net_shaper) +
11811198
sizeof(struct net_shaper *), GFP_KERNEL);
1182-
if (!leaves) {
1183-
ret = -ENOMEM;
1184-
goto unlock;
1185-
}
1199+
if (!leaves)
1200+
return -ENOMEM;
11861201
old_nodes = (void *)&leaves[leaves_count];
11871202

11881203
ret = net_shaper_parse_node(binding, info->attrs, info, &node);
@@ -1259,9 +1274,6 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info)
12591274

12601275
free_leaves:
12611276
kfree(leaves);
1262-
1263-
unlock:
1264-
net_shaper_unlock(binding);
12651277
return ret;
12661278

12671279
free_msg:
@@ -1371,14 +1383,12 @@ static void net_shaper_flush(struct net_shaper_binding *binding)
13711383
if (!hierarchy)
13721384
return;
13731385

1374-
net_shaper_lock(binding);
13751386
xa_lock(&hierarchy->shapers);
13761387
xa_for_each(&hierarchy->shapers, index, cur) {
13771388
__xa_erase(&hierarchy->shapers, index);
13781389
kfree(cur);
13791390
}
13801391
xa_unlock(&hierarchy->shapers);
1381-
net_shaper_unlock(binding);
13821392

13831393
kfree(hierarchy);
13841394
}

net/shaper/shaper_nl_gen.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,27 +98,27 @@ static const struct genl_split_ops net_shaper_nl_ops[] = {
9898
},
9999
{
100100
.cmd = NET_SHAPER_CMD_SET,
101-
.pre_doit = net_shaper_nl_pre_doit,
101+
.pre_doit = net_shaper_nl_pre_doit_write,
102102
.doit = net_shaper_nl_set_doit,
103-
.post_doit = net_shaper_nl_post_doit,
103+
.post_doit = net_shaper_nl_post_doit_write,
104104
.policy = net_shaper_set_nl_policy,
105105
.maxattr = NET_SHAPER_A_IFINDEX,
106106
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
107107
},
108108
{
109109
.cmd = NET_SHAPER_CMD_DELETE,
110-
.pre_doit = net_shaper_nl_pre_doit,
110+
.pre_doit = net_shaper_nl_pre_doit_write,
111111
.doit = net_shaper_nl_delete_doit,
112-
.post_doit = net_shaper_nl_post_doit,
112+
.post_doit = net_shaper_nl_post_doit_write,
113113
.policy = net_shaper_delete_nl_policy,
114114
.maxattr = NET_SHAPER_A_IFINDEX,
115115
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
116116
},
117117
{
118118
.cmd = NET_SHAPER_CMD_GROUP,
119-
.pre_doit = net_shaper_nl_pre_doit,
119+
.pre_doit = net_shaper_nl_pre_doit_write,
120120
.doit = net_shaper_nl_group_doit,
121-
.post_doit = net_shaper_nl_post_doit,
121+
.post_doit = net_shaper_nl_post_doit_write,
122122
.policy = net_shaper_group_nl_policy,
123123
.maxattr = NET_SHAPER_A_LEAVES,
124124
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,

net/shaper/shaper_nl_gen.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,17 @@ extern const struct nla_policy net_shaper_leaf_info_nl_policy[NET_SHAPER_A_WEIGH
1717

1818
int net_shaper_nl_pre_doit(const struct genl_split_ops *ops,
1919
struct sk_buff *skb, struct genl_info *info);
20+
int net_shaper_nl_pre_doit_write(const struct genl_split_ops *ops,
21+
struct sk_buff *skb, struct genl_info *info);
2022
int net_shaper_nl_cap_pre_doit(const struct genl_split_ops *ops,
2123
struct sk_buff *skb, struct genl_info *info);
2224
void
2325
net_shaper_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
2426
struct genl_info *info);
2527
void
28+
net_shaper_nl_post_doit_write(const struct genl_split_ops *ops,
29+
struct sk_buff *skb, struct genl_info *info);
30+
void
2631
net_shaper_nl_cap_post_doit(const struct genl_split_ops *ops,
2732
struct sk_buff *skb, struct genl_info *info);
2833
int net_shaper_nl_pre_dumpit(struct netlink_callback *cb);

0 commit comments

Comments
 (0)