Skip to content

Commit 8afb5df

Browse files
committed
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>
1 parent f8d9cf2 commit 8afb5df

File tree

4 files changed

+34
-14
lines changed

4 files changed

+34
-14
lines changed

connection.c

+20
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,26 @@ void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops)
485485
default_conn_ops.terminate_fn = ops->terminate_fn;
486486
}
487487

488+
void ksmbd_conn_r_count_inc(struct ksmbd_conn *conn)
489+
{
490+
atomic_inc(&conn->r_count);
491+
}
492+
493+
void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn)
494+
{
495+
/*
496+
* Checking waitqueue to dropping pending requests on
497+
* disconnection. waitqueue_active is safe because it
498+
* uses atomic operation for condition.
499+
*/
500+
atomic_inc(&conn->refcnt);
501+
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
502+
wake_up(&conn->r_count_q);
503+
504+
if (atomic_dec_and_test(&conn->refcnt))
505+
kfree(conn);
506+
}
507+
488508
int ksmbd_conn_transport_init(void)
489509
{
490510
int ret;

connection.h

+2
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

oplock.c

+10-2
Original file line numberDiff line numberDiff line change
@@ -740,8 +740,7 @@ static void __smb1_oplock_break_noti(struct work_struct *wk)
740740

741741
if (allocate_interim_rsp_buf(work)) {
742742
pr_err("smb_allocate_rsp_buf failed! ");
743-
ksmbd_free_work_struct(work);
744-
return;
743+
goto out;
745744
}
746745

747746
/* Init response header */
@@ -786,7 +785,9 @@ static void __smb1_oplock_break_noti(struct work_struct *wk)
786785
req->Fid, req->OplockLevel);
787786

788787
ksmbd_conn_write(work);
788+
out:
789789
ksmbd_free_work_struct(work);
790+
ksmbd_conn_r_count_dec(conn);
790791
}
791792

792793
/**
@@ -809,6 +810,7 @@ static int smb1_oplock_break_noti(struct oplock_info *opinfo)
809810
work->conn = conn;
810811

811812
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
813+
ksmbd_conn_r_count_inc(conn);
812814
INIT_WORK(&work->work, __smb1_oplock_break_noti);
813815
ksmbd_queue_work(work);
814816

@@ -836,6 +838,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
836838
{
837839
struct smb2_oplock_break *rsp = NULL;
838840
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
841+
struct ksmbd_conn *conn = work->conn;
839842
struct oplock_break_info *br_info = work->request_buf;
840843
struct smb2_hdr *rsp_hdr;
841844
struct ksmbd_file *fp;
@@ -891,6 +894,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
891894

892895
out:
893896
ksmbd_free_work_struct(work);
897+
ksmbd_conn_r_count_dec(conn);
894898
}
895899

896900
/**
@@ -925,6 +929,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo)
925929
work->sess = opinfo->sess;
926930

927931
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
932+
ksmbd_conn_r_count_inc(conn);
928933
INIT_WORK(&work->work, __smb2_oplock_break_noti);
929934
ksmbd_queue_work(work);
930935

@@ -946,6 +951,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
946951
{
947952
struct smb2_lease_break *rsp = NULL;
948953
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
954+
struct ksmbd_conn *conn = work->conn;
949955
struct lease_break_info *br_info = work->request_buf;
950956
struct smb2_hdr *rsp_hdr;
951957

@@ -992,6 +998,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
992998

993999
out:
9941000
ksmbd_free_work_struct(work);
1001+
ksmbd_conn_r_count_dec(conn);
9951002
}
9961003

9971004
/**
@@ -1031,6 +1038,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
10311038
work->sess = opinfo->sess;
10321039

10331040
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
1041+
ksmbd_conn_r_count_inc(conn);
10341042
INIT_WORK(&work->work, __smb2_lease_break_noti);
10351043
ksmbd_queue_work(work);
10361044
wait_for_break_ack(opinfo);

server.c

+2-12
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)