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 'v7.0-rc5-ksmbd-srv-fixes' of git://git.samba.org/ksmbd

Pull smb server fixes from Steve French:

- Fix out of bounds write

- Fix for better calculating max output buffers

- Fix memory leaks in SMB2/SMB3 lock

- Fix use after free

- Multichannel fix

* tag 'v7.0-rc5-ksmbd-srv-fixes' of git://git.samba.org/ksmbd:
ksmbd: fix potencial OOB in get_file_all_info() for compound requests
ksmbd: replace hardcoded hdr2_len with offsetof() in smb2_calc_max_out_buf_len()
ksmbd: fix memory leaks and NULL deref in smb2_lock()
ksmbd: fix use-after-free and NULL deref in smb_grant_oplock()
ksmbd: do not expire session on binding failure

+97 -48
+45 -27
fs/smb/server/oplock.c
··· 82 82 spin_unlock(&lb->lb_lock); 83 83 } 84 84 85 - static void lb_add(struct lease_table *lb) 85 + static struct lease_table *alloc_lease_table(struct oplock_info *opinfo) 86 86 { 87 - write_lock(&lease_list_lock); 88 - list_add(&lb->l_entry, &lease_table_list); 89 - write_unlock(&lease_list_lock); 87 + struct lease_table *lb; 88 + 89 + lb = kmalloc_obj(struct lease_table, KSMBD_DEFAULT_GFP); 90 + if (!lb) 91 + return NULL; 92 + 93 + memcpy(lb->client_guid, opinfo->conn->ClientGUID, 94 + SMB2_CLIENT_GUID_SIZE); 95 + INIT_LIST_HEAD(&lb->lease_list); 96 + spin_lock_init(&lb->lb_lock); 97 + return lb; 90 98 } 91 99 92 100 static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) ··· 1050 1042 lease2->version = lease1->version; 1051 1043 } 1052 1044 1053 - static int add_lease_global_list(struct oplock_info *opinfo) 1045 + static void add_lease_global_list(struct oplock_info *opinfo, 1046 + struct lease_table *new_lb) 1054 1047 { 1055 1048 struct lease_table *lb; 1056 1049 1057 - read_lock(&lease_list_lock); 1050 + write_lock(&lease_list_lock); 1058 1051 list_for_each_entry(lb, &lease_table_list, l_entry) { 1059 1052 if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID, 1060 1053 SMB2_CLIENT_GUID_SIZE)) { 1061 1054 opinfo->o_lease->l_lb = lb; 1062 1055 lease_add_list(opinfo); 1063 - read_unlock(&lease_list_lock); 1064 - return 0; 1056 + write_unlock(&lease_list_lock); 1057 + kfree(new_lb); 1058 + return; 1065 1059 } 1066 1060 } 1067 - read_unlock(&lease_list_lock); 1068 1061 1069 - lb = kmalloc_obj(struct lease_table, KSMBD_DEFAULT_GFP); 1070 - if (!lb) 1071 - return -ENOMEM; 1072 - 1073 - memcpy(lb->client_guid, opinfo->conn->ClientGUID, 1074 - SMB2_CLIENT_GUID_SIZE); 1075 - INIT_LIST_HEAD(&lb->lease_list); 1076 - spin_lock_init(&lb->lb_lock); 1077 - opinfo->o_lease->l_lb = lb; 1062 + opinfo->o_lease->l_lb = new_lb; 1078 1063 lease_add_list(opinfo); 1079 - lb_add(lb); 1080 - return 0; 1064 + list_add(&new_lb->l_entry, &lease_table_list); 1065 + write_unlock(&lease_list_lock); 1081 1066 } 1082 1067 1083 1068 static void set_oplock_level(struct oplock_info *opinfo, int level, ··· 1190 1189 int err = 0; 1191 1190 struct oplock_info *opinfo = NULL, *prev_opinfo = NULL; 1192 1191 struct ksmbd_inode *ci = fp->f_ci; 1192 + struct lease_table *new_lb = NULL; 1193 1193 bool prev_op_has_lease; 1194 1194 __le32 prev_op_state = 0; 1195 1195 ··· 1293 1291 set_oplock_level(opinfo, req_op_level, lctx); 1294 1292 1295 1293 out: 1294 + /* 1295 + * Set o_fp before any publication so that concurrent readers 1296 + * (e.g. find_same_lease_key() on the lease list) that 1297 + * dereference opinfo->o_fp don't hit a NULL pointer. 1298 + * 1299 + * Keep the original publication order so concurrent opens can 1300 + * still observe the in-flight grant via ci->m_op_list, but make 1301 + * everything after opinfo_add() no-fail by preallocating any new 1302 + * lease_table first. 1303 + */ 1304 + opinfo->o_fp = fp; 1305 + if (opinfo->is_lease) { 1306 + new_lb = alloc_lease_table(opinfo); 1307 + if (!new_lb) { 1308 + err = -ENOMEM; 1309 + goto err_out; 1310 + } 1311 + } 1312 + 1296 1313 opinfo_count_inc(fp); 1297 1314 opinfo_add(opinfo, fp); 1298 1315 1299 - if (opinfo->is_lease) { 1300 - err = add_lease_global_list(opinfo); 1301 - if (err) 1302 - goto err_out; 1303 - } 1316 + if (opinfo->is_lease) 1317 + add_lease_global_list(opinfo, new_lb); 1304 1318 1305 1319 rcu_assign_pointer(fp->f_opinfo, opinfo); 1306 - opinfo->o_fp = fp; 1307 1320 1308 1321 return 0; 1309 1322 err_out: 1310 - __free_opinfo(opinfo); 1323 + kfree(new_lb); 1324 + opinfo_put(opinfo); 1311 1325 return err; 1312 1326 } 1313 1327
+52 -21
fs/smb/server/smb2pdu.c
··· 1939 1939 if (sess->user && sess->user->flags & KSMBD_USER_FLAG_DELAY_SESSION) 1940 1940 try_delay = true; 1941 1941 1942 - sess->last_active = jiffies; 1943 - sess->state = SMB2_SESSION_EXPIRED; 1942 + /* 1943 + * For binding requests, session belongs to another 1944 + * connection. Do not expire it. 1945 + */ 1946 + if (!(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) { 1947 + sess->last_active = jiffies; 1948 + sess->state = SMB2_SESSION_EXPIRED; 1949 + } 1944 1950 ksmbd_user_session_put(sess); 1945 1951 work->sess = NULL; 1946 1952 if (try_delay) { ··· 4452 4446 d_info.wptr = (char *)rsp->Buffer; 4453 4447 d_info.rptr = (char *)rsp->Buffer; 4454 4448 d_info.out_buf_len = 4455 - smb2_calc_max_out_buf_len(work, 8, 4456 - le32_to_cpu(req->OutputBufferLength)); 4449 + smb2_calc_max_out_buf_len(work, 4450 + offsetof(struct smb2_query_directory_rsp, Buffer), 4451 + le32_to_cpu(req->OutputBufferLength)); 4457 4452 if (d_info.out_buf_len < 0) { 4458 4453 rc = -EINVAL; 4459 4454 goto err_out; ··· 4721 4714 } 4722 4715 4723 4716 buf_free_len = 4724 - smb2_calc_max_out_buf_len(work, 8, 4725 - le32_to_cpu(req->OutputBufferLength)); 4717 + smb2_calc_max_out_buf_len(work, 4718 + offsetof(struct smb2_query_info_rsp, Buffer), 4719 + le32_to_cpu(req->OutputBufferLength)); 4726 4720 if (buf_free_len < 0) 4727 4721 return -EINVAL; 4728 4722 ··· 4940 4932 int conv_len; 4941 4933 char *filename; 4942 4934 u64 time; 4943 - int ret; 4935 + int ret, buf_free_len, filename_len; 4936 + struct smb2_query_info_req *req = ksmbd_req_buf_next(work); 4944 4937 4945 4938 if (!(fp->daccess & FILE_READ_ATTRIBUTES_LE)) { 4946 4939 ksmbd_debug(SMB, "no right to read the attributes : 0x%x\n", ··· 4952 4943 filename = convert_to_nt_pathname(work->tcon->share_conf, &fp->filp->f_path); 4953 4944 if (IS_ERR(filename)) 4954 4945 return PTR_ERR(filename); 4946 + 4947 + filename_len = strlen(filename); 4948 + buf_free_len = smb2_calc_max_out_buf_len(work, 4949 + offsetof(struct smb2_query_info_rsp, Buffer) + 4950 + offsetof(struct smb2_file_all_info, FileName), 4951 + le32_to_cpu(req->OutputBufferLength)); 4952 + if (buf_free_len < (filename_len + 1) * 2) { 4953 + kfree(filename); 4954 + return -EINVAL; 4955 + } 4955 4956 4956 4957 ret = vfs_getattr(&fp->filp->f_path, &stat, STATX_BASIC_STATS, 4957 4958 AT_STATX_SYNC_AS_STAT); ··· 5006 4987 file_info->Mode = fp->coption; 5007 4988 file_info->AlignmentRequirement = 0; 5008 4989 conv_len = smbConvertToUTF16((__le16 *)file_info->FileName, filename, 5009 - PATH_MAX, conn->local_nls, 0); 4990 + min(filename_len, PATH_MAX), 4991 + conn->local_nls, 0); 5010 4992 conv_len *= 2; 5011 4993 file_info->FileNameLength = cpu_to_le32(conv_len); 5012 4994 rsp->OutputBufferLength = ··· 5061 5041 file_info = (struct smb2_file_stream_info *)rsp->Buffer; 5062 5042 5063 5043 buf_free_len = 5064 - smb2_calc_max_out_buf_len(work, 8, 5065 - le32_to_cpu(req->OutputBufferLength)); 5044 + smb2_calc_max_out_buf_len(work, 5045 + offsetof(struct smb2_query_info_rsp, Buffer), 5046 + le32_to_cpu(req->OutputBufferLength)); 5066 5047 if (buf_free_len < 0) 5067 5048 goto out; 5068 5049 ··· 7607 7586 rc = vfs_lock_file(filp, smb_lock->cmd, flock, NULL); 7608 7587 skip: 7609 7588 if (smb_lock->flags & SMB2_LOCKFLAG_UNLOCK) { 7589 + locks_free_lock(flock); 7590 + kfree(smb_lock); 7610 7591 if (!rc) { 7611 7592 ksmbd_debug(SMB, "File unlocked\n"); 7612 7593 } else if (rc == -ENOENT) { 7613 7594 rsp->hdr.Status = STATUS_NOT_LOCKED; 7595 + err = rc; 7614 7596 goto out; 7615 7597 } 7616 - locks_free_lock(flock); 7617 - kfree(smb_lock); 7618 7598 } else { 7619 7599 if (rc == FILE_LOCK_DEFERRED) { 7620 7600 void **argv; ··· 7684 7662 spin_unlock(&work->conn->llist_lock); 7685 7663 ksmbd_debug(SMB, "successful in taking lock\n"); 7686 7664 } else { 7665 + locks_free_lock(flock); 7666 + kfree(smb_lock); 7667 + err = rc; 7687 7668 goto out; 7688 7669 } 7689 7670 } ··· 7717 7692 struct file_lock *rlock = NULL; 7718 7693 7719 7694 rlock = smb_flock_init(filp); 7720 - rlock->c.flc_type = F_UNLCK; 7721 - rlock->fl_start = smb_lock->start; 7722 - rlock->fl_end = smb_lock->end; 7695 + if (rlock) { 7696 + rlock->c.flc_type = F_UNLCK; 7697 + rlock->fl_start = smb_lock->start; 7698 + rlock->fl_end = smb_lock->end; 7723 7699 7724 - rc = vfs_lock_file(filp, F_SETLK, rlock, NULL); 7725 - if (rc) 7726 - pr_err("rollback unlock fail : %d\n", rc); 7700 + rc = vfs_lock_file(filp, F_SETLK, rlock, NULL); 7701 + if (rc) 7702 + pr_err("rollback unlock fail : %d\n", rc); 7703 + } else { 7704 + pr_err("rollback unlock alloc failed\n"); 7705 + } 7727 7706 7728 7707 list_del(&smb_lock->llist); 7729 7708 spin_lock(&work->conn->llist_lock); ··· 7737 7708 spin_unlock(&work->conn->llist_lock); 7738 7709 7739 7710 locks_free_lock(smb_lock->fl); 7740 - locks_free_lock(rlock); 7711 + if (rlock) 7712 + locks_free_lock(rlock); 7741 7713 kfree(smb_lock); 7742 7714 } 7743 7715 out2: ··· 8221 8191 buffer = (char *)req + le32_to_cpu(req->InputOffset); 8222 8192 8223 8193 cnt_code = le32_to_cpu(req->CtlCode); 8224 - ret = smb2_calc_max_out_buf_len(work, 48, 8225 - le32_to_cpu(req->MaxOutputResponse)); 8194 + ret = smb2_calc_max_out_buf_len(work, 8195 + offsetof(struct smb2_ioctl_rsp, Buffer), 8196 + le32_to_cpu(req->MaxOutputResponse)); 8226 8197 if (ret < 0) { 8227 8198 rsp->hdr.Status = STATUS_INVALID_PARAMETER; 8228 8199 goto out;