Skip to content

Commit 11fc883

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 11fc883

File tree

1 file changed

+46
-33
lines changed

1 file changed

+46
-33
lines changed

oplock.c

+46-33
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,41 @@ 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+
opinfo = NULL;
174+
}
175+
}
176+
}
177+
168178
rcu_read_unlock();
169179

170180
return opinfo;
171181
}
172182

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

764792
ksmbd_conn_write(work);
765793
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);
773794
}
774795

775796
/**
@@ -791,7 +812,6 @@ static int smb1_oplock_break_noti(struct oplock_info *opinfo)
791812
work->request_buf = (char *)opinfo;
792813
work->conn = conn;
793814

794-
atomic_inc(&conn->r_count);
795815
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
796816
INIT_WORK(&work->work, __smb1_oplock_break_noti);
797817
ksmbd_queue_work(work);
@@ -876,13 +896,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
876896

877897
out:
878898
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);
886899
}
887900

888901
/**
@@ -916,7 +929,6 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo)
916929
work->conn = conn;
917930
work->sess = opinfo->sess;
918931

919-
atomic_inc(&conn->r_count);
920932
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
921933
INIT_WORK(&work->work, __smb2_oplock_break_noti);
922934
ksmbd_queue_work(work);
@@ -986,13 +998,6 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
986998

987999
out:
9881000
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);
9961001
}
9971002

9981003
/**
@@ -1032,7 +1037,6 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
10321037
work->conn = conn;
10331038
work->sess = opinfo->sess;
10341039

1035-
atomic_inc(&conn->r_count);
10361040
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
10371041
list_for_each_safe(tmp, t, &opinfo->interim_list) {
10381042
struct ksmbd_work *in_work;
@@ -1368,28 +1372,30 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
13681372
}
13691373
prev_opinfo = opinfo_get_list(ci);
13701374
if (!prev_opinfo ||
1371-
(prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx))
1375+
(prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) {
1376+
opinfo_conn_put(prev_opinfo);
13721377
goto set_lev;
1378+
}
13731379
prev_op_has_lease = prev_opinfo->is_lease;
13741380
if (prev_op_has_lease)
13751381
prev_op_state = prev_opinfo->o_lease->state;
13761382

13771383
if (share_ret < 0 &&
13781384
prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
13791385
err = share_ret;
1380-
opinfo_put(prev_opinfo);
1386+
opinfo_conn_put(prev_opinfo);
13811387
goto err_out;
13821388
}
13831389

13841390
if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
13851391
prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
1386-
opinfo_put(prev_opinfo);
1392+
opinfo_conn_put(prev_opinfo);
13871393
goto op_break_not_needed;
13881394
}
13891395

13901396
list_add(&work->interim_entry, &prev_opinfo->interim_list);
13911397
err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II);
1392-
opinfo_put(prev_opinfo);
1398+
opinfo_conn_put(prev_opinfo);
13931399
if (err == -ENOENT)
13941400
goto set_lev;
13951401
/* Check all oplock was freed by close */
@@ -1452,14 +1458,14 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work,
14521458
return;
14531459
if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
14541460
brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
1455-
opinfo_put(brk_opinfo);
1461+
opinfo_conn_put(brk_opinfo);
14561462
return;
14571463
}
14581464

14591465
brk_opinfo->open_trunc = is_trunc;
14601466
list_add(&work->interim_entry, &brk_opinfo->interim_list);
14611467
oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II);
1462-
opinfo_put(brk_opinfo);
1468+
opinfo_conn_put(brk_opinfo);
14631469
}
14641470

14651471
/**
@@ -1487,6 +1493,13 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
14871493
list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) {
14881494
if (!atomic_inc_not_zero(&brk_op->refcount))
14891495
continue;
1496+
1497+
atomic_inc(&brk_op->conn->r_count);
1498+
if (ksmbd_conn_releasing(brk_op->conn)) {
1499+
atomic_dec(&brk_op->conn->r_count);
1500+
continue;
1501+
}
1502+
14901503
rcu_read_unlock();
14911504

14921505
#ifdef CONFIG_SMB_INSECURE_SERVER
@@ -1547,7 +1560,7 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
15471560
brk_op->open_trunc = is_trunc;
15481561
oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE);
15491562
next:
1550-
opinfo_put(brk_op);
1563+
opinfo_conn_put(brk_op);
15511564
rcu_read_lock();
15521565
}
15531566
rcu_read_unlock();

0 commit comments

Comments
 (0)