Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 93954b40f6a4 ("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>

authored by

Jakub Kicinski and committed by
Paolo Abeni
d75ec7e8 0f9ea714

+89 -74
+6 -6
Documentation/netlink/specs/net_shaper.yaml
··· 247 247 flags: [admin-perm] 248 248 249 249 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 252 252 request: 253 253 attributes: 254 254 - ifindex ··· 278 278 flags: [admin-perm] 279 279 280 280 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 283 283 request: 284 284 attributes: *ns-binding 285 285 ··· 309 309 flags: [admin-perm] 310 310 311 311 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 314 314 request: 315 315 attributes: 316 316 - ifindex
+72 -62
net/shaper/shaper.c
··· 36 36 return &((struct net_shaper_nl_ctx *)ctx)->binding; 37 37 } 38 38 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 - 57 39 static struct net_shaper_hierarchy * 58 40 net_shaper_hierarchy(struct net_shaper_binding *binding) 59 41 { ··· 201 219 return 0; 202 220 } 203 221 222 + /* Like net_shaper_ctx_setup(), but for "write" handlers (never for dumps!) 223 + * Acquires the lock protecting the hierarchy (instance lock for netdev). 224 + */ 225 + static int net_shaper_ctx_setup_lock(const struct genl_info *info, int type, 226 + struct net_shaper_nl_ctx *ctx) 227 + { 228 + struct net *ns = genl_info_net(info); 229 + struct net_device *dev; 230 + int ifindex; 231 + 232 + if (GENL_REQ_ATTR_CHECK(info, type)) 233 + return -EINVAL; 234 + 235 + ifindex = nla_get_u32(info->attrs[type]); 236 + dev = netdev_get_by_index_lock(ns, ifindex); 237 + if (!dev) { 238 + NL_SET_BAD_ATTR(info->extack, info->attrs[type]); 239 + return -ENOENT; 240 + } 241 + 242 + if (!dev->netdev_ops->net_shaper_ops) { 243 + NL_SET_BAD_ATTR(info->extack, info->attrs[type]); 244 + netdev_unlock(dev); 245 + return -EOPNOTSUPP; 246 + } 247 + 248 + ctx->binding.type = NET_SHAPER_BINDING_TYPE_NETDEV; 249 + ctx->binding.netdev = dev; 250 + return 0; 251 + } 252 + 204 253 static void net_shaper_ctx_cleanup(struct net_shaper_nl_ctx *ctx) 205 254 { 206 255 if (ctx->binding.type == NET_SHAPER_BINDING_TYPE_NETDEV) 207 256 netdev_put(ctx->binding.netdev, &ctx->dev_tracker); 257 + } 258 + 259 + static void net_shaper_ctx_cleanup_unlock(struct net_shaper_nl_ctx *ctx) 260 + { 261 + if (ctx->binding.type == NET_SHAPER_BINDING_TYPE_NETDEV) 262 + netdev_unlock(ctx->binding.netdev); 208 263 } 209 264 210 265 static u32 net_shaper_handle_to_index(const struct net_shaper_handle *handle) ··· 297 278 } 298 279 299 280 /* Allocate on demand the per device shaper's hierarchy container. 300 - * Called under the net shaper lock 281 + * Called under the lock protecting the hierarchy (instance lock for netdev) 301 282 */ 302 283 static struct net_shaper_hierarchy * 303 284 net_shaper_hierarchy_setup(struct net_shaper_binding *binding) ··· 716 697 net_shaper_generic_post(info); 717 698 } 718 699 700 + int net_shaper_nl_pre_doit_write(const struct genl_split_ops *ops, 701 + struct sk_buff *skb, struct genl_info *info) 702 + { 703 + struct net_shaper_nl_ctx *ctx = (struct net_shaper_nl_ctx *)info->ctx; 704 + 705 + BUILD_BUG_ON(sizeof(*ctx) > sizeof(info->ctx)); 706 + 707 + return net_shaper_ctx_setup_lock(info, NET_SHAPER_A_IFINDEX, ctx); 708 + } 709 + 710 + void net_shaper_nl_post_doit_write(const struct genl_split_ops *ops, 711 + struct sk_buff *skb, struct genl_info *info) 712 + { 713 + net_shaper_ctx_cleanup_unlock((struct net_shaper_nl_ctx *)info->ctx); 714 + } 715 + 719 716 int net_shaper_nl_pre_dumpit(struct netlink_callback *cb) 720 717 { 721 718 struct net_shaper_nl_ctx *ctx = (struct net_shaper_nl_ctx *)cb->ctx; ··· 859 824 860 825 binding = net_shaper_binding_from_ctx(info->ctx); 861 826 862 - net_shaper_lock(binding); 863 827 ret = net_shaper_parse_info(binding, info->attrs, info, &shaper, 864 828 &exists); 865 829 if (ret) 866 - goto unlock; 830 + return ret; 867 831 868 832 if (!exists) 869 833 net_shaper_default_parent(&shaper.handle, &shaper.parent); 870 834 871 835 hierarchy = net_shaper_hierarchy_setup(binding); 872 - if (!hierarchy) { 873 - ret = -ENOMEM; 874 - goto unlock; 875 - } 836 + if (!hierarchy) 837 + return -ENOMEM; 876 838 877 839 /* The 'set' operation can't create node-scope shapers. */ 878 840 handle = shaper.handle; 879 841 if (handle.scope == NET_SHAPER_SCOPE_NODE && 880 - !net_shaper_lookup(binding, &handle)) { 881 - ret = -ENOENT; 882 - goto unlock; 883 - } 842 + !net_shaper_lookup(binding, &handle)) 843 + return -ENOENT; 884 844 885 845 ret = net_shaper_pre_insert(binding, &handle, info->extack); 886 846 if (ret) 887 - goto unlock; 847 + return ret; 888 848 889 849 ops = net_shaper_ops(binding); 890 850 ret = ops->set(binding, &shaper, info->extack); 891 851 if (ret) { 892 852 net_shaper_rollback(binding); 893 - goto unlock; 853 + return ret; 894 854 } 895 855 896 856 net_shaper_commit(binding, 1, &shaper); 897 857 898 - unlock: 899 - net_shaper_unlock(binding); 900 - return ret; 858 + return 0; 901 859 } 902 860 903 861 static int __net_shaper_delete(struct net_shaper_binding *binding, ··· 1118 1090 1119 1091 binding = net_shaper_binding_from_ctx(info->ctx); 1120 1092 1121 - net_shaper_lock(binding); 1122 1093 ret = net_shaper_parse_handle(info->attrs[NET_SHAPER_A_HANDLE], info, 1123 1094 &handle); 1124 1095 if (ret) 1125 - goto unlock; 1096 + return ret; 1126 1097 1127 1098 hierarchy = net_shaper_hierarchy(binding); 1128 - if (!hierarchy) { 1129 - ret = -ENOENT; 1130 - goto unlock; 1131 - } 1099 + if (!hierarchy) 1100 + return -ENOENT; 1132 1101 1133 1102 shaper = net_shaper_lookup(binding, &handle); 1134 - if (!shaper) { 1135 - ret = -ENOENT; 1136 - goto unlock; 1137 - } 1103 + if (!shaper) 1104 + return -ENOENT; 1138 1105 1139 1106 if (handle.scope == NET_SHAPER_SCOPE_NODE) { 1140 1107 ret = net_shaper_pre_del_node(binding, shaper, info->extack); 1141 1108 if (ret) 1142 - goto unlock; 1109 + return ret; 1143 1110 } 1144 1111 1145 - ret = __net_shaper_delete(binding, shaper, info->extack); 1146 - 1147 - unlock: 1148 - net_shaper_unlock(binding); 1149 - return ret; 1112 + return __net_shaper_delete(binding, shaper, info->extack); 1150 1113 } 1151 1114 1152 1115 static int net_shaper_group_send_reply(struct net_shaper_binding *binding, ··· 1186 1167 if (!net_shaper_ops(binding)->group) 1187 1168 return -EOPNOTSUPP; 1188 1169 1189 - net_shaper_lock(binding); 1190 1170 leaves_count = net_shaper_list_len(info, NET_SHAPER_A_LEAVES); 1191 1171 if (!leaves_count) { 1192 1172 NL_SET_BAD_ATTR(info->extack, 1193 1173 info->attrs[NET_SHAPER_A_LEAVES]); 1194 - ret = -EINVAL; 1195 - goto unlock; 1174 + return -EINVAL; 1196 1175 } 1197 1176 1198 1177 leaves = kcalloc(leaves_count, sizeof(struct net_shaper) + 1199 1178 sizeof(struct net_shaper *), GFP_KERNEL); 1200 - if (!leaves) { 1201 - ret = -ENOMEM; 1202 - goto unlock; 1203 - } 1179 + if (!leaves) 1180 + return -ENOMEM; 1204 1181 old_nodes = (void *)&leaves[leaves_count]; 1205 1182 1206 1183 ret = net_shaper_parse_node(binding, info->attrs, info, &node); ··· 1273 1258 1274 1259 free_leaves: 1275 1260 kfree(leaves); 1276 - 1277 - unlock: 1278 - net_shaper_unlock(binding); 1279 1261 return ret; 1280 1262 1281 1263 free_msg: ··· 1382 1370 if (!hierarchy) 1383 1371 return; 1384 1372 1385 - net_shaper_lock(binding); 1386 1373 xa_lock(&hierarchy->shapers); 1387 1374 xa_for_each(&hierarchy->shapers, index, cur) { 1388 1375 __xa_erase(&hierarchy->shapers, index); 1389 1376 kfree(cur); 1390 1377 } 1391 1378 xa_unlock(&hierarchy->shapers); 1392 - net_shaper_unlock(binding); 1393 1379 1394 1380 kfree(hierarchy); 1395 1381 }
+6 -6
net/shaper/shaper_nl_gen.c
··· 99 99 }, 100 100 { 101 101 .cmd = NET_SHAPER_CMD_SET, 102 - .pre_doit = net_shaper_nl_pre_doit, 102 + .pre_doit = net_shaper_nl_pre_doit_write, 103 103 .doit = net_shaper_nl_set_doit, 104 - .post_doit = net_shaper_nl_post_doit, 104 + .post_doit = net_shaper_nl_post_doit_write, 105 105 .policy = net_shaper_set_nl_policy, 106 106 .maxattr = NET_SHAPER_A_IFINDEX, 107 107 .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, 108 108 }, 109 109 { 110 110 .cmd = NET_SHAPER_CMD_DELETE, 111 - .pre_doit = net_shaper_nl_pre_doit, 111 + .pre_doit = net_shaper_nl_pre_doit_write, 112 112 .doit = net_shaper_nl_delete_doit, 113 - .post_doit = net_shaper_nl_post_doit, 113 + .post_doit = net_shaper_nl_post_doit_write, 114 114 .policy = net_shaper_delete_nl_policy, 115 115 .maxattr = NET_SHAPER_A_IFINDEX, 116 116 .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO, 117 117 }, 118 118 { 119 119 .cmd = NET_SHAPER_CMD_GROUP, 120 - .pre_doit = net_shaper_nl_pre_doit, 120 + .pre_doit = net_shaper_nl_pre_doit_write, 121 121 .doit = net_shaper_nl_group_doit, 122 - .post_doit = net_shaper_nl_post_doit, 122 + .post_doit = net_shaper_nl_post_doit_write, 123 123 .policy = net_shaper_group_nl_policy, 124 124 .maxattr = NET_SHAPER_A_LEAVES, 125 125 .flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
+5
net/shaper/shaper_nl_gen.h
··· 18 18 19 19 int net_shaper_nl_pre_doit(const struct genl_split_ops *ops, 20 20 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); 21 23 int net_shaper_nl_cap_pre_doit(const struct genl_split_ops *ops, 22 24 struct sk_buff *skb, struct genl_info *info); 23 25 void 24 26 net_shaper_nl_post_doit(const struct genl_split_ops *ops, struct sk_buff *skb, 25 27 struct genl_info *info); 28 + void 29 + net_shaper_nl_post_doit_write(const struct genl_split_ops *ops, 30 + struct sk_buff *skb, struct genl_info *info); 26 31 void 27 32 net_shaper_nl_cap_post_doit(const struct genl_split_ops *ops, 28 33 struct sk_buff *skb, struct genl_info *info);