Skip to content

Commit 18b4fac

Browse files
namjaejeonSteve French
authored andcommitted
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>
1 parent 21a4e47 commit 18b4fac

File tree

2 files changed

+9
-21
lines changed

2 files changed

+9
-21
lines changed

fs/smb/server/oplock.c

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,6 @@ static void free_opinfo(struct oplock_info *opinfo)
129129
kfree(opinfo);
130130
}
131131

132-
static inline void opinfo_free_rcu(struct rcu_head *rcu_head)
133-
{
134-
struct oplock_info *opinfo;
135-
136-
opinfo = container_of(rcu_head, struct oplock_info, rcu_head);
137-
free_opinfo(opinfo);
138-
}
139-
140132
struct oplock_info *opinfo_get(struct ksmbd_file *fp)
141133
{
142134
struct oplock_info *opinfo;
@@ -157,8 +149,8 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
157149
if (list_empty(&ci->m_op_list))
158150
return NULL;
159151

160-
rcu_read_lock();
161-
opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info,
152+
down_read(&ci->m_lock);
153+
opinfo = list_first_entry(&ci->m_op_list, struct oplock_info,
162154
op_entry);
163155
if (opinfo) {
164156
if (opinfo->conn == NULL ||
@@ -171,8 +163,7 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
171163
}
172164
}
173165
}
174-
175-
rcu_read_unlock();
166+
up_read(&ci->m_lock);
176167

177168
return opinfo;
178169
}
@@ -185,15 +176,15 @@ void opinfo_put(struct oplock_info *opinfo)
185176
if (!atomic_dec_and_test(&opinfo->refcount))
186177
return;
187178

188-
call_rcu(&opinfo->rcu_head, opinfo_free_rcu);
179+
free_opinfo(opinfo);
189180
}
190181

191182
static void opinfo_add(struct oplock_info *opinfo)
192183
{
193184
struct ksmbd_inode *ci = opinfo->o_fp->f_ci;
194185

195186
down_write(&ci->m_lock);
196-
list_add_rcu(&opinfo->op_entry, &ci->m_op_list);
187+
list_add(&opinfo->op_entry, &ci->m_op_list);
197188
up_write(&ci->m_lock);
198189
}
199190

@@ -207,7 +198,7 @@ static void opinfo_del(struct oplock_info *opinfo)
207198
write_unlock(&lease_list_lock);
208199
}
209200
down_write(&ci->m_lock);
210-
list_del_rcu(&opinfo->op_entry);
201+
list_del(&opinfo->op_entry);
211202
up_write(&ci->m_lock);
212203
}
213204

@@ -1347,8 +1338,8 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
13471338
ci = fp->f_ci;
13481339
op = opinfo_get(fp);
13491340

1350-
rcu_read_lock();
1351-
list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) {
1341+
down_read(&ci->m_lock);
1342+
list_for_each_entry(brk_op, &ci->m_op_list, op_entry) {
13521343
if (brk_op->conn == NULL)
13531344
continue;
13541345

@@ -1358,7 +1349,6 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
13581349
if (ksmbd_conn_releasing(brk_op->conn))
13591350
continue;
13601351

1361-
rcu_read_unlock();
13621352
if (brk_op->is_lease && (brk_op->o_lease->state &
13631353
(~(SMB2_LEASE_READ_CACHING_LE |
13641354
SMB2_LEASE_HANDLE_CACHING_LE)))) {
@@ -1388,9 +1378,8 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
13881378
oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE, NULL);
13891379
next:
13901380
opinfo_put(brk_op);
1391-
rcu_read_lock();
13921381
}
1393-
rcu_read_unlock();
1382+
up_read(&ci->m_lock);
13941383

13951384
if (op)
13961385
opinfo_put(op);

fs/smb/server/oplock.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ struct oplock_info {
7171
struct list_head lease_entry;
7272
wait_queue_head_t oplock_q; /* Other server threads */
7373
wait_queue_head_t oplock_brk; /* oplock breaking wait */
74-
struct rcu_head rcu_head;
7574
};
7675

7776
struct lease_break_info {

0 commit comments

Comments
 (0)