Skip to content

Commit d75ec7e

Browse files
kuba-mooPaolo Abeni
authored andcommitted
net: shaper: protect from late creation of hierarchy
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/[email protected] Signed-off-by: Jakub Kicinski <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
1 parent 0f9ea71 commit d75ec7e

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,
@@ -1090,35 +1118,26 @@ int net_shaper_nl_delete_doit(struct sk_buff *skb, struct genl_info *info)
10901118

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

1093-
net_shaper_lock(binding);
10941121
ret = net_shaper_parse_handle(info->attrs[NET_SHAPER_A_HANDLE], info,
10951122
&handle);
10961123
if (ret)
1097-
goto unlock;
1124+
return ret;
10981125

10991126
hierarchy = net_shaper_hierarchy(binding);
1100-
if (!hierarchy) {
1101-
ret = -ENOENT;
1102-
goto unlock;
1103-
}
1127+
if (!hierarchy)
1128+
return -ENOENT;
11041129

11051130
shaper = net_shaper_lookup(binding, &handle);
1106-
if (!shaper) {
1107-
ret = -ENOENT;
1108-
goto unlock;
1109-
}
1131+
if (!shaper)
1132+
return -ENOENT;
11101133

11111134
if (handle.scope == NET_SHAPER_SCOPE_NODE) {
11121135
ret = net_shaper_pre_del_node(binding, shaper, info->extack);
11131136
if (ret)
1114-
goto unlock;
1137+
return ret;
11151138
}
11161139

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

11241143
static int net_shaper_group_send_reply(struct net_shaper_binding *binding,
@@ -1167,21 +1186,17 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info)
11671186
if (!net_shaper_ops(binding)->group)
11681187
return -EOPNOTSUPP;
11691188

1170-
net_shaper_lock(binding);
11711189
leaves_count = net_shaper_list_len(info, NET_SHAPER_A_LEAVES);
11721190
if (!leaves_count) {
11731191
NL_SET_BAD_ATTR(info->extack,
11741192
info->attrs[NET_SHAPER_A_LEAVES]);
1175-
ret = -EINVAL;
1176-
goto unlock;
1193+
return -EINVAL;
11771194
}
11781195

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

11871202
ret = net_shaper_parse_node(binding, info->attrs, info, &node);
@@ -1258,9 +1273,6 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info)
12581273

12591274
free_leaves:
12601275
kfree(leaves);
1261-
1262-
unlock:
1263-
net_shaper_unlock(binding);
12641276
return ret;
12651277

12661278
free_msg:
@@ -1370,14 +1382,12 @@ static void net_shaper_flush(struct net_shaper_binding *binding)
13701382
if (!hierarchy)
13711383
return;
13721384

1373-
net_shaper_lock(binding);
13741385
xa_lock(&hierarchy->shapers);
13751386
xa_for_each(&hierarchy->shapers, index, cur) {
13761387
__xa_erase(&hierarchy->shapers, index);
13771388
kfree(cur);
13781389
}
13791390
xa_unlock(&hierarchy->shapers);
1380-
net_shaper_unlock(binding);
13811391

13821392
kfree(hierarchy);
13831393
}

net/shaper/shaper_nl_gen.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,27 +99,27 @@ static const struct genl_split_ops net_shaper_nl_ops[] = {
9999
},
100100
{
101101
.cmd = NET_SHAPER_CMD_SET,
102-
.pre_doit = net_shaper_nl_pre_doit,
102+
.pre_doit = net_shaper_nl_pre_doit_write,
103103
.doit = net_shaper_nl_set_doit,
104-
.post_doit = net_shaper_nl_post_doit,
104+
.post_doit = net_shaper_nl_post_doit_write,
105105
.policy = net_shaper_set_nl_policy,
106106
.maxattr = NET_SHAPER_A_IFINDEX,
107107
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
108108
},
109109
{
110110
.cmd = NET_SHAPER_CMD_DELETE,
111-
.pre_doit = net_shaper_nl_pre_doit,
111+
.pre_doit = net_shaper_nl_pre_doit_write,
112112
.doit = net_shaper_nl_delete_doit,
113-
.post_doit = net_shaper_nl_post_doit,
113+
.post_doit = net_shaper_nl_post_doit_write,
114114
.policy = net_shaper_delete_nl_policy,
115115
.maxattr = NET_SHAPER_A_IFINDEX,
116116
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
117117
},
118118
{
119119
.cmd = NET_SHAPER_CMD_GROUP,
120-
.pre_doit = net_shaper_nl_pre_doit,
120+
.pre_doit = net_shaper_nl_pre_doit_write,
121121
.doit = net_shaper_nl_group_doit,
122-
.post_doit = net_shaper_nl_post_doit,
122+
.post_doit = net_shaper_nl_post_doit_write,
123123
.policy = net_shaper_group_nl_policy,
124124
.maxattr = NET_SHAPER_A_LEAVES,
125125
.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
@@ -18,12 +18,17 @@ extern const struct nla_policy net_shaper_leaf_info_nl_policy[NET_SHAPER_A_WEIGH
1818

1919
int net_shaper_nl_pre_doit(const struct genl_split_ops *ops,
2020
struct sk_buff *skb, struct genl_info *info);
21+
int net_shaper_nl_pre_doit_write(const struct genl_split_ops *ops,
22+
struct sk_buff *skb, struct genl_info *info);
2123
int net_shaper_nl_cap_pre_doit(const struct genl_split_ops *ops,
2224
struct sk_buff *skb, struct genl_info *info);
2325
void
2426
net_shaper_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb,
2527
struct genl_info *info);
2628
void
29+
net_shaper_nl_post_doit_write(const struct genl_split_ops *ops,
30+
struct sk_buff *skb, struct genl_info *info);
31+
void
2732
net_shaper_nl_cap_post_doit(const struct genl_split_ops *ops,
2833
struct sk_buff *skb, struct genl_info *info);
2934
int net_shaper_nl_pre_dumpit(struct netlink_callback *cb);

0 commit comments

Comments
 (0)