l2tp: add idr consistency check in session_register
authorJames Chapman <jchapman@katalix.com>
Mon, 29 Jul 2024 15:38:12 +0000 (16:38 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 31 Jul 2024 08:25:13 +0000 (09:25 +0100)
l2tp_session_register uses an idr_alloc then idr_replace pattern to
insert sessions into the session IDR. To catch invalid locking, add a
WARN_ON_ONCE if the IDR entry is modified by another thread between
alloc and replace steps.

Also add comments to make expectations clear.

Signed-off-by: James Chapman <jchapman@katalix.com>
Signed-off-by: Tom Parkin <tparkin@katalix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/l2tp/l2tp_core.c

index cd9b157e8b4de54ba149adb38ff0dec3fb856c1c..fd03c17dd20c22225e13abe749f70d491f3f4904 100644 (file)
@@ -474,6 +474,7 @@ int l2tp_session_register(struct l2tp_session *session,
 {
        struct l2tp_net *pn = l2tp_pernet(tunnel->l2tp_net);
        struct l2tp_session *other_session = NULL;
+       void *old = NULL;
        u32 session_key;
        int err;
 
@@ -517,13 +518,19 @@ int l2tp_session_register(struct l2tp_session *session,
        WRITE_ONCE(session->tunnel, tunnel);
        list_add_rcu(&session->list, &tunnel->session_list);
 
+       /* this makes session available to lockless getters */
        if (tunnel->version == L2TP_HDR_VER_3) {
                if (!other_session)
-                       idr_replace(&pn->l2tp_v3_session_idr, session, session_key);
+                       old = idr_replace(&pn->l2tp_v3_session_idr, session, session_key);
        } else {
-               idr_replace(&pn->l2tp_v2_session_idr, session, session_key);
+               old = idr_replace(&pn->l2tp_v2_session_idr, session, session_key);
        }
 
+       /* old should be NULL, unless something removed or modified
+        * the IDR entry after our idr_alloc_32 above (which shouldn't
+        * happen).
+        */
+       WARN_ON_ONCE(old);
 out:
        spin_unlock_bh(&pn->l2tp_session_idr_lock);
        spin_unlock_bh(&tunnel->list_lock);