Skip to content

Commit 3632252

Browse files
namjaejeonSteve French
authored andcommitted
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. Cc: stable@vger.kernel.org Reported-by: Per Forlin <per.forlin@axis.com> Tested-by: Per Forlin <per.forlin@axis.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 0512a5f commit 3632252

File tree

1 file changed

+47
-25
lines changed

1 file changed

+47
-25
lines changed

fs/smb/server/oplock.c

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,42 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
157157
rcu_read_lock();
158158
opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info,
159159
op_entry);
160-
if (opinfo && !atomic_inc_not_zero(&opinfo->refcount))
161-
opinfo = NULL;
160+
if (opinfo) {
161+
if (!atomic_inc_not_zero(&opinfo->refcount))
162+
opinfo = NULL;
163+
else {
164+
atomic_inc(&opinfo->conn->r_count);
165+
if (ksmbd_conn_releasing(opinfo->conn)) {
166+
atomic_dec(&opinfo->conn->r_count);
167+
atomic_dec(&opinfo->refcount);
168+
opinfo = NULL;
169+
}
170+
}
171+
}
172+
162173
rcu_read_unlock();
163174

164175
return opinfo;
165176
}
166177

178+
static void opinfo_conn_put(struct oplock_info *opinfo)
179+
{
180+
struct ksmbd_conn *conn;
181+
182+
if (!opinfo)
183+
return;
184+
185+
conn = opinfo->conn;
186+
/*
187+
* Checking waitqueue to dropping pending requests on
188+
* disconnection. waitqueue_active is safe because it
189+
* uses atomic operation for condition.
190+
*/
191+
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
192+
wake_up(&conn->r_count_q);
193+
opinfo_put(opinfo);
194+
}
195+
167196
void opinfo_put(struct oplock_info *opinfo)
168197
{
169198
if (!atomic_dec_and_test(&opinfo->refcount))
@@ -666,13 +695,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
666695

667696
out:
668697
ksmbd_free_work_struct(work);
669-
/*
670-
* Checking waitqueue to dropping pending requests on
671-
* disconnection. waitqueue_active is safe because it
672-
* uses atomic operation for condition.
673-
*/
674-
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
675-
wake_up(&conn->r_count_q);
676698
}
677699

678700
/**
@@ -706,7 +728,6 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo)
706728
work->conn = conn;
707729
work->sess = opinfo->sess;
708730

709-
atomic_inc(&conn->r_count);
710731
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
711732
INIT_WORK(&work->work, __smb2_oplock_break_noti);
712733
ksmbd_queue_work(work);
@@ -776,13 +797,6 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
776797

777798
out:
778799
ksmbd_free_work_struct(work);
779-
/*
780-
* Checking waitqueue to dropping pending requests on
781-
* disconnection. waitqueue_active is safe because it
782-
* uses atomic operation for condition.
783-
*/
784-
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
785-
wake_up(&conn->r_count_q);
786800
}
787801

788802
/**
@@ -822,7 +836,6 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
822836
work->conn = conn;
823837
work->sess = opinfo->sess;
824838

825-
atomic_inc(&conn->r_count);
826839
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
827840
list_for_each_safe(tmp, t, &opinfo->interim_list) {
828841
struct ksmbd_work *in_work;
@@ -1144,28 +1157,30 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
11441157
}
11451158
prev_opinfo = opinfo_get_list(ci);
11461159
if (!prev_opinfo ||
1147-
(prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx))
1160+
(prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) {
1161+
opinfo_conn_put(prev_opinfo);
11481162
goto set_lev;
1163+
}
11491164
prev_op_has_lease = prev_opinfo->is_lease;
11501165
if (prev_op_has_lease)
11511166
prev_op_state = prev_opinfo->o_lease->state;
11521167

11531168
if (share_ret < 0 &&
11541169
prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
11551170
err = share_ret;
1156-
opinfo_put(prev_opinfo);
1171+
opinfo_conn_put(prev_opinfo);
11571172
goto err_out;
11581173
}
11591174

11601175
if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
11611176
prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
1162-
opinfo_put(prev_opinfo);
1177+
opinfo_conn_put(prev_opinfo);
11631178
goto op_break_not_needed;
11641179
}
11651180

11661181
list_add(&work->interim_entry, &prev_opinfo->interim_list);
11671182
err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II);
1168-
opinfo_put(prev_opinfo);
1183+
opinfo_conn_put(prev_opinfo);
11691184
if (err == -ENOENT)
11701185
goto set_lev;
11711186
/* Check all oplock was freed by close */
@@ -1228,14 +1243,14 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work,
12281243
return;
12291244
if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
12301245
brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
1231-
opinfo_put(brk_opinfo);
1246+
opinfo_conn_put(brk_opinfo);
12321247
return;
12331248
}
12341249

12351250
brk_opinfo->open_trunc = is_trunc;
12361251
list_add(&work->interim_entry, &brk_opinfo->interim_list);
12371252
oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II);
1238-
opinfo_put(brk_opinfo);
1253+
opinfo_conn_put(brk_opinfo);
12391254
}
12401255

12411256
/**
@@ -1263,6 +1278,13 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
12631278
list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) {
12641279
if (!atomic_inc_not_zero(&brk_op->refcount))
12651280
continue;
1281+
1282+
atomic_inc(&brk_op->conn->r_count);
1283+
if (ksmbd_conn_releasing(brk_op->conn)) {
1284+
atomic_dec(&brk_op->conn->r_count);
1285+
continue;
1286+
}
1287+
12661288
rcu_read_unlock();
12671289
if (brk_op->is_lease && (brk_op->o_lease->state &
12681290
(~(SMB2_LEASE_READ_CACHING_LE |
@@ -1292,7 +1314,7 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
12921314
brk_op->open_trunc = is_trunc;
12931315
oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE);
12941316
next:
1295-
opinfo_put(brk_op);
1317+
opinfo_conn_put(brk_op);
12961318
rcu_read_lock();
12971319
}
12981320
rcu_read_unlock();

0 commit comments

Comments
 (0)