]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ksmbd: fix use-after-free in smb_break_all_levII_oplock()
authorNamjae Jeon <linkinjeon@kernel.org>
Tue, 15 Apr 2025 00:30:21 +0000 (09:30 +0900)
committerSteve French <stfrench@microsoft.com>
Tue, 15 Apr 2025 03:21:26 +0000 (22:21 -0500)
There is a room in smb_break_all_levII_oplock that can cause racy issues
when unlocking in the middle of the loop. This patch use read lock
to protect whole loop.

Cc: stable@vger.kernel.org
Reported-by: Norbert Szetei <norbert@doyensec.com>
Tested-by: Norbert Szetei <norbert@doyensec.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/server/oplock.c
fs/smb/server/oplock.h

index f103b1bd0400401aeb94a2effe7c5e256e52fe74..81a29857b1e32f73822764eb6c7d87c748b06a96 100644 (file)
@@ -129,14 +129,6 @@ static void free_opinfo(struct oplock_info *opinfo)
        kfree(opinfo);
 }
 
-static inline void opinfo_free_rcu(struct rcu_head *rcu_head)
-{
-       struct oplock_info *opinfo;
-
-       opinfo = container_of(rcu_head, struct oplock_info, rcu_head);
-       free_opinfo(opinfo);
-}
-
 struct oplock_info *opinfo_get(struct ksmbd_file *fp)
 {
        struct oplock_info *opinfo;
@@ -157,8 +149,8 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
        if (list_empty(&ci->m_op_list))
                return NULL;
 
-       rcu_read_lock();
-       opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info,
+       down_read(&ci->m_lock);
+       opinfo = list_first_entry(&ci->m_op_list, struct oplock_info,
                                        op_entry);
        if (opinfo) {
                if (opinfo->conn == NULL ||
@@ -171,8 +163,7 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
                        }
                }
        }
-
-       rcu_read_unlock();
+       up_read(&ci->m_lock);
 
        return opinfo;
 }
@@ -185,7 +176,7 @@ void opinfo_put(struct oplock_info *opinfo)
        if (!atomic_dec_and_test(&opinfo->refcount))
                return;
 
-       call_rcu(&opinfo->rcu_head, opinfo_free_rcu);
+       free_opinfo(opinfo);
 }
 
 static void opinfo_add(struct oplock_info *opinfo)
@@ -193,7 +184,7 @@ static void opinfo_add(struct oplock_info *opinfo)
        struct ksmbd_inode *ci = opinfo->o_fp->f_ci;
 
        down_write(&ci->m_lock);
-       list_add_rcu(&opinfo->op_entry, &ci->m_op_list);
+       list_add(&opinfo->op_entry, &ci->m_op_list);
        up_write(&ci->m_lock);
 }
 
@@ -207,7 +198,7 @@ static void opinfo_del(struct oplock_info *opinfo)
                write_unlock(&lease_list_lock);
        }
        down_write(&ci->m_lock);
-       list_del_rcu(&opinfo->op_entry);
+       list_del(&opinfo->op_entry);
        up_write(&ci->m_lock);
 }
 
@@ -1347,8 +1338,8 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
        ci = fp->f_ci;
        op = opinfo_get(fp);
 
-       rcu_read_lock();
-       list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) {
+       down_read(&ci->m_lock);
+       list_for_each_entry(brk_op, &ci->m_op_list, op_entry) {
                if (brk_op->conn == NULL)
                        continue;
 
@@ -1358,7 +1349,6 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
                if (ksmbd_conn_releasing(brk_op->conn))
                        continue;
 
-               rcu_read_unlock();
                if (brk_op->is_lease && (brk_op->o_lease->state &
                    (~(SMB2_LEASE_READ_CACHING_LE |
                                SMB2_LEASE_HANDLE_CACHING_LE)))) {
@@ -1388,9 +1378,8 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
                oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE, NULL);
 next:
                opinfo_put(brk_op);
-               rcu_read_lock();
        }
-       rcu_read_unlock();
+       up_read(&ci->m_lock);
 
        if (op)
                opinfo_put(op);
index 3f64f07872638e8a0bfb89342b90a24ff2935edc..9a56eaadd0dd8f063afde5a0f4abc2ce83792aa6 100644 (file)
@@ -71,7 +71,6 @@ struct oplock_info {
        struct list_head        lease_entry;
        wait_queue_head_t oplock_q; /* Other server threads */
        wait_queue_head_t oplock_brk; /* oplock breaking wait */
-       struct rcu_head         rcu_head;
 };
 
 struct lease_break_info {