[PATCH net v2 2/2] genetlink: Clear stale members when assigning multicast group IDs
From: Anil Kaushik
Date: Sat Oct 31 2026 - 01:48:15 EST
genl_bind() checks a group under cb_lock, but netlink_setsockopt() and
netlink_bind() set the membership bit only after it returns and the lock
is dropped. If the family is unregistered in between, its members are
cleared before this socket is one, and the socket stays a member of a
group ID that no family owns. A family registered later can get that ID
and its GENL_MCAST_CAP_* flags are never checked for the socket.
Clear the members of the group IDs when a family is given them, the
same way genl_unregister_mc_groups() does when they are freed. No socket
can be a valid member at that point: cb_lock is held for writing, so
genl_bind() cannot see the new family yet.
Link: https://lore.kernel.org/netdev/179340003139.3131.11274717768928642724@xxxxxxxxxx/
Fixes: e03781879a0d ("drop_monitor: Require 'CAP_SYS_ADMIN' when joining "events" group")
Signed-off-by: Anil Kaushik <anilkaushikwireless@xxxxxxxxx>
---
net/netlink/genetlink.c | 31 +++++++++++++++++++++----------
1 file changed, 21 insertions(+), 10 deletions(-)
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index f038f04ef4ea..0ddebd828585 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -452,6 +452,22 @@ static int genl_allocate_reserve_groups(int n_groups, int *first_id)
static struct genl_family genl_ctrl;
+static void genl_clear_mc_users(const struct genl_family *family)
+{
+ struct net *net;
+ int i;
+
+ netlink_table_grab();
+ rcu_read_lock();
+ for_each_net_rcu(net) {
+ for (i = 0; i < family->n_mcgrps; i++)
+ __netlink_clear_multicast_users(net->genl_sock,
+ family->mcgrp_offset + i);
+ }
+ rcu_read_unlock();
+ netlink_table_ungrab();
+}
+
static int genl_validate_assign_mc_groups(struct genl_family *family)
{
int first_id;
@@ -527,23 +543,18 @@ static int genl_validate_assign_mc_groups(struct genl_family *family)
clear_bit(family->mcgrp_offset + i, mc_groups);
}
+ /* drop sockets that joined the IDs while no family owned them */
+ if (!err)
+ genl_clear_mc_users(family);
+
return err;
}
static void genl_unregister_mc_groups(const struct genl_family *family)
{
- struct net *net;
int i;
- netlink_table_grab();
- rcu_read_lock();
- for_each_net_rcu(net) {
- for (i = 0; i < family->n_mcgrps; i++)
- __netlink_clear_multicast_users(
- net->genl_sock, family->mcgrp_offset + i);
- }
- rcu_read_unlock();
- netlink_table_ungrab();
+ genl_clear_mc_users(family);
for (i = 0; i < family->n_mcgrps; i++) {
int grp_id = family->mcgrp_offset + i;
--
2.25.1