125 lines
3.5 KiB
Diff
125 lines
3.5 KiB
Diff
From 67437a4c66847a82ab538705b932144d4af28f4b Mon Sep 17 00:00:00 2001
|
|
From: Namjae Jeon <linkinjeon@kernel.org>
|
|
Date: Tue, 15 Apr 2025 09:30:21 +0900
|
|
Subject: ksmbd: fix use-after-free in smb_break_all_levII_oplock()
|
|
|
|
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 | 29 +++++++++--------------------
|
|
fs/smb/server/oplock.h | 1 -
|
|
2 files changed, 9 insertions(+), 21 deletions(-)
|
|
|
|
--- a/fs/smb/server/oplock.c
|
|
+++ b/fs/smb/server/oplock.c
|
|
@@ -129,14 +129,6 @@ static void free_opinfo(struct oplock_in
|
|
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_li
|
|
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_li
|
|
}
|
|
}
|
|
}
|
|
-
|
|
- rcu_read_unlock();
|
|
+ up_read(&ci->m_lock);
|
|
|
|
return opinfo;
|
|
}
|
|
@@ -185,7 +176,7 @@ void opinfo_put(struct oplock_info *opin
|
|
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_inf
|
|
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_inf
|
|
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 k
|
|
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 k
|
|
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 k
|
|
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);
|
|
--- a/fs/smb/server/oplock.h
|
|
+++ b/fs/smb/server/oplock.h
|
|
@@ -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 {
|