Skip to content

Commit b228a94

Browse files
Guillaume Naultdavem330
authored andcommitted
l2tp: fix race between l2tp_session_delete() and l2tp_tunnel_closeall()
There are several ways to remove L2TP sessions: * deleting a session explicitly using the netlink interface (with L2TP_CMD_SESSION_DELETE), * deleting the session's parent tunnel (either by closing the tunnel's file descriptor or using the netlink interface), * closing the PPPOL2TP file descriptor of a PPP pseudo-wire. In some cases, when these methods are used concurrently on the same session, the session can be removed twice, leading to use-after-free bugs. This patch adds a 'dead' flag, used by l2tp_session_delete() and l2tp_tunnel_closeall() to prevent them from stepping on each other's toes. The session deletion path used when closing a PPPOL2TP file descriptor doesn't need to be adapted. It already has to ensure that a session remains valid for the lifetime of its PPPOL2TP file descriptor. So it takes an extra reference on the session in the ->session_close() callback (pppol2tp_session_close()), which is eventually dropped in the ->sk_destruct() callback of the PPPOL2TP socket (pppol2tp_session_destruct()). Still, __l2tp_session_unhash() and l2tp_session_queue_purge() can be called twice and even concurrently for a given session, but thanks to proper locking and re-initialisation of list fields, this is not an issue. Signed-off-by: Guillaume Nault <g.nault@alphalink.fr> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent cdd10c9 commit b228a94

File tree

2 files changed

+7
-0
lines changed

2 files changed

+7
-0
lines changed

net/l2tp/l2tp_core.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,9 @@ void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel)
13141314

13151315
hlist_del_init(&session->hlist);
13161316

1317+
if (test_and_set_bit(0, &session->dead))
1318+
goto again;
1319+
13171320
if (session->ref != NULL)
13181321
(*session->ref)(session);
13191322

@@ -1750,6 +1753,9 @@ EXPORT_SYMBOL_GPL(__l2tp_session_unhash);
17501753
*/
17511754
int l2tp_session_delete(struct l2tp_session *session)
17521755
{
1756+
if (test_and_set_bit(0, &session->dead))
1757+
return 0;
1758+
17531759
if (session->ref)
17541760
(*session->ref)(session);
17551761
__l2tp_session_unhash(session);

net/l2tp/l2tp_core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ struct l2tp_session_cfg {
7676
struct l2tp_session {
7777
int magic; /* should be
7878
* L2TP_SESSION_MAGIC */
79+
long dead;
7980

8081
struct l2tp_tunnel *tunnel; /* back pointer to tunnel
8182
* context */

0 commit comments

Comments
 (0)