aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/netlink/af_netlink.c34
1 files changed, 30 insertions, 4 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index d7d1b822e824..d46da6cb92e4 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -83,6 +83,11 @@ struct netlink_sock {
83 struct module *module; 83 struct module *module;
84}; 84};
85 85
86struct listeners_rcu_head {
87 struct rcu_head rcu_head;
88 void *ptr;
89};
90
86#define NETLINK_KERNEL_SOCKET 0x1 91#define NETLINK_KERNEL_SOCKET 0x1
87#define NETLINK_RECV_PKTINFO 0x2 92#define NETLINK_RECV_PKTINFO 0x2
88#define NETLINK_BROADCAST_SEND_ERROR 0x4 93#define NETLINK_BROADCAST_SEND_ERROR 0x4
@@ -1453,7 +1458,8 @@ netlink_kernel_create(struct net *net, int unit, unsigned int groups,
1453 if (groups < 32) 1458 if (groups < 32)
1454 groups = 32; 1459 groups = 32;
1455 1460
1456 listeners = kzalloc(NLGRPSZ(groups), GFP_KERNEL); 1461 listeners = kzalloc(NLGRPSZ(groups) + sizeof(struct listeners_rcu_head),
1462 GFP_KERNEL);
1457 if (!listeners) 1463 if (!listeners)
1458 goto out_sock_release; 1464 goto out_sock_release;
1459 1465
@@ -1501,6 +1507,14 @@ netlink_kernel_release(struct sock *sk)
1501EXPORT_SYMBOL(netlink_kernel_release); 1507EXPORT_SYMBOL(netlink_kernel_release);
1502 1508
1503 1509
1510static void netlink_free_old_listeners(struct rcu_head *rcu_head)
1511{
1512 struct listeners_rcu_head *lrh;
1513
1514 lrh = container_of(rcu_head, struct listeners_rcu_head, rcu_head);
1515 kfree(lrh->ptr);
1516}
1517
1504/** 1518/**
1505 * netlink_change_ngroups - change number of multicast groups 1519 * netlink_change_ngroups - change number of multicast groups
1506 * 1520 *
@@ -1516,6 +1530,7 @@ EXPORT_SYMBOL(netlink_kernel_release);
1516int netlink_change_ngroups(struct sock *sk, unsigned int groups) 1530int netlink_change_ngroups(struct sock *sk, unsigned int groups)
1517{ 1531{
1518 unsigned long *listeners, *old = NULL; 1532 unsigned long *listeners, *old = NULL;
1533 struct listeners_rcu_head *old_rcu_head;
1519 struct netlink_table *tbl = &nl_table[sk->sk_protocol]; 1534 struct netlink_table *tbl = &nl_table[sk->sk_protocol];
1520 int err = 0; 1535 int err = 0;
1521 1536
@@ -1524,7 +1539,9 @@ int netlink_change_ngroups(struct sock *sk, unsigned int groups)
1524 1539
1525 netlink_table_grab(); 1540 netlink_table_grab();
1526 if (NLGRPSZ(tbl->groups) < NLGRPSZ(groups)) { 1541 if (NLGRPSZ(tbl->groups) < NLGRPSZ(groups)) {
1527 listeners = kzalloc(NLGRPSZ(groups), GFP_ATOMIC); 1542 listeners = kzalloc(NLGRPSZ(groups) +
1543 sizeof(struct listeners_rcu_head),
1544 GFP_ATOMIC);
1528 if (!listeners) { 1545 if (!listeners) {
1529 err = -ENOMEM; 1546 err = -ENOMEM;
1530 goto out_ungrab; 1547 goto out_ungrab;
@@ -1532,13 +1549,22 @@ int netlink_change_ngroups(struct sock *sk, unsigned int groups)
1532 old = tbl->listeners; 1549 old = tbl->listeners;
1533 memcpy(listeners, old, NLGRPSZ(tbl->groups)); 1550 memcpy(listeners, old, NLGRPSZ(tbl->groups));
1534 rcu_assign_pointer(tbl->listeners, listeners); 1551 rcu_assign_pointer(tbl->listeners, listeners);
1552 /*
1553 * Free the old memory after an RCU grace period so we
1554 * don't leak it. We use call_rcu() here in order to be
1555 * able to call this function from atomic contexts. The
1556 * allocation of this memory will have reserved enough
1557 * space for struct listeners_rcu_head at the end.
1558 */
1559 old_rcu_head = (void *)(tbl->listeners +
1560 NLGRPLONGS(tbl->groups));
1561 old_rcu_head->ptr = old;
1562 call_rcu(&old_rcu_head->rcu_head, netlink_free_old_listeners);
1535 } 1563 }
1536 tbl->groups = groups; 1564 tbl->groups = groups;
1537 1565
1538 out_ungrab: 1566 out_ungrab:
1539 netlink_table_ungrab(); 1567 netlink_table_ungrab();
1540 synchronize_rcu();
1541 kfree(old);
1542 return err; 1568 return err;
1543} 1569}
1544 1570