From patchwork Wed Dec 12 05:10:57 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [3.5.y.z, extended, stable] Patch "netlink: use kfree_rcu() in netlink_release()" has been added to staging queue Date: Tue, 11 Dec 2012 19:10:57 -0000 From: Herton Ronaldo Krzesinski X-Patchwork-Id: 205407 Message-Id: <1355289057-30838-1-git-send-email-herton.krzesinski@canonical.com> To: Eric Dumazet Cc: Sam Leffler , Jonathan Kliegman , kernel-team@lists.ubuntu.com, =?UTF-8?q?St=C3=A9phane=20Marchesin?= , "David S. Miller" This is a note to let you know that I have just added a patch titled netlink: use kfree_rcu() in netlink_release() to the linux-3.5.y-queue branch of the 3.5.y.z extended stable tree which can be found at: http://kernel.ubuntu.com/git?p=ubuntu/linux.git;a=shortlog;h=refs/heads/linux-3.5.y-queue If you, or anyone else, feels it should not be added to this tree, please reply to this email. For more information about the 3.5.y.z tree, see https://wiki.ubuntu.com/Kernel/Dev/ExtendedStable Thanks. -Herton ------ >From cd77db9d8a50023961c56f86d4ffd22c6b3b6415 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 18 Oct 2012 03:21:55 +0000 Subject: [PATCH] netlink: use kfree_rcu() in netlink_release() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6d772ac5578f711d1ce7b03535d1c95bffb21dff upstream. On some suspend/resume operations involving wimax device, we have noticed some intermittent memory corruptions in netlink code. Stéphane Marchesin tracked this corruption in netlink_update_listeners() and suggested a patch. It appears netlink_release() should use kfree_rcu() instead of kfree() for the listeners structure as it may be used by other cpus using RCU protection. netlink_release() must set to NULL the listeners pointer when it is about to be freed. Also have to protect netlink_update_listeners() and netlink_has_listeners() if listeners is NULL. Add a nl_deref_protected() lockdep helper to properly document which locks protects us. Reported-by: Jonathan Kliegman Signed-off-by: Eric Dumazet Cc: Stéphane Marchesin Cc: Sam Leffler Signed-off-by: David S. Miller Signed-off-by: Herton Ronaldo Krzesinski --- net/netlink/af_netlink.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) -- 1.7.9.5 diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index efffdfc..f4980b6 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -136,6 +136,8 @@ static int netlink_dump(struct sock *sk); static DEFINE_RWLOCK(nl_table_lock); static atomic_t nl_table_users = ATOMIC_INIT(0); +#define nl_deref_protected(X) rcu_dereference_protected(X, lockdep_is_held(&nl_table_lock)); + static ATOMIC_NOTIFIER_HEAD(netlink_chain); static inline u32 netlink_group_mask(u32 group) @@ -343,6 +345,11 @@ netlink_update_listeners(struct sock *sk) struct hlist_node *node; unsigned long mask; unsigned int i; + struct listeners *listeners; + + listeners = nl_deref_protected(tbl->listeners); + if (!listeners) + return; for (i = 0; i < NLGRPLONGS(tbl->groups); i++) { mask = 0; @@ -350,7 +357,7 @@ netlink_update_listeners(struct sock *sk) if (i < NLGRPLONGS(nlk_sk(sk)->ngroups)) mask |= nlk_sk(sk)->groups[i]; } - tbl->listeners->masks[i] = mask; + listeners->masks[i] = mask; } /* this function is only called with the netlink table "grabbed", which * makes sure updates are visible before bind or setsockopt return. */ @@ -531,7 +538,11 @@ static int netlink_release(struct socket *sock) if (netlink_is_kernel(sk)) { BUG_ON(nl_table[sk->sk_protocol].registered == 0); if (--nl_table[sk->sk_protocol].registered == 0) { - kfree(nl_table[sk->sk_protocol].listeners); + struct listeners *old; + + old = nl_deref_protected(nl_table[sk->sk_protocol].listeners); + RCU_INIT_POINTER(nl_table[sk->sk_protocol].listeners, NULL); + kfree_rcu(old, rcu); nl_table[sk->sk_protocol].module = NULL; nl_table[sk->sk_protocol].registered = 0; } @@ -964,7 +975,7 @@ int netlink_has_listeners(struct sock *sk, unsigned int group) rcu_read_lock(); listeners = rcu_dereference(nl_table[sk->sk_protocol].listeners); - if (group - 1 < nl_table[sk->sk_protocol].groups) + if (listeners && group - 1 < nl_table[sk->sk_protocol].groups) res = test_bit(group - 1, listeners->masks); rcu_read_unlock(); @@ -1597,7 +1608,7 @@ int __netlink_change_ngroups(struct sock *sk, unsigned int groups) new = kzalloc(sizeof(*new) + NLGRPSZ(groups), GFP_ATOMIC); if (!new) return -ENOMEM; - old = rcu_dereference_protected(tbl->listeners, 1); + old = nl_deref_protected(tbl->listeners); memcpy(new->masks, old->masks, NLGRPSZ(tbl->groups)); rcu_assign_pointer(tbl->listeners, new);