Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+45 -39
+20
fs/smb/server/connection.c
··· 433 433 default_conn_ops.terminate_fn = ops->terminate_fn; 434 434 } 435 435 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 + 436 456 int ksmbd_conn_transport_init(void) 437 457 { 438 458 int ret;
+2
fs/smb/server/connection.h
··· 168 168 void ksmbd_conn_transport_destroy(void); 169 169 void ksmbd_conn_lock(struct ksmbd_conn *conn); 170 170 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); 171 173 172 174 /* 173 175 * WARNING
-3
fs/smb/server/ksmbd_work.c
··· 26 26 INIT_LIST_HEAD(&work->request_entry); 27 27 INIT_LIST_HEAD(&work->async_request_entry); 28 28 INIT_LIST_HEAD(&work->fp_entry); 29 - INIT_LIST_HEAD(&work->interim_entry); 30 29 INIT_LIST_HEAD(&work->aux_read_list); 31 30 work->iov_alloc_cnt = 4; 32 31 work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec), ··· 55 56 kfree(work->tr_buf); 56 57 kvfree(work->request_buf); 57 58 kfree(work->iov); 58 - if (!list_empty(&work->interim_entry)) 59 - list_del(&work->interim_entry); 60 59 61 60 if (work->async_id) 62 61 ksmbd_release_id(&work->conn->async_ida, work->async_id);
-1
fs/smb/server/ksmbd_work.h
··· 89 89 /* List head at conn->async_requests */ 90 90 struct list_head async_request_entry; 91 91 struct list_head fp_entry; 92 - struct list_head interim_entry; 93 92 }; 94 93 95 94 /**
+21 -22
fs/smb/server/oplock.c
··· 46 46 opinfo->fid = id; 47 47 opinfo->Tid = Tid; 48 48 INIT_LIST_HEAD(&opinfo->op_entry); 49 - INIT_LIST_HEAD(&opinfo->interim_list); 50 49 init_waitqueue_head(&opinfo->oplock_q); 51 50 init_waitqueue_head(&opinfo->oplock_brk); 52 51 atomic_set(&opinfo->refcount, 1); ··· 634 635 { 635 636 struct smb2_oplock_break *rsp = NULL; 636 637 struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); 638 + struct ksmbd_conn *conn = work->conn; 637 639 struct oplock_break_info *br_info = work->request_buf; 638 640 struct smb2_hdr *rsp_hdr; 639 641 struct ksmbd_file *fp; ··· 690 690 691 691 out: 692 692 ksmbd_free_work_struct(work); 693 + ksmbd_conn_r_count_dec(conn); 693 694 } 694 695 695 696 /** ··· 725 724 work->sess = opinfo->sess; 726 725 727 726 if (opinfo->op_state == OPLOCK_ACK_WAIT) { 727 + ksmbd_conn_r_count_inc(conn); 728 728 INIT_WORK(&work->work, __smb2_oplock_break_noti); 729 729 ksmbd_queue_work(work); 730 730 ··· 747 745 { 748 746 struct smb2_lease_break *rsp = NULL; 749 747 struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); 748 + struct ksmbd_conn *conn = work->conn; 750 749 struct lease_break_info *br_info = work->request_buf; 751 750 struct smb2_hdr *rsp_hdr; 752 751 ··· 794 791 795 792 out: 796 793 ksmbd_free_work_struct(work); 794 + ksmbd_conn_r_count_dec(conn); 797 795 } 798 796 799 797 /** ··· 807 803 static int smb2_lease_break_noti(struct oplock_info *opinfo) 808 804 { 809 805 struct ksmbd_conn *conn = opinfo->conn; 810 - struct list_head *tmp, *t; 811 806 struct ksmbd_work *work; 812 807 struct lease_break_info *br_info; 813 808 struct lease *lease = opinfo->o_lease; ··· 834 831 work->sess = opinfo->sess; 835 832 836 833 if (opinfo->op_state == OPLOCK_ACK_WAIT) { 837 - list_for_each_safe(tmp, t, &opinfo->interim_list) { 838 - struct ksmbd_work *in_work; 839 - 840 - in_work = list_entry(tmp, struct ksmbd_work, 841 - interim_entry); 842 - setup_async_work(in_work, NULL, NULL); 843 - smb2_send_interim_resp(in_work, STATUS_PENDING); 844 - list_del_init(&in_work->interim_entry); 845 - release_async_work(in_work); 846 - } 834 + ksmbd_conn_r_count_inc(conn); 847 835 INIT_WORK(&work->work, __smb2_lease_break_noti); 848 836 ksmbd_queue_work(work); 849 837 wait_for_break_ack(opinfo); ··· 865 871 } 866 872 } 867 873 868 - static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level) 874 + static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, 875 + struct ksmbd_work *in_work) 869 876 { 870 877 int err = 0; 871 878 ··· 909 914 } 910 915 911 916 if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE | 912 - SMB2_LEASE_HANDLE_CACHING_LE)) 917 + SMB2_LEASE_HANDLE_CACHING_LE)) { 918 + if (in_work) { 919 + setup_async_work(in_work, NULL, NULL); 920 + smb2_send_interim_resp(in_work, STATUS_PENDING); 921 + release_async_work(in_work); 922 + } 923 + 913 924 brk_opinfo->op_state = OPLOCK_ACK_WAIT; 914 - else 925 + } else 915 926 atomic_dec(&brk_opinfo->breaking_cnt); 916 927 } else { 917 928 err = oplock_break_pending(brk_opinfo, req_op_level); ··· 1117 1116 if (ksmbd_conn_releasing(opinfo->conn)) 1118 1117 continue; 1119 1118 1120 - oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE); 1119 + oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL); 1121 1120 opinfo_put(opinfo); 1122 1121 } 1123 1122 } ··· 1153 1152 1154 1153 if (ksmbd_conn_releasing(opinfo->conn)) 1155 1154 continue; 1156 - oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE); 1155 + oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL); 1157 1156 opinfo_put(opinfo); 1158 1157 } 1159 1158 } ··· 1253 1252 goto op_break_not_needed; 1254 1253 } 1255 1254 1256 - list_add(&work->interim_entry, &prev_opinfo->interim_list); 1257 - err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II); 1255 + err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II, work); 1258 1256 opinfo_put(prev_opinfo); 1259 1257 if (err == -ENOENT) 1260 1258 goto set_lev; ··· 1322 1322 } 1323 1323 1324 1324 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); 1327 1326 opinfo_put(brk_opinfo); 1328 1327 } 1329 1328 ··· 1385 1386 SMB2_LEASE_KEY_SIZE)) 1386 1387 goto next; 1387 1388 brk_op->open_trunc = is_trunc; 1388 - oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE); 1389 + oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE, NULL); 1389 1390 next: 1390 1391 opinfo_put(brk_op); 1391 1392 rcu_read_lock();
-1
fs/smb/server/oplock.h
··· 67 67 bool is_lease; 68 68 bool open_trunc; /* truncate on open */ 69 69 struct lease *o_lease; 70 - struct list_head interim_list; 71 70 struct list_head op_entry; 72 71 struct list_head lease_entry; 73 72 wait_queue_head_t oplock_q; /* Other server threads */
+2 -12
fs/smb/server/server.c
··· 270 270 271 271 ksmbd_conn_try_dequeue_request(work); 272 272 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); 284 274 } 285 275 286 276 /** ··· 300 310 conn->request_buf = NULL; 301 311 302 312 ksmbd_conn_enqueue_request(work); 303 - atomic_inc(&conn->r_count); 313 + ksmbd_conn_r_count_inc(conn); 304 314 /* update activity on connection */ 305 315 conn->last_active = jiffies; 306 316 INIT_WORK(&work->work, handle_ksmbd_work);