From 6c4e675ad3594526d6604a7d30f1defdd08a42e4 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 7 Jun 2021 09:08:45 +0900 Subject: cifsd: fix list_add double add BUG_ON trap in setup_async_work() BUG_ON trap is coming when running xfstests generic/591 and smb2 leases = yes in smb.conf. [ 597.224978] list_add double add: new=ffff9110d292bb20, prev=ffff9110d292bb20, next=ffff9110d6c389e8. [ 597.225073] ------------[ cut here ]------------ [ 597.225077] kernel BUG at lib/list_debug.c:31! [ 597.225090] invalid opcode: 0000 [#1] SMP PTI [ 597.225095] CPU: 2 PID: 501 Comm: kworker/2:3 Tainted: G OE 5.13.0-rc1+ #2 [ 597.225099] Hardware name: SAMSUNG ELECTRONICS CO., LTD. Samsung DeskTop System/SAMSUNG_DT1234567890, BIOS P04KBM.022.121023.SK 10/23/2012 [ 597.225102] Workqueue: ksmbd-io handle_ksmbd_work [ksmbd] [ 597.225125] RIP: 0010:__list_add_valid+0x66/0x70 [ 597.225132] Code: 0b 48 89 c1 4c 89 c6 48 c7 c7 c8 08 c0 95 e8 fd 54 66 00 0f 0b 48 89 f2 4c 89 c1 48 89 fe 48 c7 c7 20 09 c0 95 e8 e6 54 66 00 <0f> 0b 0f 1f 84 00 00 00 00 00 55 48 8b 07 48 b9 00 01 00 00 00 00 [ 597.225136] RSP: 0018:ffffb9c9408dbac0 EFLAGS: 00010282 [ 597.225139] RAX: 0000000000000058 RBX: ffff9110d292ba40 RCX: 0000000000000000 [ 597.225142] RDX: 0000000000000000 RSI: ffff9111da328c30 RDI: ffff9111da328c30 [ 597.225144] RBP: ffffb9c9408dbac0 R08: 0000000000000001 R09: 0000000000000001 [ 597.225147] R10: 0000000003dd35ed R11: ffffb9c9408db888 R12: ffff9110d6c38998 [ 597.225149] R13: ffff9110d6c38800 R14: ffff9110d292bb20 R15: ffff9110d292bb20 [ 597.225152] FS: 0000000000000000(0000) GS:ffff9111da300000(0000) knlGS:0000000000000000 [ 597.225155] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 597.225157] CR2: 00007fd1629f84d0 CR3: 00000000c9a12006 CR4: 00000000001706e0 [ 597.225160] Call Trace: [ 597.225163] setup_async_work+0xa2/0x120 [ksmbd] [ 597.225191] oplock_break+0x396/0x5d0 [ksmbd] [ 597.225206] smb_grant_oplock+0x7a1/0x900 [ksmbd] [ 597.225218] ? smb_grant_oplock+0x7a1/0x900 [ksmbd] [ 597.225231] smb2_open+0xbbb/0x2960 [ksmbd] [ 597.225243] ? smb2_open+0xbbb/0x2960 [ksmbd] [ 597.225257] ? find_held_lock+0x35/0xa0 [ 597.225261] ? xa_load+0xaf/0x160 [ 597.225268] handle_ksmbd_work+0x2e0/0x420 [ksmbd] [ 597.225280] ? handle_ksmbd_work+0x2e0/0x420 [ksmbd] [ 597.225292] process_one_work+0x25a/0x5d0 [ 597.225298] worker_thread+0x3f/0x3a0 [ 597.225302] ? __kthread_parkme+0x6f/0xa0 [ 597.225306] ? process_one_work+0x5d0/0x5d0 [ 597.225309] kthread+0x142/0x160 [ 597.225313] ? kthread_park+0x90/0x90 [ 597.225316] ret_from_fork+0x22/0x30 same work struct can be add to list in smb_break_all_write_oplock() and smb_grant_oplock(). If client send invalid lease break ack to server, This issue can occur by calling both functions. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 3e112fbdc2d9..5b92e00881bb 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -690,9 +690,11 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg) work->cancel_fn = fn; work->cancel_argv = arg; - spin_lock(&conn->request_lock); - list_add_tail(&work->async_request_entry, &conn->async_requests); - spin_unlock(&conn->request_lock); + if (list_empty(&work->async_request_entry)) { + spin_lock(&conn->request_lock); + list_add_tail(&work->async_request_entry, &conn->async_requests); + spin_unlock(&conn->request_lock); + } return 0; } -- cgit From ade62d8b429fe49325593785316bdee3cabaec44 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 7 Jun 2021 09:22:22 +0900 Subject: cifsd: set epoch in smb2_lease_break response When running generic/591 after smb2 leases is enable, all smb2 lease ack requests failed in ksmbd. because cifs client seems to support only smb2 v2 lease. So cifs doesn't update lease state in ack request if epoch is not set in smb2 lease break request from ksmbd. epoch is used for smb2 v2 leases. So this patch add smb2 create v2 lease context and set increased epoch in smb2 lease break response. Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/oplock.c | 95 +++++++++++++++++++++++++++++++++++++++--------------- fs/cifsd/oplock.h | 15 ++++++--- fs/cifsd/smb2ops.c | 6 ++-- fs/cifsd/smb2pdu.h | 21 +++++++++++- 4 files changed, 102 insertions(+), 35 deletions(-) diff --git a/fs/cifsd/oplock.c b/fs/cifsd/oplock.c index f76de7861e7b..5868cdca7187 100644 --- a/fs/cifsd/oplock.c +++ b/fs/cifsd/oplock.c @@ -102,6 +102,9 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) lease->new_state = 0; lease->flags = lctx->flags; lease->duration = lctx->duration; + memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); + lease->version = lctx->version; + lease->epoch = 0; INIT_LIST_HEAD(&opinfo->lease_entry); opinfo->o_lease = lease; @@ -750,7 +753,7 @@ static void __smb2_lease_break_noti(struct work_struct *wk) rsp = work->response_buf; rsp->StructureSize = cpu_to_le16(44); - rsp->Reserved = 0; + rsp->Epoch = br_info->epoch; rsp->Flags = 0; if (br_info->curr_state & (SMB2_LEASE_WRITE_CACHING_LE | @@ -798,6 +801,10 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) br_info->curr_state = lease->state; br_info->new_state = lease->new_state; + if (lease->version == 2) + br_info->epoch = cpu_to_le16(++lease->epoch); + else + br_info->epoch = 0; memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE); work->request_buf = (char *)br_info; @@ -1084,11 +1091,8 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, __le32 prev_op_state = 0; /* not support directory lease */ - if (S_ISDIR(file_inode(fp->filp)->i_mode)) { - if (lctx) - lctx->dlease = 1; + if (S_ISDIR(file_inode(fp->filp)->i_mode)) return 0; - } opinfo = alloc_opinfo(work, pid, tid); if (!opinfo) @@ -1328,24 +1332,48 @@ __u8 smb2_map_lease_to_oplock(__le32 lease_state) */ void create_lease_buf(u8 *rbuf, struct lease *lease) { - struct create_lease *buf = (struct create_lease *)rbuf; char *LeaseKey = (char *)&lease->lease_key; - memset(buf, 0, sizeof(struct create_lease)); - buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey); - buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8)); - buf->lcontext.LeaseFlags = lease->flags; - buf->lcontext.LeaseState = lease->state; - buf->ccontext.DataOffset = cpu_to_le16(offsetof - (struct create_lease, lcontext)); - buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); - buf->ccontext.NameOffset = cpu_to_le16(offsetof + if (lease->version == 2) { + struct create_lease_v2 *buf = (struct create_lease_v2 *)rbuf; + char *ParentLeaseKey = (char *)&lease->parent_lease_key; + + memset(buf, 0, sizeof(struct create_lease_v2)); + buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey); + buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8)); + buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseState = lease->state; + buf->lcontext.ParentLeaseKeyLow = *((__le64 *)ParentLeaseKey); + buf->lcontext.ParentLeaseKeyHigh = *((__le64 *)(ParentLeaseKey + 8)); + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease_v2, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context_v2)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof + (struct create_lease_v2, Name)); + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + } else { + struct create_lease *buf = (struct create_lease *)rbuf; + + memset(buf, 0, sizeof(struct create_lease)); + buf->lcontext.LeaseKeyLow = *((__le64 *)LeaseKey); + buf->lcontext.LeaseKeyHigh = *((__le64 *)(LeaseKey + 8)); + buf->lcontext.LeaseFlags = lease->flags; + buf->lcontext.LeaseState = lease->state; + buf->ccontext.DataOffset = cpu_to_le16(offsetof + (struct create_lease, lcontext)); + buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context)); + buf->ccontext.NameOffset = cpu_to_le16(offsetof (struct create_lease, Name)); - buf->ccontext.NameLength = cpu_to_le16(4); - buf->Name[0] = 'R'; - buf->Name[1] = 'q'; - buf->Name[2] = 'L'; - buf->Name[3] = 's'; + buf->ccontext.NameLength = cpu_to_le16(4); + buf->Name[0] = 'R'; + buf->Name[1] = 'q'; + buf->Name[2] = 'L'; + buf->Name[3] = 's'; + } } /** @@ -1382,12 +1410,27 @@ struct lease_ctx_info *parse_lease_state(void *open_req) } while (next != 0); if (found) { - struct create_lease *lc = (struct create_lease *)cc; - *((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow; - *((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh; - lreq->req_state = lc->lcontext.LeaseState; - lreq->flags = lc->lcontext.LeaseFlags; - lreq->duration = lc->lcontext.LeaseDuration; + if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { + struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; + + *((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow; + *((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh; + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + *((__le64 *)lreq->parent_lease_key) = lc->lcontext.ParentLeaseKeyLow; + *((__le64 *)(lreq->parent_lease_key + 8)) = lc->lcontext.ParentLeaseKeyHigh; + lreq->version = 2; + } else { + struct create_lease *lc = (struct create_lease *)cc; + + *((__le64 *)lreq->lease_key) = lc->lcontext.LeaseKeyLow; + *((__le64 *)(lreq->lease_key + 8)) = lc->lcontext.LeaseKeyHigh; + lreq->req_state = lc->lcontext.LeaseState; + lreq->flags = lc->lcontext.LeaseFlags; + lreq->duration = lc->lcontext.LeaseDuration; + lreq->version = 1; + } return lreq; } diff --git a/fs/cifsd/oplock.h b/fs/cifsd/oplock.h index 0abd26123f6d..9fb7ea74e86c 100644 --- a/fs/cifsd/oplock.h +++ b/fs/cifsd/oplock.h @@ -37,11 +37,12 @@ #define SMB2_LEASE_KEY_SIZE 16 struct lease_ctx_info { - __u8 lease_key[SMB2_LEASE_KEY_SIZE]; - __le32 req_state; - __le32 flags; - __le64 duration; - int dlease; + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; + __le32 req_state; + __le32 flags; + __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; }; struct lease_table { @@ -57,6 +58,9 @@ struct lease { __le32 new_state; __le32 flags; __le64 duration; + __u8 parent_lease_key[SMB2_LEASE_KEY_SIZE]; + int version; + unsigned short epoch; struct lease_table *l_lb; }; @@ -86,6 +90,7 @@ struct oplock_info { struct lease_break_info { __le32 curr_state; __le32 new_state; + __le16 epoch; char lease_key[SMB2_LEASE_KEY_SIZE]; }; diff --git a/fs/cifsd/smb2ops.c b/fs/cifsd/smb2ops.c index c47d60bce9d4..8999c3faf4fc 100644 --- a/fs/cifsd/smb2ops.c +++ b/fs/cifsd/smb2ops.c @@ -57,7 +57,7 @@ static struct smb_version_values smb30_server_values = { .cap_unix = 0, .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease), + .create_lease_size = sizeof(struct create_lease_v2), .create_durable_size = sizeof(struct create_durable_rsp), .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), .create_mxac_size = sizeof(struct create_mxac_rsp), @@ -83,7 +83,7 @@ static struct smb_version_values smb302_server_values = { .cap_unix = 0, .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease), + .create_lease_size = sizeof(struct create_lease_v2), .create_durable_size = sizeof(struct create_durable_rsp), .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), .create_mxac_size = sizeof(struct create_mxac_rsp), @@ -109,7 +109,7 @@ static struct smb_version_values smb311_server_values = { .cap_unix = 0, .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, - .create_lease_size = sizeof(struct create_lease), + .create_lease_size = sizeof(struct create_lease_v2), .create_durable_size = sizeof(struct create_durable_rsp), .create_durable_v2_size = sizeof(struct create_durable_v2_rsp), .create_mxac_size = sizeof(struct create_mxac_rsp), diff --git a/fs/cifsd/smb2pdu.h b/fs/cifsd/smb2pdu.h index b3d3365d7070..0d5349e75dd9 100644 --- a/fs/cifsd/smb2pdu.h +++ b/fs/cifsd/smb2pdu.h @@ -735,12 +735,31 @@ struct lease_context { __le64 LeaseDuration; } __packed; +struct lease_context_v2 { + __le64 LeaseKeyLow; + __le64 LeaseKeyHigh; + __le32 LeaseState; + __le32 LeaseFlags; + __le64 LeaseDuration; + __le64 ParentLeaseKeyLow; + __le64 ParentLeaseKeyHigh; + __le16 Epoch; + __le16 Reserved; +} __packed; + struct create_lease { struct create_context ccontext; __u8 Name[8]; struct lease_context lcontext; } __packed; +struct create_lease_v2 { + struct create_context ccontext; + __u8 Name[8]; + struct lease_context_v2 lcontext; + __u8 Pad[4]; +} __packed; + /* Currently defined values for close flags */ #define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) struct smb2_close_req { @@ -1249,7 +1268,7 @@ struct smb2_oplock_break { struct smb2_lease_break { struct smb2_hdr hdr; __le16 StructureSize; /* Must be 44 */ - __le16 Reserved; + __le16 Epoch; __le32 Flags; __u8 LeaseKey[16]; __le32 CurrentLeaseState; -- cgit From d4b26c285802d3088342ff4ddf4d287d540c929e Mon Sep 17 00:00:00 2001 From: Hyunchul Lee Date: Sun, 6 Jun 2021 11:42:25 +0900 Subject: cifsd: fix possible compile error for asn1.c spnego_negtokeninit.asn1.h and spnego_negtokentarg.asn1.h have to be generated before asn1.o is compiled. Because of parallel build, the dependency could be broken, we need to specify the dependency in Makefile. Signed-off-by: Hyunchul Lee Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/Makefile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/cifsd/Makefile b/fs/cifsd/Makefile index ccacb798a932..e422e9853579 100644 --- a/fs/cifsd/Makefile +++ b/fs/cifsd/Makefile @@ -4,9 +4,6 @@ # obj-$(CONFIG_SMB_SERVER) += ksmbd.o -$(obj)/spnego_negtokeninit.asn1.o: $(obj)/spnego_negtokeninit.asn1.c $(obj)/spnego_negtokeninit.asn1.h -$(obj)/spnego_negtokentarg.asn1.o: $(obj)/spnego_negtokentarg.asn1.c $(obj)/spnego_negtokentarg.asn1.h - ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o buffer_pool.o \ misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \ mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \ @@ -14,4 +11,10 @@ ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o buffer_pool.o \ transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \ smb2ops.o smb2misc.o spnego_negtokeninit.asn1.o \ spnego_negtokentarg.asn1.o asn1.o ndr.o + +$(obj)/asn1.o: $(obj)/spnego_negtokeninit.asn1.h $(obj)/spnego_negtokentarg.asn1.h + +$(obj)/spnego_negtokeninit.asn1.o: $(obj)/spnego_negtokeninit.asn1.c $(obj)/spnego_negtokeninit.asn1.h +$(obj)/spnego_negtokentarg.asn1.o: $(obj)/spnego_negtokentarg.asn1.c $(obj)/spnego_negtokentarg.asn1.h + ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o -- cgit From 3aefd54da5ec6e7ec1f1e682007f5819c99d8588 Mon Sep 17 00:00:00 2001 From: Wan Jiabing Date: Mon, 7 Jun 2021 12:54:32 +0800 Subject: cifsd: remove duplicated argument Fix the following coccicheck warning: ./fs/cifsd/smb2pdu.c:1713:27-41: duplicated argument to & or | FILE_DELETE_LE is duplicated. Remove one and reorder argument to make coding style reasonable. Signed-off-by: Wan Jiabing Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/cifsd/smb2pdu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/cifsd/smb2pdu.c b/fs/cifsd/smb2pdu.c index 5b92e00881bb..ac15a9287310 100644 --- a/fs/cifsd/smb2pdu.c +++ b/fs/cifsd/smb2pdu.c @@ -1712,10 +1712,10 @@ int smb2_tree_connect(struct ksmbd_work *work) KSMBD_TREE_CONN_FLAG_WRITABLE)) { rsp->MaximalAccess |= FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE | FILE_WRITE_EA_LE | - FILE_DELETE_CHILD_LE | FILE_DELETE_LE | - FILE_WRITE_ATTRIBUTES_LE | FILE_DELETE_LE | - FILE_READ_CONTROL_LE | FILE_WRITE_DAC_LE | - FILE_WRITE_OWNER_LE | FILE_SYNCHRONIZE_LE; + FILE_DELETE_LE | FILE_WRITE_ATTRIBUTES_LE | + FILE_DELETE_CHILD_LE | FILE_READ_CONTROL_LE | + FILE_WRITE_DAC_LE | FILE_WRITE_OWNER_LE | + FILE_SYNCHRONIZE_LE; } } -- cgit