Skip to content

Commit 3571e8b

Browse files
committed
Merge tag 'v6.14-rc6-smb3-server-fixes' of git://git.samba.org/ksmbd
Pull smb server fixes from Steve French: - Two fixes for oplock break/lease races * tag 'v6.14-rc6-smb3-server-fixes' of git://git.samba.org/ksmbd: ksmbd: prevent connection release during oplock break notification ksmbd: fix use-after-free in ksmbd_free_work_struct
2 parents a29967b + 3aa660c commit 3571e8b

File tree

7 files changed

+45
-39
lines changed

7 files changed

+45
-39
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/ksmbd_work.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void)
2626
INIT_LIST_HEAD(&work->request_entry);
2727
INIT_LIST_HEAD(&work->async_request_entry);
2828
INIT_LIST_HEAD(&work->fp_entry);
29-
INIT_LIST_HEAD(&work->interim_entry);
3029
INIT_LIST_HEAD(&work->aux_read_list);
3130
work->iov_alloc_cnt = 4;
3231
work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec),
@@ -56,8 +55,6 @@ void ksmbd_free_work_struct(struct ksmbd_work *work)
5655
kfree(work->tr_buf);
5756
kvfree(work->request_buf);
5857
kfree(work->iov);
59-
if (!list_empty(&work->interim_entry))
60-
list_del(&work->interim_entry);
6158

6259
if (work->async_id)
6360
ksmbd_release_id(&work->conn->async_ida, work->async_id);

fs/smb/server/ksmbd_work.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ struct ksmbd_work {
8989
/* List head at conn->async_requests */
9090
struct list_head async_request_entry;
9191
struct list_head fp_entry;
92-
struct list_head interim_entry;
9392
};
9493

9594
/**

fs/smb/server/oplock.c

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
4646
opinfo->fid = id;
4747
opinfo->Tid = Tid;
4848
INIT_LIST_HEAD(&opinfo->op_entry);
49-
INIT_LIST_HEAD(&opinfo->interim_list);
5049
init_waitqueue_head(&opinfo->oplock_q);
5150
init_waitqueue_head(&opinfo->oplock_brk);
5251
atomic_set(&opinfo->refcount, 1);
@@ -635,6 +634,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
635634
{
636635
struct smb2_oplock_break *rsp = NULL;
637636
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
637+
struct ksmbd_conn *conn = work->conn;
638638
struct oplock_break_info *br_info = work->request_buf;
639639
struct smb2_hdr *rsp_hdr;
640640
struct ksmbd_file *fp;
@@ -690,6 +690,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
690690

691691
out:
692692
ksmbd_free_work_struct(work);
693+
ksmbd_conn_r_count_dec(conn);
693694
}
694695

695696
/**
@@ -724,6 +725,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo)
724725
work->sess = opinfo->sess;
725726

726727
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
728+
ksmbd_conn_r_count_inc(conn);
727729
INIT_WORK(&work->work, __smb2_oplock_break_noti);
728730
ksmbd_queue_work(work);
729731

@@ -745,6 +747,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
745747
{
746748
struct smb2_lease_break *rsp = NULL;
747749
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
750+
struct ksmbd_conn *conn = work->conn;
748751
struct lease_break_info *br_info = work->request_buf;
749752
struct smb2_hdr *rsp_hdr;
750753

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

792795
out:
793796
ksmbd_free_work_struct(work);
797+
ksmbd_conn_r_count_dec(conn);
794798
}
795799

796800
/**
@@ -803,7 +807,6 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
803807
static int smb2_lease_break_noti(struct oplock_info *opinfo)
804808
{
805809
struct ksmbd_conn *conn = opinfo->conn;
806-
struct list_head *tmp, *t;
807810
struct ksmbd_work *work;
808811
struct lease_break_info *br_info;
809812
struct lease *lease = opinfo->o_lease;
@@ -831,16 +834,7 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
831834
work->sess = opinfo->sess;
832835

833836
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
834-
list_for_each_safe(tmp, t, &opinfo->interim_list) {
835-
struct ksmbd_work *in_work;
836-
837-
in_work = list_entry(tmp, struct ksmbd_work,
838-
interim_entry);
839-
setup_async_work(in_work, NULL, NULL);
840-
smb2_send_interim_resp(in_work, STATUS_PENDING);
841-
list_del_init(&in_work->interim_entry);
842-
release_async_work(in_work);
843-
}
837+
ksmbd_conn_r_count_inc(conn);
844838
INIT_WORK(&work->work, __smb2_lease_break_noti);
845839
ksmbd_queue_work(work);
846840
wait_for_break_ack(opinfo);
@@ -871,7 +865,8 @@ static void wait_lease_breaking(struct oplock_info *opinfo)
871865
}
872866
}
873867

874-
static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
868+
static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level,
869+
struct ksmbd_work *in_work)
875870
{
876871
int err = 0;
877872

@@ -914,9 +909,15 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
914909
}
915910

916911
if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE |
917-
SMB2_LEASE_HANDLE_CACHING_LE))
912+
SMB2_LEASE_HANDLE_CACHING_LE)) {
913+
if (in_work) {
914+
setup_async_work(in_work, NULL, NULL);
915+
smb2_send_interim_resp(in_work, STATUS_PENDING);
916+
release_async_work(in_work);
917+
}
918+
918919
brk_opinfo->op_state = OPLOCK_ACK_WAIT;
919-
else
920+
} else
920921
atomic_dec(&brk_opinfo->breaking_cnt);
921922
} else {
922923
err = oplock_break_pending(brk_opinfo, req_op_level);
@@ -1116,7 +1117,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
11161117
if (ksmbd_conn_releasing(opinfo->conn))
11171118
continue;
11181119

1119-
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
1120+
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL);
11201121
opinfo_put(opinfo);
11211122
}
11221123
}
@@ -1152,7 +1153,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
11521153

11531154
if (ksmbd_conn_releasing(opinfo->conn))
11541155
continue;
1155-
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
1156+
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL);
11561157
opinfo_put(opinfo);
11571158
}
11581159
}
@@ -1252,8 +1253,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
12521253
goto op_break_not_needed;
12531254
}
12541255

1255-
list_add(&work->interim_entry, &prev_opinfo->interim_list);
1256-
err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II);
1256+
err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II, work);
12571257
opinfo_put(prev_opinfo);
12581258
if (err == -ENOENT)
12591259
goto set_lev;
@@ -1322,8 +1322,7 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work,
13221322
}
13231323

13241324
brk_opinfo->open_trunc = is_trunc;
1325-
list_add(&work->interim_entry, &brk_opinfo->interim_list);
1326-
oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II);
1325+
oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work);
13271326
opinfo_put(brk_opinfo);
13281327
}
13291328

@@ -1386,7 +1385,7 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
13861385
SMB2_LEASE_KEY_SIZE))
13871386
goto next;
13881387
brk_op->open_trunc = is_trunc;
1389-
oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE);
1388+
oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE, NULL);
13901389
next:
13911390
opinfo_put(brk_op);
13921391
rcu_read_lock();

fs/smb/server/oplock.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ struct oplock_info {
6767
bool is_lease;
6868
bool open_trunc; /* truncate on open */
6969
struct lease *o_lease;
70-
struct list_head interim_list;
7170
struct list_head op_entry;
7271
struct list_head lease_entry;
7372
wait_queue_head_t oplock_q; /* Other server threads */

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)