Skip to content

Commit 11aa06f

Browse files
committed
ksmbd: fix UAF issue from opinfo->conn
If opinfo->conn is another connection and while ksmbd send oplock break request to cient on current connection, The connection for opinfo->conn can be disconnect and conn could be freed. When sending oplock break request, this ksmbd_conn can be used and cause user-after-free issue. When getting opinfo from the list, ksmbd check connection is being released. If it is not released, Increase ->r_count to wait that connection is freed. Reported-by: Per Forlin <[email protected]> Tested-by: Per Forlin <[email protected]> Signed-off-by: Namjae Jeon <[email protected]>
1 parent 8bd0ddf commit 11aa06f

File tree

1 file changed

+47
-33
lines changed

1 file changed

+47
-33
lines changed

oplock.c

+47-33
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,42 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
163163
rcu_read_lock();
164164
opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info,
165165
op_entry);
166-
if (opinfo && !atomic_inc_not_zero(&opinfo->refcount))
167-
opinfo = NULL;
166+
if (opinfo) {
167+
if (!atomic_inc_not_zero(&opinfo->refcount))
168+
opinfo = NULL;
169+
else {
170+
atomic_inc(&opinfo->conn->r_count);
171+
if (ksmbd_conn_releasing(opinfo->conn)) {
172+
atomic_dec(&opinfo->conn->r_count);
173+
atomic_dec(&opinfo->refcount);
174+
opinfo = NULL;
175+
}
176+
}
177+
}
178+
168179
rcu_read_unlock();
169180

170181
return opinfo;
171182
}
172183

184+
static void opinfo_conn_put(struct oplock_info *opinfo)
185+
{
186+
struct ksmbd_conn *conn;
187+
188+
if (!opinfo)
189+
return;
190+
191+
conn = opinfo->conn;
192+
/*
193+
* Checking waitqueue to dropping pending requests on
194+
* disconnection. waitqueue_active is safe because it
195+
* uses atomic operation for condition.
196+
*/
197+
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
198+
wake_up(&conn->r_count_q);
199+
opinfo_put(opinfo);
200+
}
201+
173202
void opinfo_put(struct oplock_info *opinfo)
174203
{
175204
if (!atomic_dec_and_test(&opinfo->refcount))
@@ -763,13 +792,6 @@ static void __smb1_oplock_break_noti(struct work_struct *wk)
763792

764793
ksmbd_conn_write(work);
765794
ksmbd_free_work_struct(work);
766-
/*
767-
* Checking waitqueue to dropping pending requests on
768-
* disconnection. waitqueue_active is safe because it
769-
* uses atomic operation for condition.
770-
*/
771-
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
772-
wake_up(&conn->r_count_q);
773795
}
774796

775797
/**
@@ -791,7 +813,6 @@ static int smb1_oplock_break_noti(struct oplock_info *opinfo)
791813
work->request_buf = (char *)opinfo;
792814
work->conn = conn;
793815

794-
atomic_inc(&conn->r_count);
795816
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
796817
INIT_WORK(&work->work, __smb1_oplock_break_noti);
797818
ksmbd_queue_work(work);
@@ -876,13 +897,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
876897

877898
out:
878899
ksmbd_free_work_struct(work);
879-
/*
880-
* Checking waitqueue to dropping pending requests on
881-
* disconnection. waitqueue_active is safe because it
882-
* uses atomic operation for condition.
883-
*/
884-
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
885-
wake_up(&conn->r_count_q);
886900
}
887901

888902
/**
@@ -916,7 +930,6 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo)
916930
work->conn = conn;
917931
work->sess = opinfo->sess;
918932

919-
atomic_inc(&conn->r_count);
920933
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
921934
INIT_WORK(&work->work, __smb2_oplock_break_noti);
922935
ksmbd_queue_work(work);
@@ -986,13 +999,6 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
986999

9871000
out:
9881001
ksmbd_free_work_struct(work);
989-
/*
990-
* Checking waitqueue to dropping pending requests on
991-
* disconnection. waitqueue_active is safe because it
992-
* uses atomic operation for condition.
993-
*/
994-
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
995-
wake_up(&conn->r_count_q);
9961002
}
9971003

9981004
/**
@@ -1032,7 +1038,6 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
10321038
work->conn = conn;
10331039
work->sess = opinfo->sess;
10341040

1035-
atomic_inc(&conn->r_count);
10361041
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
10371042
list_for_each_safe(tmp, t, &opinfo->interim_list) {
10381043
struct ksmbd_work *in_work;
@@ -1368,28 +1373,30 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
13681373
}
13691374
prev_opinfo = opinfo_get_list(ci);
13701375
if (!prev_opinfo ||
1371-
(prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx))
1376+
(prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) {
1377+
opinfo_conn_put(prev_opinfo);
13721378
goto set_lev;
1379+
}
13731380
prev_op_has_lease = prev_opinfo->is_lease;
13741381
if (prev_op_has_lease)
13751382
prev_op_state = prev_opinfo->o_lease->state;
13761383

13771384
if (share_ret < 0 &&
13781385
prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
13791386
err = share_ret;
1380-
opinfo_put(prev_opinfo);
1387+
opinfo_conn_put(prev_opinfo);
13811388
goto err_out;
13821389
}
13831390

13841391
if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
13851392
prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
1386-
opinfo_put(prev_opinfo);
1393+
opinfo_conn_put(prev_opinfo);
13871394
goto op_break_not_needed;
13881395
}
13891396

13901397
list_add(&work->interim_entry, &prev_opinfo->interim_list);
13911398
err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II);
1392-
opinfo_put(prev_opinfo);
1399+
opinfo_conn_put(prev_opinfo);
13931400
if (err == -ENOENT)
13941401
goto set_lev;
13951402
/* Check all oplock was freed by close */
@@ -1452,14 +1459,14 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work,
14521459
return;
14531460
if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
14541461
brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
1455-
opinfo_put(brk_opinfo);
1462+
opinfo_conn_put(brk_opinfo);
14561463
return;
14571464
}
14581465

14591466
brk_opinfo->open_trunc = is_trunc;
14601467
list_add(&work->interim_entry, &brk_opinfo->interim_list);
14611468
oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II);
1462-
opinfo_put(brk_opinfo);
1469+
opinfo_conn_put(brk_opinfo);
14631470
}
14641471

14651472
/**
@@ -1487,6 +1494,13 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
14871494
list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) {
14881495
if (!atomic_inc_not_zero(&brk_op->refcount))
14891496
continue;
1497+
1498+
atomic_inc(&brk_op->conn->r_count);
1499+
if (ksmbd_conn_releasing(brk_op->conn)) {
1500+
atomic_dec(&brk_op->conn->r_count);
1501+
continue;
1502+
}
1503+
14901504
rcu_read_unlock();
14911505

14921506
#ifdef CONFIG_SMB_INSECURE_SERVER
@@ -1547,7 +1561,7 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
15471561
brk_op->open_trunc = is_trunc;
15481562
oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE);
15491563
next:
1550-
opinfo_put(brk_op);
1564+
opinfo_conn_put(brk_op);
15511565
rcu_read_lock();
15521566
}
15531567
rcu_read_unlock();

0 commit comments

Comments
 (0)