Commit 5c398dc8f5a58b5417d8ae0d474704feb6e12a12

Authored by Eric Dumazet
Committed by David S. Miller
1 parent 7f8a688e1e

netlink: fix netlink_change_ngroups()

commit 6c04bb18ddd633 (netlink: use call_rcu for netlink_change_ngroups)
used a somewhat convoluted and racy way to perform call_rcu().

The old block of memory is freed after a grace period, but the rcu_head
used to track it is located in new block.

This can clash if we call two times or more netlink_change_ngroups(),
and a block is freed before another. call_rcu() called on different cpus
makes no guarantee in order of callbacks.

Fix this using a more standard way of handling this : Each block of
memory contains its own rcu_head, so that no 'use after free' can
happens.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Johannes Berg <johannes@sipsolutions.net>
CC: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 1 changed file with 24 additions and 41 deletions Side-by-side Diff

net/netlink/af_netlink.c
... ... @@ -83,9 +83,9 @@
83 83 struct module *module;
84 84 };
85 85  
86   -struct listeners_rcu_head {
87   - struct rcu_head rcu_head;
88   - void *ptr;
  86 +struct listeners {
  87 + struct rcu_head rcu;
  88 + unsigned long masks[0];
89 89 };
90 90  
91 91 #define NETLINK_KERNEL_SOCKET 0x1
... ... @@ -119,7 +119,7 @@
119 119 struct netlink_table {
120 120 struct nl_pid_hash hash;
121 121 struct hlist_head mc_list;
122   - unsigned long *listeners;
  122 + struct listeners __rcu *listeners;
123 123 unsigned int nl_nonroot;
124 124 unsigned int groups;
125 125 struct mutex *cb_mutex;
... ... @@ -338,7 +338,7 @@
338 338 if (i < NLGRPLONGS(nlk_sk(sk)->ngroups))
339 339 mask |= nlk_sk(sk)->groups[i];
340 340 }
341   - tbl->listeners[i] = mask;
  341 + tbl->listeners->masks[i] = mask;
342 342 }
343 343 /* this function is only called with the netlink table "grabbed", which
344 344 * makes sure updates are visible before bind or setsockopt return. */
... ... @@ -936,7 +936,7 @@
936 936 int netlink_has_listeners(struct sock *sk, unsigned int group)
937 937 {
938 938 int res = 0;
939   - unsigned long *listeners;
  939 + struct listeners *listeners;
940 940  
941 941 BUG_ON(!netlink_is_kernel(sk));
942 942  
... ... @@ -944,7 +944,7 @@
944 944 listeners = rcu_dereference(nl_table[sk->sk_protocol].listeners);
945 945  
946 946 if (group - 1 < nl_table[sk->sk_protocol].groups)
947   - res = test_bit(group - 1, listeners);
  947 + res = test_bit(group - 1, listeners->masks);
948 948  
949 949 rcu_read_unlock();
950 950  
... ... @@ -1498,7 +1498,7 @@
1498 1498 struct socket *sock;
1499 1499 struct sock *sk;
1500 1500 struct netlink_sock *nlk;
1501   - unsigned long *listeners = NULL;
  1501 + struct listeners *listeners = NULL;
1502 1502  
1503 1503 BUG_ON(!nl_table);
1504 1504  
... ... @@ -1523,8 +1523,7 @@
1523 1523 if (groups < 32)
1524 1524 groups = 32;
1525 1525  
1526   - listeners = kzalloc(NLGRPSZ(groups) + sizeof(struct listeners_rcu_head),
1527   - GFP_KERNEL);
  1526 + listeners = kzalloc(sizeof(*listeners) + NLGRPSZ(groups), GFP_KERNEL);
1528 1527 if (!listeners)
1529 1528 goto out_sock_release;
1530 1529  
... ... @@ -1541,7 +1540,7 @@
1541 1540 netlink_table_grab();
1542 1541 if (!nl_table[unit].registered) {
1543 1542 nl_table[unit].groups = groups;
1544   - nl_table[unit].listeners = listeners;
  1543 + rcu_assign_pointer(nl_table[unit].listeners, listeners);
1545 1544 nl_table[unit].cb_mutex = cb_mutex;
1546 1545 nl_table[unit].module = module;
1547 1546 nl_table[unit].registered = 1;
1548 1547  
1549 1548  
1550 1549  
1551 1550  
... ... @@ -1572,43 +1571,28 @@
1572 1571 EXPORT_SYMBOL(netlink_kernel_release);
1573 1572  
1574 1573  
1575   -static void netlink_free_old_listeners(struct rcu_head *rcu_head)
  1574 +static void listeners_free_rcu(struct rcu_head *head)
1576 1575 {
1577   - struct listeners_rcu_head *lrh;
1578   -
1579   - lrh = container_of(rcu_head, struct listeners_rcu_head, rcu_head);
1580   - kfree(lrh->ptr);
  1576 + kfree(container_of(head, struct listeners, rcu));
1581 1577 }
1582 1578  
1583 1579 int __netlink_change_ngroups(struct sock *sk, unsigned int groups)
1584 1580 {
1585   - unsigned long *listeners, *old = NULL;
1586   - struct listeners_rcu_head *old_rcu_head;
  1581 + struct listeners *new, *old;
1587 1582 struct netlink_table *tbl = &nl_table[sk->sk_protocol];
1588 1583  
1589 1584 if (groups < 32)
1590 1585 groups = 32;
1591 1586  
1592 1587 if (NLGRPSZ(tbl->groups) < NLGRPSZ(groups)) {
1593   - listeners = kzalloc(NLGRPSZ(groups) +
1594   - sizeof(struct listeners_rcu_head),
1595   - GFP_ATOMIC);
1596   - if (!listeners)
  1588 + new = kzalloc(sizeof(*new) + NLGRPSZ(groups), GFP_ATOMIC);
  1589 + if (!new)
1597 1590 return -ENOMEM;
1598   - old = tbl->listeners;
1599   - memcpy(listeners, old, NLGRPSZ(tbl->groups));
1600   - rcu_assign_pointer(tbl->listeners, listeners);
1601   - /*
1602   - * Free the old memory after an RCU grace period so we
1603   - * don't leak it. We use call_rcu() here in order to be
1604   - * able to call this function from atomic contexts. The
1605   - * allocation of this memory will have reserved enough
1606   - * space for struct listeners_rcu_head at the end.
1607   - */
1608   - old_rcu_head = (void *)(tbl->listeners +
1609   - NLGRPLONGS(tbl->groups));
1610   - old_rcu_head->ptr = old;
1611   - call_rcu(&old_rcu_head->rcu_head, netlink_free_old_listeners);
  1591 + old = rcu_dereference_raw(tbl->listeners);
  1592 + memcpy(new->masks, old->masks, NLGRPSZ(tbl->groups));
  1593 + rcu_assign_pointer(tbl->listeners, new);
  1594 +
  1595 + call_rcu(&old->rcu, listeners_free_rcu);
1612 1596 }
1613 1597 tbl->groups = groups;
1614 1598  
1615 1599  
1616 1600  
1617 1601  
... ... @@ -2104,18 +2088,17 @@
2104 2088  
2105 2089 static void __init netlink_add_usersock_entry(void)
2106 2090 {
2107   - unsigned long *listeners;
  2091 + struct listeners *listeners;
2108 2092 int groups = 32;
2109 2093  
2110   - listeners = kzalloc(NLGRPSZ(groups) + sizeof(struct listeners_rcu_head),
2111   - GFP_KERNEL);
  2094 + listeners = kzalloc(sizeof(*listeners) + NLGRPSZ(groups), GFP_KERNEL);
2112 2095 if (!listeners)
2113   - panic("netlink_add_usersock_entry: Cannot allocate listneres\n");
  2096 + panic("netlink_add_usersock_entry: Cannot allocate listeners\n");
2114 2097  
2115 2098 netlink_table_grab();
2116 2099  
2117 2100 nl_table[NETLINK_USERSOCK].groups = groups;
2118   - nl_table[NETLINK_USERSOCK].listeners = listeners;
  2101 + rcu_assign_pointer(nl_table[NETLINK_USERSOCK].listeners, listeners);
2119 2102 nl_table[NETLINK_USERSOCK].module = THIS_MODULE;
2120 2103 nl_table[NETLINK_USERSOCK].registered = 1;
2121 2104