Patch "ipv6: add rcu grace period before freeing fib6_node" has been added to the 4.9-stable tree
Subsystems (3)
This is a note to let you know that I've just added the patch titled
ipv6: add rcu grace period before freeing fib6_node
to the 4.9-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
ipv6-add-rcu-grace-period-before-freeing-fib6_node.patch
and it can be found in the queue-4.9 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From foo@baz Thu Sep 14 23:20:08 PDT 2017
From: Wei Wang <[email protected]>
Date: Mon, 21 Aug 2017 09:47:10 -0700
Subject: ipv6: add rcu grace period before freeing fib6_node
From: Wei Wang <[email protected]>
[ Upstream commit c5cff8561d2d0006e972bd114afd51f082fee77c ]
We currently keep rt->rt6i_node pointing to the fib6_node for the route. And some functions make use of this pointer to dereference the fib6_node from rt structure, e.g. rt6_check(). However, as there is neither refcount nor rcu taken when dereferencing rt->rt6i_node, it could potentially cause crashes as rt->rt6i_node could be set to NULL by other CPUs when doing a route deletion.
This patch introduces an rcu grace period before freeing fib6_node and makes sure the functions that dereference it takes rcu_read_lock().
Note: there is no "Fixes" tag because this bug was there in a very early stage.
--- include/net/ip6_fib.h | 30 +++++++++++++++++++++++++++++- net/ipv6/ip6_fib.c | 20 ++++++++++++++++---- net/ipv6/route.c | 14 +++++++++++--- 3 files changed, 56 insertions(+), 8 deletions(-)
include/net/ip6_fib.h +29 -1
@@ -68,6 +68,7 @@ struct fib6_node {__u16 fn_flags;int fn_sernum;struct rt6_info *rr_ptr; +struct rcu_head rcu; };#ifndef CONFIG_IPV6_SUBTREES
@@ -165,13 +166,40 @@ static inline void rt6_update_expires(st rt0->rt6i_flags |=RTF_EXPIRES ; } +/* Function to safely get fn->sernum for passed in rt +* and store result in passed in cookie. +* Return true if we can get cookie safely +* Return false if not +*/ +static inline bool rt6_get_cookie_safe (const struct rt6_info *rt, +u32 *cookie) +{ +struct fib6_node *fn; +bool status = false; + +rcu_read_lock (); + fn =rcu_dereference (rt->rt6i_node); + +if (fn) { + *cookie = fn->fn_sernum; + status = true; + } + +rcu_read_unlock (); +return status; +} +static inline u32 rt6_get_cookie (const struct rt6_info *rt) { +u32 cookie =0 ; +if (rt->rt6i_flags &RTF_PCPU || (unlikely (rt->dst.flags &DST_NOCACHE ) && rt->dst.from)) rt = (struct rt6_info *)(rt->dst.from); -return rt->rt6i_node ? rt->rt6i_node->fn_sernum :0 ; +rt6_get_cookie_safe (rt, &cookie); + +return cookie; }static inline void ip6_rt_put (struct rt6_info *rt)
net/ipv6/ip6_fib.c +16 -4
@@ -148,11 +148,23 @@ static struct fib6_node *node_alloc(voidreturn fn; } -static void node_free (struct fib6_node *fn) +static void node_free_immediate (struct fib6_node *fn) +{ +kmem_cache_free (fib6_node_kmem, fn); +} + +static void node_free_rcu (struct rcu_head *head) { + struct fib6_node *fn = container_of(head, struct fib6_node, rcu); +kmem_cache_free (fib6_node_kmem, fn); } +static void node_free (struct fib6_node *fn) +{ +call_rcu (&fn->rcu, node_free_rcu); +} +static void rt6_rcu_free (struct rt6_info *rt) {call_rcu (&rt->dst.rcu_head, dst_rcu_free);
@@ -589,9 +601,9 @@ insert_above:if (!in || !ln) {if (in) -node_free (in); +node_free_immediate (in);if (ln) -node_free (ln); +node_free_immediate (ln);return ERR_PTR (-ENOMEM ); }
@@ -1020,7 +1032,7 @@ int fib6_add(struct fib6_node *root, strroot, and then (in failure) stale node in main tree. */ -node_free (sfn); +node_free_immediate (sfn); err =PTR_ERR (sn); goto failure; }
net/ipv6/route.c +11 -3
@@ -1267,7 +1267,9 @@ static void rt6_dst_from_metrics_check(sstatic struct dst_entry *rt6_check (struct rt6_info *rt,u32 cookie) { -if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie)) +u32 rt_cookie; + +if (!rt6_get_cookie_safe (rt, &rt_cookie) || rt_cookie != cookie)return NULL ;if (rt6_check_expired (rt))
@@ -1335,8 +1337,14 @@ static void ip6_link_failure(struct sk_bif (rt->rt6i_flags &RTF_CACHE ) {dst_hold (&rt->dst);ip6_del_rt (rt); - }else if (rt->rt6i_node && (rt->rt6i_flags &RTF_DEFAULT )) { - rt->rt6i_node->fn_sernum =-1 ; + }else { +struct fib6_node *fn; + +rcu_read_lock (); + fn =rcu_dereference (rt->rt6i_node); +if (fn && (rt->rt6i_flags &RTF_DEFAULT )) + fn->fn_sernum =-1 ; +rcu_read_unlock (); } } }
Patches currently in stable-queue which might be from [email protected] are
queue-4.9/ipv6-fix-sparse-warning-on-rt6i_node.patch queue-4.9/tcp-initialize-rcv_mss-to-tcp_min_mss-instead-of-0.patch queue-4.9/ipv6-add-rcu-grace-period-before-freeing-fib6_node.patch