Message ID | 20240704152508.1923908-1-jchapman@katalix.com (mailing list archive) |
---|---|
State | New |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | [net-next,v2] l2tp: fix possible UAF when cleaning up tunnels | expand |
On Thu, 4 Jul 2024 16:25:08 +0100 James Chapman <jchapman@katalix.com> > --- a/net/l2tp/l2tp_core.c > +++ b/net/l2tp/l2tp_core.c > @@ -1290,17 +1290,20 @@ static void l2tp_session_unhash(struct l2tp_session *session) > static void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel) > { > struct l2tp_session *session; > - struct list_head *pos; > - struct list_head *tmp; > > spin_lock_bh(&tunnel->list_lock); > tunnel->acpt_newsess = false; > - list_for_each_safe(pos, tmp, &tunnel->session_list) { > - session = list_entry(pos, struct l2tp_session, list); > + for (;;) { > + session = list_first_entry_or_null(&tunnel->session_list, > + struct l2tp_session, list); > + if (!session) > + break; > + l2tp_session_inc_refcount(session); > list_del_init(&session->list); > spin_unlock_bh(&tunnel->list_lock); > l2tp_session_delete(session); > spin_lock_bh(&tunnel->list_lock); > + l2tp_session_dec_refcount(session); Bumping refcount up makes it safe for the current cpu to go thru race after releasing lock, and if it wins the race, dropping refcount makes the peer head on uaf.
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 64f446f0930b..2790a51e59e3 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1290,17 +1290,20 @@ static void l2tp_session_unhash(struct l2tp_session *session) static void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel) { struct l2tp_session *session; - struct list_head *pos; - struct list_head *tmp; spin_lock_bh(&tunnel->list_lock); tunnel->acpt_newsess = false; - list_for_each_safe(pos, tmp, &tunnel->session_list) { - session = list_entry(pos, struct l2tp_session, list); + for (;;) { + session = list_first_entry_or_null(&tunnel->session_list, + struct l2tp_session, list); + if (!session) + break; + l2tp_session_inc_refcount(session); list_del_init(&session->list); spin_unlock_bh(&tunnel->list_lock); l2tp_session_delete(session); spin_lock_bh(&tunnel->list_lock); + l2tp_session_dec_refcount(session); } spin_unlock_bh(&tunnel->list_lock); }