Skip to content

Commit 3aa660c

Browse files
namjaejeonSteve French
authored andcommitted
ksmbd: prevent connection release during oplock break notification
ksmbd_work could be freed when after connection release. Increment r_count of ksmbd_conn to indicate that requests are not finished yet and to not release the connection. 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 bb39ed4 commit 3aa660c

File tree

4 files changed

+30
-12
lines changed

4 files changed

+30
-12
lines changed

fs/smb/server/connection.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,26 @@ void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops)
433433
default_conn_ops.terminate_fn = ops->terminate_fn;
434434
}
435435

436+
void ksmbd_conn_r_count_inc(struct ksmbd_conn *conn)
437+
{
438+
atomic_inc(&conn->r_count);
439+
}
440+
441+
void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn)
442+
{
443+
/*
444+
* Checking waitqueue to dropping pending requests on
445+
* disconnection. waitqueue_active is safe because it
446+
* uses atomic operation for condition.
447+
*/
448+
atomic_inc(&conn->refcnt);
449+
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
450+
wake_up(&conn->r_count_q);
451+
452+
if (atomic_dec_and_test(&conn->refcnt))
453+
kfree(conn);
454+
}
455+
436456
int ksmbd_conn_transport_init(void)
437457
{
438458
int ret;

fs/smb/server/connection.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ int ksmbd_conn_transport_init(void);
168168
void ksmbd_conn_transport_destroy(void);
169169
void ksmbd_conn_lock(struct ksmbd_conn *conn);
170170
void ksmbd_conn_unlock(struct ksmbd_conn *conn);
171+
void ksmbd_conn_r_count_inc(struct ksmbd_conn *conn);
172+
void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn);
171173

172174
/*
173175
* WARNING

fs/smb/server/oplock.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
634634
{
635635
struct smb2_oplock_break *rsp = NULL;
636636
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
637+
struct ksmbd_conn *conn = work->conn;
637638
struct oplock_break_info *br_info = work->request_buf;
638639
struct smb2_hdr *rsp_hdr;
639640
struct ksmbd_file *fp;
@@ -689,6 +690,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
689690

690691
out:
691692
ksmbd_free_work_struct(work);
693+
ksmbd_conn_r_count_dec(conn);
692694
}
693695

694696
/**
@@ -723,6 +725,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo)
723725
work->sess = opinfo->sess;
724726

725727
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
728+
ksmbd_conn_r_count_inc(conn);
726729
INIT_WORK(&work->work, __smb2_oplock_break_noti);
727730
ksmbd_queue_work(work);
728731

@@ -744,6 +747,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
744747
{
745748
struct smb2_lease_break *rsp = NULL;
746749
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
750+
struct ksmbd_conn *conn = work->conn;
747751
struct lease_break_info *br_info = work->request_buf;
748752
struct smb2_hdr *rsp_hdr;
749753

@@ -790,6 +794,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
790794

791795
out:
792796
ksmbd_free_work_struct(work);
797+
ksmbd_conn_r_count_dec(conn);
793798
}
794799

795800
/**
@@ -829,6 +834,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
829834
work->sess = opinfo->sess;
830835

831836
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
837+
ksmbd_conn_r_count_inc(conn);
832838
INIT_WORK(&work->work, __smb2_lease_break_noti);
833839
ksmbd_queue_work(work);
834840
wait_for_break_ack(opinfo);

fs/smb/server/server.c

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -270,17 +270,7 @@ static void handle_ksmbd_work(struct work_struct *wk)
270270

271271
ksmbd_conn_try_dequeue_request(work);
272272
ksmbd_free_work_struct(work);
273-
/*
274-
* Checking waitqueue to dropping pending requests on
275-
* disconnection. waitqueue_active is safe because it
276-
* uses atomic operation for condition.
277-
*/
278-
atomic_inc(&conn->refcnt);
279-
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
280-
wake_up(&conn->r_count_q);
281-
282-
if (atomic_dec_and_test(&conn->refcnt))
283-
kfree(conn);
273+
ksmbd_conn_r_count_dec(conn);
284274
}
285275

286276
/**
@@ -310,7 +300,7 @@ static int queue_ksmbd_work(struct ksmbd_conn *conn)
310300
conn->request_buf = NULL;
311301

312302
ksmbd_conn_enqueue_request(work);
313-
atomic_inc(&conn->r_count);
303+
ksmbd_conn_r_count_inc(conn);
314304
/* update activity on connection */
315305
conn->last_active = jiffies;
316306
INIT_WORK(&work->work, handle_ksmbd_work);

0 commit comments

Comments
 (0)