Skip to content

Commit fad45a8

Browse files
Sean Tranchettigregkh
authored andcommitted
genetlink: remove genl_bind
[ Upstream commit 1e82a62 ] A potential deadlock can occur during registering or unregistering a new generic netlink family between the main nl_table_lock and the cb_lock where each thread wants the lock held by the other, as demonstrated below. 1) Thread 1 is performing a netlink_bind() operation on a socket. As part of this call, it will call netlink_lock_table(), incrementing the nl_table_users count to 1. 2) Thread 2 is registering (or unregistering) a genl_family via the genl_(un)register_family() API. The cb_lock semaphore will be taken for writing. 3) Thread 1 will call genl_bind() as part of the bind operation to handle subscribing to GENL multicast groups at the request of the user. It will attempt to take the cb_lock semaphore for reading, but it will fail and be scheduled away, waiting for Thread 2 to finish the write. 4) Thread 2 will call netlink_table_grab() during the (un)registration call. However, as Thread 1 has incremented nl_table_users, it will not be able to proceed, and both threads will be stuck waiting for the other. genl_bind() is a noop, unless a genl_family implements the mcast_bind() function to handle setting up family-specific multicast operations. Since no one in-tree uses this functionality as Cong pointed out, simply removing the genl_bind() function will remove the possibility for deadlock, as there is no attempt by Thread 1 above to take the cb_lock semaphore. Fixes: c380d9a ("genetlink: pass multicast bind/unbind to families") Suggested-by: Cong Wang <[email protected]> Acked-by: Johannes Berg <[email protected]> Reported-by: kernel test robot <[email protected]> Signed-off-by: Sean Tranchetti <[email protected]> Signed-off-by: David S. Miller <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 6648696 commit fad45a8

File tree

2 files changed

+0
-60
lines changed

2 files changed

+0
-60
lines changed

include/net/genetlink.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,6 @@ struct genl_info;
3333
* do additional, common, filtering and return an error
3434
* @post_doit: called after an operation's doit callback, it may
3535
* undo operations done by pre_doit, for example release locks
36-
* @mcast_bind: a socket bound to the given multicast group (which
37-
* is given as the offset into the groups array)
38-
* @mcast_unbind: a socket was unbound from the given multicast group.
39-
* Note that unbind() will not be called symmetrically if the
40-
* generic netlink family is removed while there are still open
41-
* sockets.
4236
* @attrbuf: buffer to store parsed attributes
4337
* @family_list: family list
4438
* @mcgrps: multicast groups used by this family (private)
@@ -61,8 +55,6 @@ struct genl_family {
6155
void (*post_doit)(const struct genl_ops *ops,
6256
struct sk_buff *skb,
6357
struct genl_info *info);
64-
int (*mcast_bind)(struct net *net, int group);
65-
void (*mcast_unbind)(struct net *net, int group);
6658
struct nlattr ** attrbuf; /* private */
6759
const struct genl_ops * ops; /* private */
6860
const struct genl_multicast_group *mcgrps; /* private */

net/netlink/genetlink.c

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -992,63 +992,11 @@ static const struct genl_multicast_group genl_ctrl_groups[] = {
992992
{ .name = "notify", },
993993
};
994994

995-
static int genl_bind(struct net *net, int group)
996-
{
997-
int i, err = -ENOENT;
998-
999-
down_read(&cb_lock);
1000-
for (i = 0; i < GENL_FAM_TAB_SIZE; i++) {
1001-
struct genl_family *f;
1002-
1003-
list_for_each_entry(f, genl_family_chain(i), family_list) {
1004-
if (group >= f->mcgrp_offset &&
1005-
group < f->mcgrp_offset + f->n_mcgrps) {
1006-
int fam_grp = group - f->mcgrp_offset;
1007-
1008-
if (!f->netnsok && net != &init_net)
1009-
err = -ENOENT;
1010-
else if (f->mcast_bind)
1011-
err = f->mcast_bind(net, fam_grp);
1012-
else
1013-
err = 0;
1014-
break;
1015-
}
1016-
}
1017-
}
1018-
up_read(&cb_lock);
1019-
1020-
return err;
1021-
}
1022-
1023-
static void genl_unbind(struct net *net, int group)
1024-
{
1025-
int i;
1026-
1027-
down_read(&cb_lock);
1028-
for (i = 0; i < GENL_FAM_TAB_SIZE; i++) {
1029-
struct genl_family *f;
1030-
1031-
list_for_each_entry(f, genl_family_chain(i), family_list) {
1032-
if (group >= f->mcgrp_offset &&
1033-
group < f->mcgrp_offset + f->n_mcgrps) {
1034-
int fam_grp = group - f->mcgrp_offset;
1035-
1036-
if (f->mcast_unbind)
1037-
f->mcast_unbind(net, fam_grp);
1038-
break;
1039-
}
1040-
}
1041-
}
1042-
up_read(&cb_lock);
1043-
}
1044-
1045995
static int __net_init genl_pernet_init(struct net *net)
1046996
{
1047997
struct netlink_kernel_cfg cfg = {
1048998
.input = genl_rcv,
1049999
.flags = NL_CFG_F_NONROOT_RECV,
1050-
.bind = genl_bind,
1051-
.unbind = genl_unbind,
10521000
};
10531001

10541002
/* we'll bump the group number right afterwards */

0 commit comments

Comments
 (0)