mirror of
https://github.com/torvalds/linux.git
synced 2026-10-07 02:56:02 +02:00
ipv6: Prevent rt6_insert_exception() for dying fib6_info.
Before the cited commit, fib6_nh_flush_exceptions() always set
from->exception_bucket_flushed = 1 under rt6_exception_lock to
prevent rt6_insert_exception() from inserting a new exception
for a dying fib6_info.
The flag was replaced with the FIB6_EXCEPTION_BUCKET_FLUSHED
bit stored in nh->rt6i_exception_bucket.
The problem is that now the bit is only set when the bucket
is not NULL and fib6_nh_flush_exceptions() is called from
fib6_nh_release() after fib6_ref has already reached zero.
If rt6_insert_exception() is called while the target fib6_info
is being removed via fib6_purge_rt(), a new exception could be
created successfully because rt6_flush_exceptions() no longer
sets the bit.
This creates a reference cycle between the fib6_info and the
exception route, leaking the fib6_info, its nexthop device,
and all per-CPU routes in fib6_nh->rt6i_pcpu, which stalls netdev
unregistration.
[ 34.680602] unregister_netdevice: waiting for gre6 to become free. Usage count = 68
[ 44.920675] unregister_netdevice: waiting for gre6 to become free. Usage count = 68
[ 55.176582] unregister_netdevice: waiting for gre6 to become free. Usage count = 68
Let's call fib6_drop_pcpu_from() before rt6_flush_exceptions(),
to set fib6_destroying before rt6_exception_lock, and check
f6i->fib6_destroying in rt6_insert_exception().
Note that FIB6_EXCEPTION_BUCKET_FLUSHED logic is dead and
we can clean it up in net-next.
Fixes: cc5c073a69 ("ipv6: Move exception bucket to fib6_nh")
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Link: https://patch.msgid.link/20260918082209.2853582-1-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
d06f2ebf67
commit
0346ec2f08
|
|
@ -1043,8 +1043,8 @@ static void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
|
|||
struct fib6_table *table = rt->fib6_table;
|
||||
|
||||
/* Flush all cached dst in exception table */
|
||||
rt6_flush_exceptions(rt);
|
||||
fib6_drop_pcpu_from(rt);
|
||||
rt6_flush_exceptions(rt);
|
||||
|
||||
if (rt->nh) {
|
||||
spin_lock(&rt->nh->lock);
|
||||
|
|
|
|||
|
|
@ -1729,6 +1729,11 @@ static int rt6_insert_exception(struct rt6_info *nrt,
|
|||
|
||||
spin_lock_bh(&rt6_exception_lock);
|
||||
|
||||
if (f6i->fib6_destroying) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bucket = rcu_dereference_protected(nh->rt6i_exception_bucket,
|
||||
lockdep_is_held(&rt6_exception_lock));
|
||||
if (!bucket) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user