From 7a2158b73c36903e8822dae5442c27d6d0e1014b Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 20 Nov 2024 20:18:31 +0300 Subject: [PATCH 01/16] smb/client: Prevent error pointer dereference The cifs_sb_tlink() function can return error pointers, but this code dereferences it before checking for error pointers. Re-order the code to fix that. Fixes: 0f9b6b045bb2 ("fs/smb/client: implement chmod() for SMB3 POSIX Extensions") Signed-off-by: Dan Carpenter Reviewed-by: Bharath SM Signed-off-by: Steve French --- fs/smb/client/cifsacl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index c68ad526a4de..ba79aa2107cc 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -1592,14 +1592,16 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, struct smb_ntsd *pntsd = NULL; /* acl obtained from server */ struct smb_ntsd *pnntsd = NULL; /* modified acl to be sent to server */ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); + struct tcon_link *tlink; struct smb_version_operations *ops; bool mode_from_sid, id_from_sid; - bool posix = tlink_tcon(tlink)->posix_extensions; const u32 info = 0; + bool posix; + tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink); + posix = tlink_tcon(tlink)->posix_extensions; ops = tlink_tcon(tlink)->ses->server->ops; -- 2.50.1 From db363b0a1d9e6b9dc556296f1b1007aeb496a8cf Mon Sep 17 00:00:00 2001 From: Ritvik Budhiraja Date: Mon, 11 Nov 2024 11:43:51 +0000 Subject: [PATCH 02/16] CIFS: New mount option for cifs.upcall namespace resolution In the current implementation, the SMB filesystem on a mount point can trigger upcalls from the kernel to the userspace to enable certain functionalities like spnego, dns_resolution, amongst others. These upcalls usually either happen in the context of the mount or in the context of an application/user. The upcall handler for cifs, cifs.upcall already has existing code which switches the namespaces to the caller's namespace before handling the upcall. This behaviour is expected for scenarios like multiuser mounts, but might not cover all single user scenario with services such as Kubernetes, where the mount can happen from different locations such as on the host, from an app container, or a driver pod which does the mount on behalf of a different pod. This patch introduces a new mount option called upcall_target, to customise the upcall behaviour. upcall_target can take 'mount' and 'app' as possible values. This aids use cases like Kubernetes where the mount happens on behalf of the application in another container altogether. Having this new mount option allows the mount command to specify where the upcall should happen: 'mount' for resolving the upcall to the host namespace, and 'app' for resolving the upcall to the ns of the calling thread. This will enable both the scenarios where the Kerberos credentials can be found on the application namespace or the host namespace to which just the mount operation is "delegated". Reviewed-by: Shyam Prasad Reviewed-by: Bharath S M Reviewed-by: Ronnie Sahlberg Signed-off-by: Ritvik Budhiraja Signed-off-by: Steve French --- fs/smb/client/cifs_spnego.c | 16 +++++++++++++++ fs/smb/client/cifsfs.c | 25 ++++++++++++++++++++++++ fs/smb/client/cifsglob.h | 7 +++++++ fs/smb/client/connect.c | 20 +++++++++++++++++++ fs/smb/client/fs_context.c | 39 +++++++++++++++++++++++++++++++++++++ fs/smb/client/fs_context.h | 10 ++++++++++ 6 files changed, 117 insertions(+) diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c index af7849e5974f..28f568b5fc27 100644 --- a/fs/smb/client/cifs_spnego.c +++ b/fs/smb/client/cifs_spnego.c @@ -82,6 +82,9 @@ struct key_type cifs_spnego_key_type = { /* strlen of ";pid=0x" */ #define PID_KEY_LEN 7 +/* strlen of ";upcall_target=" */ +#define UPCALL_TARGET_KEY_LEN 15 + /* get a key struct with a SPNEGO security blob, suitable for session setup */ struct key * cifs_get_spnego_key(struct cifs_ses *sesInfo, @@ -108,6 +111,11 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo, if (sesInfo->user_name) desc_len += USER_KEY_LEN + strlen(sesInfo->user_name); + if (sesInfo->upcall_target == UPTARGET_MOUNT) + desc_len += UPCALL_TARGET_KEY_LEN + 5; // strlen("mount") + else + desc_len += UPCALL_TARGET_KEY_LEN + 3; // strlen("app") + spnego_key = ERR_PTR(-ENOMEM); description = kzalloc(desc_len, GFP_KERNEL); if (description == NULL) @@ -156,6 +164,14 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo, dp = description + strlen(description); sprintf(dp, ";pid=0x%x", current->pid); + if (sesInfo->upcall_target == UPTARGET_MOUNT) { + dp = description + strlen(description); + sprintf(dp, ";upcall_target=mount"); + } else { + dp = description + strlen(description); + sprintf(dp, ";upcall_target=app"); + } + cifs_dbg(FYI, "key description = %s\n", description); saved_cred = override_creds(spnego_cred); spnego_key = request_key(&cifs_spnego_key_type, description, ""); diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 20cafdff5081..979853471027 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -546,6 +546,30 @@ static int cifs_show_devname(struct seq_file *m, struct dentry *root) return 0; } +static void +cifs_show_upcall_target(struct seq_file *s, struct cifs_sb_info *cifs_sb) +{ + if (cifs_sb->ctx->upcall_target == UPTARGET_UNSPECIFIED) { + seq_puts(s, ",upcall_target=app"); + return; + } + + seq_puts(s, ",upcall_target="); + + switch (cifs_sb->ctx->upcall_target) { + case UPTARGET_APP: + seq_puts(s, "app"); + break; + case UPTARGET_MOUNT: + seq_puts(s, "mount"); + break; + default: + /* shouldn't ever happen */ + seq_puts(s, "unknown"); + break; + } +} + /* * cifs_show_options() is for displaying mount options in /proc/mounts. * Not all settable options are displayed but most of the important @@ -562,6 +586,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root) seq_show_option(s, "vers", tcon->ses->server->vals->version_string); cifs_show_security(s, tcon->ses); cifs_show_cache_flavor(s, cifs_sb); + cifs_show_upcall_target(s, cifs_sb); if (tcon->no_lease) seq_puts(s, ",nolease"); diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 5041b1ffc244..63d194ebbd7d 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -153,6 +153,12 @@ enum securityEnum { Kerberos, /* Kerberos via SPNEGO */ }; +enum upcall_target_enum { + UPTARGET_UNSPECIFIED, /* not specified, defaults to app */ + UPTARGET_MOUNT, /* upcall to the mount namespace */ + UPTARGET_APP, /* upcall to the application namespace which did the mount */ +}; + enum cifs_reparse_type { CIFS_REPARSE_TYPE_NFS, CIFS_REPARSE_TYPE_WSL, @@ -1084,6 +1090,7 @@ struct cifs_ses { struct session_key auth_key; struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ enum securityEnum sectype; /* what security flavor was specified? */ + enum upcall_target_enum upcall_target; /* what upcall target was specified? */ bool sign; /* is signing required? */ bool domainAuto:1; bool expired_pwd; /* track if access denied or expired pwd so can know if need to update */ diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 0ce2d704b1f3..0a97228c06b1 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -2339,6 +2339,26 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) ses->sectype = ctx->sectype; ses->sign = ctx->sign; + + /* + *Explicitly marking upcall_target mount option for easier handling + * by cifs_spnego.c and eventually cifs.upcall.c + */ + + switch (ctx->upcall_target) { + case UPTARGET_UNSPECIFIED: /* default to app */ + case UPTARGET_APP: + ses->upcall_target = UPTARGET_APP; + break; + case UPTARGET_MOUNT: + ses->upcall_target = UPTARGET_MOUNT; + break; + default: + // should never happen + ses->upcall_target = UPTARGET_APP; + break; + } + ses->local_nls = load_nls(ctx->local_nls->charset); /* add server as first channel */ diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c index 5c5a52019efa..c87879e4739b 100644 --- a/fs/smb/client/fs_context.c +++ b/fs/smb/client/fs_context.c @@ -67,6 +67,12 @@ static const match_table_t cifs_secflavor_tokens = { { Opt_sec_err, NULL } }; +static const match_table_t cifs_upcall_target = { + { Opt_upcall_target_mount, "mount" }, + { Opt_upcall_target_application, "app" }, + { Opt_upcall_target_err, NULL } +}; + const struct fs_parameter_spec smb3_fs_parameters[] = { /* Mount options that take no arguments */ fsparam_flag_no("user_xattr", Opt_user_xattr), @@ -178,6 +184,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = { fsparam_string("sec", Opt_sec), fsparam_string("cache", Opt_cache), fsparam_string("reparse", Opt_reparse), + fsparam_string("upcall_target", Opt_upcalltarget), /* Arguments that should be ignored */ fsparam_flag("guest", Opt_ignore), @@ -248,6 +255,29 @@ cifs_parse_security_flavors(struct fs_context *fc, char *value, struct smb3_fs_c return 0; } +static int +cifs_parse_upcall_target(struct fs_context *fc, char *value, struct smb3_fs_context *ctx) +{ + substring_t args[MAX_OPT_ARGS]; + + ctx->upcall_target = UPTARGET_UNSPECIFIED; + + switch (match_token(value, cifs_upcall_target, args)) { + case Opt_upcall_target_mount: + ctx->upcall_target = UPTARGET_MOUNT; + break; + case Opt_upcall_target_application: + ctx->upcall_target = UPTARGET_APP; + break; + + default: + cifs_errorf(fc, "bad upcall target: %s\n", value); + return 1; + } + + return 0; +} + static const match_table_t cifs_cacheflavor_tokens = { { Opt_cache_loose, "loose" }, { Opt_cache_strict, "strict" }, @@ -1450,6 +1480,10 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, if (cifs_parse_security_flavors(fc, param->string, ctx) != 0) goto cifs_parse_mount_err; break; + case Opt_upcalltarget: + if (cifs_parse_upcall_target(fc, param->string, ctx) != 0) + goto cifs_parse_mount_err; + break; case Opt_cache: if (cifs_parse_cache_flavor(fc, param->string, ctx) != 0) goto cifs_parse_mount_err; @@ -1627,6 +1661,11 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, } /* case Opt_ignore: - is ignored as expected ... */ + if (ctx->multiuser && ctx->upcall_target == UPTARGET_MOUNT) { + cifs_errorf(fc, "multiuser mount option not supported with upcalltarget set as 'mount'\n"); + goto cifs_parse_mount_err; + } + return 0; cifs_parse_mount_err: diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h index 890d6d9d4a59..67b7fc48ac58 100644 --- a/fs/smb/client/fs_context.h +++ b/fs/smb/client/fs_context.h @@ -61,6 +61,12 @@ enum cifs_sec_param { Opt_sec_err }; +enum cifs_upcall_target_param { + Opt_upcall_target_mount, + Opt_upcall_target_application, + Opt_upcall_target_err +}; + enum cifs_param { /* Mount options that take no arguments */ Opt_user_xattr, @@ -114,6 +120,8 @@ enum cifs_param { Opt_multichannel, Opt_compress, Opt_witness, + Opt_is_upcall_target_mount, + Opt_is_upcall_target_application, /* Mount options which take numeric value */ Opt_backupuid, @@ -157,6 +165,7 @@ enum cifs_param { Opt_sec, Opt_cache, Opt_reparse, + Opt_upcalltarget, /* Mount options to be ignored */ Opt_ignore, @@ -198,6 +207,7 @@ struct smb3_fs_context { umode_t file_mode; umode_t dir_mode; enum securityEnum sectype; /* sectype requested via mnt opts */ + enum upcall_target_enum upcall_target; /* where to upcall for mount */ bool sign; /* was signing requested via mnt opts? */ bool ignore_signature:1; bool retry:1; -- 2.50.1 From 0d6b0d2e38167f4a3aa177191e3a10a9c3681a0c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Pali=20Roh=C3=A1r?= Date: Thu, 3 Oct 2024 21:39:03 +0200 Subject: [PATCH 03/16] cifs: Recognize SFU char/block devices created by Windows NFS server on Windows Server <<2012 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Windows NFS server versions on Windows Server older than 2012 release use for storing char and block devices modified SFU format, not compatible with the original SFU. Windows NFS server on Windows Server 2012 and new versions use different format (reparse points), not related to SFU-style. SFU / SUA / Interix subsystem stores the major and major numbers as pair of 64-bit integer, but Windows NFS server stores as pair of 32-bit integers. Which makes char and block devices between Windows NFS server <<2012 and Windows SFU/SUA/Interix subsytem incompatible. So improve Linux SMB client. When SFU mode is enabled (mount option -o sfu is specified) then recognize also these kind of char and block devices and its major and minor numbers, which are used by Windows Server versions older than 2012. Signed-off-by: Pali Rohár Signed-off-by: Steve French --- fs/smb/client/inode.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 72ebd72dd02b..de8063b44072 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -598,6 +598,17 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path, mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); fattr->cf_rdev = MKDEV(mjr, mnr); + } else if (bytes_read == 16) { + /* + * Windows NFS server before Windows Server 2012 + * stores major and minor number in SFU-modified + * style, just as 32-bit numbers. Recognize it. + */ + __u32 mjr; /* major */ + __u32 mnr; /* minor */ + mjr = le32_to_cpu(*(__le32 *)(pbuf+8)); + mnr = le32_to_cpu(*(__le32 *)(pbuf+12)); + fattr->cf_rdev = MKDEV(mjr, mnr); } } else if (memcmp("IntxCHR\0", pbuf, 8) == 0) { cifs_dbg(FYI, "Char device\n"); @@ -610,6 +621,17 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path, mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); fattr->cf_rdev = MKDEV(mjr, mnr); + } else if (bytes_read == 16) { + /* + * Windows NFS server before Windows Server 2012 + * stores major and minor number in SFU-modified + * style, just as 32-bit numbers. Recognize it. + */ + __u32 mjr; /* major */ + __u32 mnr; /* minor */ + mjr = le32_to_cpu(*(__le32 *)(pbuf+8)); + mnr = le32_to_cpu(*(__le32 *)(pbuf+12)); + fattr->cf_rdev = MKDEV(mjr, mnr); } } else if (memcmp("LnxSOCK", pbuf, 8) == 0) { cifs_dbg(FYI, "Socket\n"); -- 2.50.1 From 9ed9d83a51a9636d367c796252409e7b2f4de4d4 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 18 Nov 2024 12:19:46 -0600 Subject: [PATCH 04/16] smb3: request handle caching when caching directories This client was only requesting READ caching, not READ and HANDLE caching in the LeaseState on the open requests we send for directories. To delay closing a handle (e.g. for caching directory contents) we should be requesting HANDLE as well as READ (as we already do for deferred close of files). See MS-SMB2 3.3.1.4 e.g. Cc: stable@vger.kernel.org Signed-off-by: Steve French --- fs/smb/client/smb2ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 24a2aa04a108..7571fefeb83a 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -4080,7 +4080,7 @@ map_oplock_to_lease(u8 oplock) if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) return SMB2_LEASE_WRITE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE; else if (oplock == SMB2_OPLOCK_LEVEL_II) - return SMB2_LEASE_READ_CACHING_LE; + return SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE; else if (oplock == SMB2_OPLOCK_LEVEL_BATCH) return SMB2_LEASE_HANDLE_CACHING_LE | SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_WRITE_CACHING_LE; -- 2.50.1 From bc925c1216f0848da96ac642fba3cb92ae1f4e06 Mon Sep 17 00:00:00 2001 From: Paulo Alcantara Date: Mon, 18 Nov 2024 12:35:14 -0300 Subject: [PATCH 05/16] smb: client: improve compound padding in encryption After commit f7f291e14dde ("cifs: fix oops during encryption"), the encryption layer can handle vmalloc'd buffers as well as kmalloc'd buffers, so there is no need to inefficiently squash request iovs into a single one to handle padding in compound requests. Cc: David Howells Signed-off-by: Paulo Alcantara (Red Hat) Signed-off-by: Steve French --- fs/smb/client/cifsglob.h | 4 ++-- fs/smb/client/smb2ops.c | 37 +++--------------------------------- fs/smb/client/transport.c | 40 +++++++++++++-------------------------- 3 files changed, 18 insertions(+), 63 deletions(-) diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index 63d194ebbd7d..fc33dfe7e925 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -2230,7 +2230,7 @@ static inline int cifs_get_num_sgs(const struct smb_rqst *rqst, struct kvec *iov = &rqst[i].rq_iov[j]; addr = (unsigned long)iov->iov_base + skip; - if (unlikely(is_vmalloc_addr((void *)addr))) { + if (is_vmalloc_or_module_addr((void *)addr)) { len = iov->iov_len - skip; nents += DIV_ROUND_UP(offset_in_page(addr) + len, PAGE_SIZE); @@ -2257,7 +2257,7 @@ static inline void cifs_sg_set_buf(struct sg_table *sgtable, unsigned int off = offset_in_page(addr); addr &= PAGE_MASK; - if (unlikely(is_vmalloc_addr((void *)addr))) { + if (is_vmalloc_or_module_addr((void *)addr)) { do { unsigned int len = min_t(unsigned int, buflen, PAGE_SIZE - off); diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index 7571fefeb83a..fa96ebed8310 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -2606,7 +2606,7 @@ smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst) struct cifs_ses *ses = tcon->ses; struct TCP_Server_Info *server = ses->server; unsigned long len = smb_rqst_len(server, rqst); - int i, num_padding; + int num_padding; shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); if (shdr == NULL) { @@ -2615,44 +2615,13 @@ smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst) } /* SMB headers in a compound are 8 byte aligned. */ - - /* No padding needed */ - if (!(len & 7)) - goto finished; - - num_padding = 8 - (len & 7); - if (!smb3_encryption_required(tcon)) { - /* - * If we do not have encryption then we can just add an extra - * iov for the padding. - */ + if (!IS_ALIGNED(len, 8)) { + num_padding = 8 - (len & 7); rqst->rq_iov[rqst->rq_nvec].iov_base = smb2_padding; rqst->rq_iov[rqst->rq_nvec].iov_len = num_padding; rqst->rq_nvec++; len += num_padding; - } else { - /* - * We can not add a small padding iov for the encryption case - * because the encryption framework can not handle the padding - * iovs. - * We have to flatten this into a single buffer and add - * the padding to it. - */ - for (i = 1; i < rqst->rq_nvec; i++) { - memcpy(rqst->rq_iov[0].iov_base + - rqst->rq_iov[0].iov_len, - rqst->rq_iov[i].iov_base, - rqst->rq_iov[i].iov_len); - rqst->rq_iov[0].iov_len += rqst->rq_iov[i].iov_len; - } - memset(rqst->rq_iov[0].iov_base + rqst->rq_iov[0].iov_len, - 0, num_padding); - rqst->rq_iov[0].iov_len += num_padding; - len += num_padding; - rqst->rq_nvec = 1; } - - finished: shdr->NextCommand = cpu_to_le32(len); } diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c index 91812150186c..0dc80959ce48 100644 --- a/fs/smb/client/transport.c +++ b/fs/smb/client/transport.c @@ -418,19 +418,16 @@ out: return rc; } -struct send_req_vars { - struct smb2_transform_hdr tr_hdr; - struct smb_rqst rqst[MAX_COMPOUND]; - struct kvec iov; -}; - static int smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, struct smb_rqst *rqst, int flags) { - struct send_req_vars *vars; - struct smb_rqst *cur_rqst; - struct kvec *iov; + struct smb2_transform_hdr tr_hdr; + struct smb_rqst new_rqst[MAX_COMPOUND] = {}; + struct kvec iov = { + .iov_base = &tr_hdr, + .iov_len = sizeof(tr_hdr), + }; int rc; if (flags & CIFS_COMPRESS_REQ) @@ -447,26 +444,15 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, return -EIO; } - vars = kzalloc(sizeof(*vars), GFP_NOFS); - if (!vars) - return -ENOMEM; - cur_rqst = vars->rqst; - iov = &vars->iov; - - iov->iov_base = &vars->tr_hdr; - iov->iov_len = sizeof(vars->tr_hdr); - cur_rqst[0].rq_iov = iov; - cur_rqst[0].rq_nvec = 1; + new_rqst[0].rq_iov = &iov; + new_rqst[0].rq_nvec = 1; rc = server->ops->init_transform_rq(server, num_rqst + 1, - &cur_rqst[0], rqst); - if (rc) - goto out; - - rc = __smb_send_rqst(server, num_rqst + 1, &cur_rqst[0]); - smb3_free_compound_rqst(num_rqst, &cur_rqst[1]); -out: - kfree(vars); + new_rqst, rqst); + if (!rc) { + rc = __smb_send_rqst(server, num_rqst + 1, new_rqst); + smb3_free_compound_rqst(num_rqst, &new_rqst[1]); + } return rc; } -- 2.50.1 From 9f544d26b15bfc52cf3a6e6a655f759e76c1a01a Mon Sep 17 00:00:00 2001 From: Paulo Alcantara Date: Mon, 18 Nov 2024 12:35:15 -0300 Subject: [PATCH 06/16] smb: client: get rid of bounds check in SMB2_ioctl_init() smb2_set_next_command() no longer squashes request iovs into a single iov, so the bounds check can be dropped. Cc: David Howells Signed-off-by: Paulo Alcantara (Red Hat) Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index ab3a2ca66be3..055236835537 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3313,15 +3313,6 @@ SMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server, return rc; if (indatalen) { - unsigned int len; - - if (WARN_ON_ONCE(smb3_encryption_required(tcon) && - (check_add_overflow(total_len - 1, - ALIGN(indatalen, 8), &len) || - len > MAX_CIFS_SMALL_BUFFER_SIZE))) { - cifs_small_buf_release(req); - return -EIO; - } /* * indatalen is usually small at a couple of bytes max, so * just allocate through generic pool -- 2.50.1 From 0812340811e45ec4039d409049be53056182a552 Mon Sep 17 00:00:00 2001 From: Paulo Alcantara Date: Mon, 18 Nov 2024 12:35:16 -0300 Subject: [PATCH 07/16] smb: client: handle max length for SMB symlinks We can't use PATH_MAX for SMB symlinks because (1) Windows Server will fail FSCTL_SET_REPARSE_POINT with STATUS_IO_REPARSE_DATA_INVALID when input buffer is larger than 16K, as specified in MS-FSA 2.1.5.10.37. (2) The client won't be able to parse large SMB responses that includes SMB symlink path within SMB2_CREATE or SMB2_IOCTL responses. Fix this by defining a maximum length value (4060) for SMB symlinks that both client and server can handle. Cc: David Howells Cc: stable@vger.kernel.org Signed-off-by: Paulo Alcantara (Red Hat) Signed-off-by: Steve French --- fs/smb/client/reparse.c | 5 ++++- fs/smb/client/reparse.h | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/smb/client/reparse.c b/fs/smb/client/reparse.c index 74abbdf5026c..90da1e2b6217 100644 --- a/fs/smb/client/reparse.c +++ b/fs/smb/client/reparse.c @@ -35,6 +35,9 @@ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, u16 len, plen; int rc = 0; + if (strlen(symname) > REPARSE_SYM_PATH_MAX) + return -ENAMETOOLONG; + sym = kstrdup(symname, GFP_KERNEL); if (!sym) return -ENOMEM; @@ -64,7 +67,7 @@ int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, if (rc < 0) goto out; - plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX); + plen = 2 * UniStrnlen((wchar_t *)path, REPARSE_SYM_PATH_MAX); len = sizeof(*buf) + plen * 2; buf = kzalloc(len, GFP_KERNEL); if (!buf) { diff --git a/fs/smb/client/reparse.h b/fs/smb/client/reparse.h index 158e7b7aae64..2a9f4f9f79de 100644 --- a/fs/smb/client/reparse.h +++ b/fs/smb/client/reparse.h @@ -12,6 +12,8 @@ #include "fs_context.h" #include "cifsglob.h" +#define REPARSE_SYM_PATH_MAX 4060 + /* * Used only by cifs.ko to ignore reparse points from files when client or * server doesn't support FSCTL_GET_REPARSE_POINT. -- 2.50.1 From 7afb86733685c64c604d32faf00fa4a1f22c2ab1 Mon Sep 17 00:00:00 2001 From: Paul Aurich Date: Mon, 18 Nov 2024 13:50:26 -0800 Subject: [PATCH 08/16] smb: Don't leak cfid when reconnect races with open_cached_dir open_cached_dir() may either race with the tcon reconnection even before compound_send_recv() or directly trigger a reconnection via SMB2_open_init() or SMB_query_info_init(). The reconnection process invokes invalidate_all_cached_dirs() via cifs_mark_open_files_invalid(), which removes all cfids from the cfids->entries list but doesn't drop a ref if has_lease isn't true. This results in the currently-being-constructed cfid not being on the list, but still having a refcount of 2. It leaks if returned from open_cached_dir(). Fix this by setting cfid->has_lease when the ref is actually taken; the cfid will not be used by other threads until it has a valid time. Addresses these kmemleaks: unreferenced object 0xffff8881090c4000 (size 1024): comm "bash", pid 1860, jiffies 4295126592 hex dump (first 32 bytes): 00 01 00 00 00 00 ad de 22 01 00 00 00 00 ad de ........"....... 00 ca 45 22 81 88 ff ff f8 dc 4f 04 81 88 ff ff ..E"......O..... backtrace (crc 6f58c20f): [] __kmalloc_cache_noprof+0x2be/0x350 [] open_cached_dir+0x993/0x1fb0 [] cifs_readdir+0x15a0/0x1d50 [] iterate_dir+0x28f/0x4b0 [] __x64_sys_getdents64+0xfd/0x200 [] do_syscall_64+0x95/0x1a0 [] entry_SYSCALL_64_after_hwframe+0x76/0x7e unreferenced object 0xffff8881044fdcf8 (size 8): comm "bash", pid 1860, jiffies 4295126592 hex dump (first 8 bytes): 00 cc cc cc cc cc cc cc ........ backtrace (crc 10c106a9): [] __kmalloc_node_track_caller_noprof+0x363/0x480 [] kstrdup+0x36/0x60 [] open_cached_dir+0x9b0/0x1fb0 [] cifs_readdir+0x15a0/0x1d50 [] iterate_dir+0x28f/0x4b0 [] __x64_sys_getdents64+0xfd/0x200 [] do_syscall_64+0x95/0x1a0 [] entry_SYSCALL_64_after_hwframe+0x76/0x7e And addresses these BUG splats when unmounting the SMB filesystem: BUG: Dentry ffff888140590ba0{i=1000000000080,n=/} still in use (2) [unmount of cifs cifs] WARNING: CPU: 3 PID: 3433 at fs/dcache.c:1536 umount_check+0xd0/0x100 Modules linked in: CPU: 3 UID: 0 PID: 3433 Comm: bash Not tainted 6.12.0-rc4-g850925a8133c-dirty #49 Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 11/12/2020 RIP: 0010:umount_check+0xd0/0x100 Code: 8d 7c 24 40 e8 31 5a f4 ff 49 8b 54 24 40 41 56 49 89 e9 45 89 e8 48 89 d9 41 57 48 89 de 48 c7 c7 80 e7 db ac e8 f0 72 9a ff <0f> 0b 58 31 c0 5a 5b 5d 41 5c 41 5d 41 5e 41 5f e9 2b e5 5d 01 41 RSP: 0018:ffff88811cc27978 EFLAGS: 00010286 RAX: 0000000000000000 RBX: ffff888140590ba0 RCX: ffffffffaaf20bae RDX: dffffc0000000000 RSI: 0000000000000008 RDI: ffff8881f6fb6f40 RBP: ffff8881462ec000 R08: 0000000000000001 R09: ffffed1023984ee3 R10: ffff88811cc2771f R11: 00000000016cfcc0 R12: ffff888134383e08 R13: 0000000000000002 R14: ffff8881462ec668 R15: ffffffffaceab4c0 FS: 00007f23bfa98740(0000) GS:ffff8881f6f80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000556de4a6f808 CR3: 0000000123c80000 CR4: 0000000000350ef0 Call Trace: d_walk+0x6a/0x530 shrink_dcache_for_umount+0x6a/0x200 generic_shutdown_super+0x52/0x2a0 kill_anon_super+0x22/0x40 cifs_kill_sb+0x159/0x1e0 deactivate_locked_super+0x66/0xe0 cleanup_mnt+0x140/0x210 task_work_run+0xfb/0x170 syscall_exit_to_user_mode+0x29f/0x2b0 do_syscall_64+0xa1/0x1a0 entry_SYSCALL_64_after_hwframe+0x76/0x7e RIP: 0033:0x7f23bfb93ae7 Code: ff ff ff ff c3 66 0f 1f 44 00 00 48 8b 0d 11 93 0d 00 f7 d8 64 89 01 b8 ff ff ff ff eb bf 0f 1f 44 00 00 b8 50 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d e9 92 0d 00 f7 d8 64 89 01 48 RSP: 002b:00007ffee9138598 EFLAGS: 00000246 ORIG_RAX: 0000000000000050 RAX: 0000000000000000 RBX: 0000558f1803e9a0 RCX: 00007f23bfb93ae7 RDX: 0000000000000000 RSI: 0000000000000004 RDI: 0000558f1803e9a0 RBP: 0000558f1803e600 R08: 0000000000000007 R09: 0000558f17fab610 R10: d91d5ec34ab757b0 R11: 0000000000000246 R12: 0000000000000001 R13: 0000000000000000 R14: 0000000000000015 R15: 0000000000000000 irq event stamp: 1163486 hardirqs last enabled at (1163485): [] _raw_spin_unlock_irqrestore+0x34/0x60 hardirqs last disabled at (1163486): [] __schedule+0xc7c/0x19a0 softirqs last enabled at (1163482): [] __smb_send_rqst+0x3de/0x990 softirqs last disabled at (1163480): [] release_sock+0x21/0xf0 ---[ end trace 0000000000000000 ]--- VFS: Busy inodes after unmount of cifs (cifs) ------------[ cut here ]------------ kernel BUG at fs/super.c:661! Oops: invalid opcode: 0000 [#1] PREEMPT SMP KASAN NOPTI CPU: 1 UID: 0 PID: 3433 Comm: bash Tainted: G W 6.12.0-rc4-g850925a8133c-dirty #49 Tainted: [W]=WARN Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 11/12/2020 RIP: 0010:generic_shutdown_super+0x290/0x2a0 Code: e8 15 7c f7 ff 48 8b 5d 28 48 89 df e8 09 7c f7 ff 48 8b 0b 48 89 ee 48 8d 95 68 06 00 00 48 c7 c7 80 7f db ac e8 00 69 af ff <0f> 0b 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 00 90 90 90 90 90 90 RSP: 0018:ffff88811cc27a50 EFLAGS: 00010246 RAX: 000000000000003e RBX: ffffffffae994420 RCX: 0000000000000027 RDX: 0000000000000000 RSI: ffffffffab06180e RDI: ffff8881f6eb18c8 RBP: ffff8881462ec000 R08: 0000000000000001 R09: ffffed103edd6319 R10: ffff8881f6eb18cb R11: 00000000016d3158 R12: ffff8881462ec9c0 R13: ffff8881462ec050 R14: 0000000000000001 R15: 0000000000000000 FS: 00007f23bfa98740(0000) GS:ffff8881f6e80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f8364005d68 CR3: 0000000123c80000 CR4: 0000000000350ef0 Call Trace: kill_anon_super+0x22/0x40 cifs_kill_sb+0x159/0x1e0 deactivate_locked_super+0x66/0xe0 cleanup_mnt+0x140/0x210 task_work_run+0xfb/0x170 syscall_exit_to_user_mode+0x29f/0x2b0 do_syscall_64+0xa1/0x1a0 entry_SYSCALL_64_after_hwframe+0x76/0x7e RIP: 0033:0x7f23bfb93ae7 Modules linked in: ---[ end trace 0000000000000000 ]--- RIP: 0010:generic_shutdown_super+0x290/0x2a0 Code: e8 15 7c f7 ff 48 8b 5d 28 48 89 df e8 09 7c f7 ff 48 8b 0b 48 89 ee 48 8d 95 68 06 00 00 48 c7 c7 80 7f db ac e8 00 69 af ff <0f> 0b 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 00 90 90 90 90 90 90 RSP: 0018:ffff88811cc27a50 EFLAGS: 00010246 RAX: 000000000000003e RBX: ffffffffae994420 RCX: 0000000000000027 RDX: 0000000000000000 RSI: ffffffffab06180e RDI: ffff8881f6eb18c8 RBP: ffff8881462ec000 R08: 0000000000000001 R09: ffffed103edd6319 R10: ffff8881f6eb18cb R11: 00000000016d3158 R12: ffff8881462ec9c0 R13: ffff8881462ec050 R14: 0000000000000001 R15: 0000000000000000 FS: 00007f23bfa98740(0000) GS:ffff8881f6e80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f8364005d68 CR3: 0000000123c80000 CR4: 0000000000350ef0 This reproduces eventually with an SMB mount and two shells running these loops concurrently - while true; do cd ~; sleep 1; for i in {1..3}; do cd /mnt/test/subdir; echo $PWD; sleep 1; cd ..; echo $PWD; sleep 1; done; echo ...; done - while true; do iptables -F OUTPUT; mount -t cifs -a; for _ in {0..2}; do ls /mnt/test/subdir/ | wc -l; done; iptables -I OUTPUT -p tcp --dport 445 -j DROP; sleep 10 echo "unmounting"; umount -l -t cifs -a; echo "done unmounting"; sleep 20 echo "recovering"; iptables -F OUTPUT; sleep 10; done Fixes: ebe98f1447bb ("cifs: enable caching of directories for which a lease is held") Fixes: 5c86919455c1 ("smb: client: fix use-after-free in smb2_query_info_compound()") Cc: stable@vger.kernel.org Signed-off-by: Paul Aurich Signed-off-by: Steve French --- fs/smb/client/cached_dir.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c index adcba1335204..bb9d4c284ce5 100644 --- a/fs/smb/client/cached_dir.c +++ b/fs/smb/client/cached_dir.c @@ -59,6 +59,16 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids, list_add(&cfid->entry, &cfids->entries); cfid->on_list = true; kref_get(&cfid->refcount); + /* + * Set @cfid->has_lease to true during construction so that the lease + * reference can be put in cached_dir_lease_break() due to a potential + * lease break right after the request is sent or while @cfid is still + * being cached, or if a reconnection is triggered during construction. + * Concurrent processes won't be to use it yet due to @cfid->time being + * zero. + */ + cfid->has_lease = true; + spin_unlock(&cfids->cfid_list_lock); return cfid; } @@ -176,12 +186,12 @@ replay_again: return -ENOENT; } /* - * Return cached fid if it has a lease. Otherwise, it is either a new - * entry or laundromat worker removed it from @cfids->entries. Caller - * will put last reference if the latter. + * Return cached fid if it is valid (has a lease and has a time). + * Otherwise, it is either a new entry or laundromat worker removed it + * from @cfids->entries. Caller will put last reference if the latter. */ spin_lock(&cfids->cfid_list_lock); - if (cfid->has_lease) { + if (cfid->has_lease && cfid->time) { spin_unlock(&cfids->cfid_list_lock); *ret_cfid = cfid; kfree(utf16_path); @@ -267,15 +277,6 @@ replay_again: smb2_set_related(&rqst[1]); - /* - * Set @cfid->has_lease to true before sending out compounded request so - * its lease reference can be put in cached_dir_lease_break() due to a - * potential lease break right after the request is sent or while @cfid - * is still being cached. Concurrent processes won't be to use it yet - * due to @cfid->time being zero. - */ - cfid->has_lease = true; - if (retries) { smb2_set_replay(server, &rqst[0]); smb2_set_replay(server, &rqst[1]); -- 2.50.1 From a9685b409a03b73d2980bbfa53eb47555802d0a9 Mon Sep 17 00:00:00 2001 From: Paul Aurich Date: Mon, 18 Nov 2024 13:50:27 -0800 Subject: [PATCH 09/16] smb: prevent use-after-free due to open_cached_dir error paths If open_cached_dir() encounters an error parsing the lease from the server, the error handling may race with receiving a lease break, resulting in open_cached_dir() freeing the cfid while the queued work is pending. Update open_cached_dir() to drop refs rather than directly freeing the cfid. Have cached_dir_lease_break(), cfids_laundromat_worker(), and invalidate_all_cached_dirs() clear has_lease immediately while still holding cfids->cfid_list_lock, and then use this to also simplify the reference counting in cfids_laundromat_worker() and invalidate_all_cached_dirs(). Fixes this KASAN splat (which manually injects an error and lease break in open_cached_dir()): ================================================================== BUG: KASAN: slab-use-after-free in smb2_cached_lease_break+0x27/0xb0 Read of size 8 at addr ffff88811cc24c10 by task kworker/3:1/65 CPU: 3 UID: 0 PID: 65 Comm: kworker/3:1 Not tainted 6.12.0-rc6-g255cf264e6e5-dirty #87 Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 11/12/2020 Workqueue: cifsiod smb2_cached_lease_break Call Trace: dump_stack_lvl+0x77/0xb0 print_report+0xce/0x660 kasan_report+0xd3/0x110 smb2_cached_lease_break+0x27/0xb0 process_one_work+0x50a/0xc50 worker_thread+0x2ba/0x530 kthread+0x17c/0x1c0 ret_from_fork+0x34/0x60 ret_from_fork_asm+0x1a/0x30 Allocated by task 2464: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 open_cached_dir+0xa7d/0x1fb0 smb2_query_path_info+0x43c/0x6e0 cifs_get_fattr+0x346/0xf10 cifs_get_inode_info+0x157/0x210 cifs_revalidate_dentry_attr+0x2d1/0x460 cifs_getattr+0x173/0x470 vfs_statx_path+0x10f/0x160 vfs_statx+0xe9/0x150 vfs_fstatat+0x5e/0xc0 __do_sys_newfstatat+0x91/0xf0 do_syscall_64+0x95/0x1a0 entry_SYSCALL_64_after_hwframe+0x76/0x7e Freed by task 2464: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x51/0x70 kfree+0x174/0x520 open_cached_dir+0x97f/0x1fb0 smb2_query_path_info+0x43c/0x6e0 cifs_get_fattr+0x346/0xf10 cifs_get_inode_info+0x157/0x210 cifs_revalidate_dentry_attr+0x2d1/0x460 cifs_getattr+0x173/0x470 vfs_statx_path+0x10f/0x160 vfs_statx+0xe9/0x150 vfs_fstatat+0x5e/0xc0 __do_sys_newfstatat+0x91/0xf0 do_syscall_64+0x95/0x1a0 entry_SYSCALL_64_after_hwframe+0x76/0x7e Last potentially related work creation: kasan_save_stack+0x33/0x60 __kasan_record_aux_stack+0xad/0xc0 insert_work+0x32/0x100 __queue_work+0x5c9/0x870 queue_work_on+0x82/0x90 open_cached_dir+0x1369/0x1fb0 smb2_query_path_info+0x43c/0x6e0 cifs_get_fattr+0x346/0xf10 cifs_get_inode_info+0x157/0x210 cifs_revalidate_dentry_attr+0x2d1/0x460 cifs_getattr+0x173/0x470 vfs_statx_path+0x10f/0x160 vfs_statx+0xe9/0x150 vfs_fstatat+0x5e/0xc0 __do_sys_newfstatat+0x91/0xf0 do_syscall_64+0x95/0x1a0 entry_SYSCALL_64_after_hwframe+0x76/0x7e The buggy address belongs to the object at ffff88811cc24c00 which belongs to the cache kmalloc-1k of size 1024 The buggy address is located 16 bytes inside of freed 1024-byte region [ffff88811cc24c00, ffff88811cc25000) Cc: stable@vger.kernel.org Signed-off-by: Paul Aurich Signed-off-by: Steve French --- fs/smb/client/cached_dir.c | 70 ++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 41 deletions(-) diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c index bb9d4c284ce5..06eb19dabb0e 100644 --- a/fs/smb/client/cached_dir.c +++ b/fs/smb/client/cached_dir.c @@ -348,6 +348,7 @@ oshr_free: SMB2_query_info_free(&rqst[1]); free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base); free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base); +out: if (rc) { spin_lock(&cfids->cfid_list_lock); if (cfid->on_list) { @@ -359,23 +360,14 @@ oshr_free: /* * We are guaranteed to have two references at this * point. One for the caller and one for a potential - * lease. Release the Lease-ref so that the directory - * will be closed when the caller closes the cached - * handle. + * lease. Release one here, and the second below. */ cfid->has_lease = false; - spin_unlock(&cfids->cfid_list_lock); kref_put(&cfid->refcount, smb2_close_cached_fid); - goto out; } spin_unlock(&cfids->cfid_list_lock); - } -out: - if (rc) { - if (cfid->is_open) - SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid, - cfid->fid.volatile_fid); - free_cached_dir(cfid); + + kref_put(&cfid->refcount, smb2_close_cached_fid); } else { *ret_cfid = cfid; atomic_inc(&tcon->num_remote_opens); @@ -513,25 +505,24 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon) cfids->num_entries--; cfid->is_open = false; cfid->on_list = false; - /* To prevent race with smb2_cached_lease_break() */ - kref_get(&cfid->refcount); + if (cfid->has_lease) { + /* + * The lease was never cancelled from the server, + * so steal that reference. + */ + cfid->has_lease = false; + } else + kref_get(&cfid->refcount); } spin_unlock(&cfids->cfid_list_lock); list_for_each_entry_safe(cfid, q, &entry, entry) { list_del(&cfid->entry); cancel_work_sync(&cfid->lease_break); - if (cfid->has_lease) { - /* - * We lease was never cancelled from the server so we - * need to drop the reference. - */ - spin_lock(&cfids->cfid_list_lock); - cfid->has_lease = false; - spin_unlock(&cfids->cfid_list_lock); - kref_put(&cfid->refcount, smb2_close_cached_fid); - } - /* Drop the extra reference opened above*/ + /* + * Drop the ref-count from above, either the lease-ref (if there + * was one) or the extra one acquired. + */ kref_put(&cfid->refcount, smb2_close_cached_fid); } } @@ -542,9 +533,6 @@ smb2_cached_lease_break(struct work_struct *work) struct cached_fid *cfid = container_of(work, struct cached_fid, lease_break); - spin_lock(&cfid->cfids->cfid_list_lock); - cfid->has_lease = false; - spin_unlock(&cfid->cfids->cfid_list_lock); kref_put(&cfid->refcount, smb2_close_cached_fid); } @@ -562,6 +550,7 @@ int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]) !memcmp(lease_key, cfid->fid.lease_key, SMB2_LEASE_KEY_SIZE)) { + cfid->has_lease = false; cfid->time = 0; /* * We found a lease remove it from the list @@ -639,8 +628,14 @@ static void cfids_laundromat_worker(struct work_struct *work) cfid->on_list = false; list_move(&cfid->entry, &entry); cfids->num_entries--; - /* To prevent race with smb2_cached_lease_break() */ - kref_get(&cfid->refcount); + if (cfid->has_lease) { + /* + * Our lease has not yet been cancelled from the + * server. Steal that reference. + */ + cfid->has_lease = false; + } else + kref_get(&cfid->refcount); } } spin_unlock(&cfids->cfid_list_lock); @@ -652,17 +647,10 @@ static void cfids_laundromat_worker(struct work_struct *work) * with it. */ cancel_work_sync(&cfid->lease_break); - if (cfid->has_lease) { - /* - * Our lease has not yet been cancelled from the server - * so we need to drop the reference. - */ - spin_lock(&cfids->cfid_list_lock); - cfid->has_lease = false; - spin_unlock(&cfids->cfid_list_lock); - kref_put(&cfid->refcount, smb2_close_cached_fid); - } - /* Drop the extra reference opened above */ + /* + * Drop the ref-count from above, either the lease-ref (if there + * was one) or the extra one acquired. + */ kref_put(&cfid->refcount, smb2_close_cached_fid); } queue_delayed_work(cifsiod_wq, &cfids->laundromat_work, -- 2.50.1 From 02e9bda80d66cdd53364b9b32166ac0be1ced58f Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 14 Oct 2024 10:16:43 -0400 Subject: [PATCH 10/16] tpm: ibmvtpm: Set TPM_OPS_AUTO_STARTUP flag on driver Set the TPM_OPS_AUTO_STARTUP on the driver so that the ibmvtpm driver now uses tpm2_auto_startup and tpm1_auto_startup like many other drivers do. Remove tpm_get_timeouts, tpm2_get_cc_attrs_tbl, and tpm2_sessions_init calls from it since these will all be called in tpm2_auto_startup and tpm1_auto_startup. The exporting of the tpm2_session_init symbol was only necessary while the ibmvtpm driver was calling this function. Since this is not the case anymore, remove this symbol from being exported. What is new for the ibmvtpm driver is that now tpm2_do_selftest and tpm1_do_selftest will be called that send commands to the TPM to perform or continue its selftest. However, the firmware should already have sent these commands so that the TPM will not do much work at this time. Signed-off-by: Stefan Berger Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm2-sessions.c | 1 - drivers/char/tpm/tpm_ibmvtpm.c | 15 +-------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c index b0f13c8ea79c..b70165b588ec 100644 --- a/drivers/char/tpm/tpm2-sessions.c +++ b/drivers/char/tpm/tpm2-sessions.c @@ -1390,5 +1390,4 @@ int tpm2_sessions_init(struct tpm_chip *chip) return rc; } -EXPORT_SYMBOL(tpm2_sessions_init); #endif /* CONFIG_TCG_TPM2_HMAC */ diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 1e5b107d1f3b..76d048f63d55 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -450,6 +450,7 @@ static bool tpm_ibmvtpm_req_canceled(struct tpm_chip *chip, u8 status) } static const struct tpm_class_ops tpm_ibmvtpm = { + .flags = TPM_OPS_AUTO_STARTUP, .recv = tpm_ibmvtpm_recv, .send = tpm_ibmvtpm_send, .cancel = tpm_ibmvtpm_cancel, @@ -690,20 +691,6 @@ static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev, if (!strcmp(id->compat, "IBM,vtpm20")) chip->flags |= TPM_CHIP_FLAG_TPM2; - rc = tpm_get_timeouts(chip); - if (rc) - goto init_irq_cleanup; - - if (chip->flags & TPM_CHIP_FLAG_TPM2) { - rc = tpm2_get_cc_attrs_tbl(chip); - if (rc) - goto init_irq_cleanup; - - rc = tpm2_sessions_init(chip); - if (rc) - goto init_irq_cleanup; - } - return tpm_chip_register(chip); init_irq_cleanup: do { -- 2.50.1 From 932e3a5e1ea31712c91e6b4c64d0c7f675315ab4 Mon Sep 17 00:00:00 2001 From: Jan Dabros Date: Thu, 10 Oct 2024 09:15:58 +0000 Subject: [PATCH 11/16] char: tpm: cr50: Use generic request/relinquish locality ops Instead of using static functions tpm_cr50_request_locality and tpm_cr50_release_locality register callbacks from tpm class chip->ops created for this purpose. Signed-off-by: Jan Dabros Signed-off-by: Grzegorz Bernacki Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_i2c_cr50.c | 94 +++++++++++++++++------------ 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_i2c_cr50.c b/drivers/char/tpm/tpm_tis_i2c_cr50.c index adf22992138e..eed1c296a00c 100644 --- a/drivers/char/tpm/tpm_tis_i2c_cr50.c +++ b/drivers/char/tpm/tpm_tis_i2c_cr50.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -35,6 +36,7 @@ #define TPM_CR50_I2C_MAX_RETRIES 3 /* Max retries due to I2C errors */ #define TPM_CR50_I2C_RETRY_DELAY_LO 55 /* Min usecs between retries on I2C */ #define TPM_CR50_I2C_RETRY_DELAY_HI 65 /* Max usecs between retries on I2C */ +#define TPM_CR50_I2C_DEFAULT_LOC 0 #define TPM_I2C_ACCESS(l) (0x0000 | ((l) << 4)) #define TPM_I2C_STS(l) (0x0001 | ((l) << 4)) @@ -285,25 +287,26 @@ out: } /** - * tpm_cr50_check_locality() - Verify TPM locality 0 is active. + * tpm_cr50_check_locality() - Verify if required TPM locality is active. * @chip: A TPM chip. + * @loc: Locality to be verified * * Return: - * - 0: Success. + * - loc: Success. * - -errno: A POSIX error code. */ -static int tpm_cr50_check_locality(struct tpm_chip *chip) +static int tpm_cr50_check_locality(struct tpm_chip *chip, int loc) { u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY; u8 buf; int rc; - rc = tpm_cr50_i2c_read(chip, TPM_I2C_ACCESS(0), &buf, sizeof(buf)); + rc = tpm_cr50_i2c_read(chip, TPM_I2C_ACCESS(loc), &buf, sizeof(buf)); if (rc < 0) return rc; if ((buf & mask) == mask) - return 0; + return loc; return -EIO; } @@ -311,48 +314,57 @@ static int tpm_cr50_check_locality(struct tpm_chip *chip) /** * tpm_cr50_release_locality() - Release TPM locality. * @chip: A TPM chip. - * @force: Flag to force release if set. + * @loc: Locality to be released + * + * Return: + * - 0: Success. + * - -errno: A POSIX error code. */ -static void tpm_cr50_release_locality(struct tpm_chip *chip, bool force) +static int tpm_cr50_release_locality(struct tpm_chip *chip, int loc) { u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_REQUEST_PENDING; - u8 addr = TPM_I2C_ACCESS(0); + u8 addr = TPM_I2C_ACCESS(loc); u8 buf; + int rc; - if (tpm_cr50_i2c_read(chip, addr, &buf, sizeof(buf)) < 0) - return; + rc = tpm_cr50_i2c_read(chip, addr, &buf, sizeof(buf)); + if (rc < 0) + return rc; - if (force || (buf & mask) == mask) { + if ((buf & mask) == mask) { buf = TPM_ACCESS_ACTIVE_LOCALITY; - tpm_cr50_i2c_write(chip, addr, &buf, sizeof(buf)); + rc = tpm_cr50_i2c_write(chip, addr, &buf, sizeof(buf)); } + + return rc; } /** - * tpm_cr50_request_locality() - Request TPM locality 0. + * tpm_cr50_request_locality() - Request TPM locality. * @chip: A TPM chip. + * @loc: Locality to be requested. * * Return: - * - 0: Success. + * - loc: Success. * - -errno: A POSIX error code. */ -static int tpm_cr50_request_locality(struct tpm_chip *chip) +static int tpm_cr50_request_locality(struct tpm_chip *chip, int loc) { u8 buf = TPM_ACCESS_REQUEST_USE; unsigned long stop; int rc; - if (!tpm_cr50_check_locality(chip)) - return 0; + if (tpm_cr50_check_locality(chip, loc) == loc) + return loc; - rc = tpm_cr50_i2c_write(chip, TPM_I2C_ACCESS(0), &buf, sizeof(buf)); + rc = tpm_cr50_i2c_write(chip, TPM_I2C_ACCESS(loc), &buf, sizeof(buf)); if (rc < 0) return rc; stop = jiffies + chip->timeout_a; do { - if (!tpm_cr50_check_locality(chip)) - return 0; + if (tpm_cr50_check_locality(chip, loc) == loc) + return loc; msleep(TPM_CR50_TIMEOUT_SHORT_MS); } while (time_before(jiffies, stop)); @@ -373,7 +385,7 @@ static u8 tpm_cr50_i2c_tis_status(struct tpm_chip *chip) { u8 buf[4]; - if (tpm_cr50_i2c_read(chip, TPM_I2C_STS(0), buf, sizeof(buf)) < 0) + if (tpm_cr50_i2c_read(chip, TPM_I2C_STS(chip->locality), buf, sizeof(buf)) < 0) return 0; return buf[0]; @@ -389,7 +401,7 @@ static void tpm_cr50_i2c_tis_set_ready(struct tpm_chip *chip) { u8 buf[4] = { TPM_STS_COMMAND_READY }; - tpm_cr50_i2c_write(chip, TPM_I2C_STS(0), buf, sizeof(buf)); + tpm_cr50_i2c_write(chip, TPM_I2C_STS(chip->locality), buf, sizeof(buf)); msleep(TPM_CR50_TIMEOUT_SHORT_MS); } @@ -419,7 +431,7 @@ static int tpm_cr50_i2c_get_burst_and_status(struct tpm_chip *chip, u8 mask, stop = jiffies + chip->timeout_b; do { - if (tpm_cr50_i2c_read(chip, TPM_I2C_STS(0), buf, sizeof(buf)) < 0) { + if (tpm_cr50_i2c_read(chip, TPM_I2C_STS(chip->locality), buf, sizeof(buf)) < 0) { msleep(TPM_CR50_TIMEOUT_SHORT_MS); continue; } @@ -453,7 +465,7 @@ static int tpm_cr50_i2c_tis_recv(struct tpm_chip *chip, u8 *buf, size_t buf_len) u8 mask = TPM_STS_VALID | TPM_STS_DATA_AVAIL; size_t burstcnt, cur, len, expected; - u8 addr = TPM_I2C_DATA_FIFO(0); + u8 addr = TPM_I2C_DATA_FIFO(chip->locality); u32 status; int rc; @@ -515,7 +527,6 @@ static int tpm_cr50_i2c_tis_recv(struct tpm_chip *chip, u8 *buf, size_t buf_len) goto out_err; } - tpm_cr50_release_locality(chip, false); return cur; out_err: @@ -523,7 +534,6 @@ out_err: if (tpm_cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY) tpm_cr50_i2c_tis_set_ready(chip); - tpm_cr50_release_locality(chip, false); return rc; } @@ -545,10 +555,6 @@ static int tpm_cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) u32 status; int rc; - rc = tpm_cr50_request_locality(chip); - if (rc < 0) - return rc; - /* Wait until TPM is ready for a command */ stop = jiffies + chip->timeout_b; while (!(tpm_cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY)) { @@ -577,7 +583,8 @@ static int tpm_cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) * that is inserted by tpm_cr50_i2c_write() */ limit = min_t(size_t, burstcnt - 1, len); - rc = tpm_cr50_i2c_write(chip, TPM_I2C_DATA_FIFO(0), &buf[sent], limit); + rc = tpm_cr50_i2c_write(chip, TPM_I2C_DATA_FIFO(chip->locality), + &buf[sent], limit); if (rc < 0) { dev_err(&chip->dev, "Write failed\n"); goto out_err; @@ -598,7 +605,7 @@ static int tpm_cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) } /* Start the TPM command */ - rc = tpm_cr50_i2c_write(chip, TPM_I2C_STS(0), tpm_go, + rc = tpm_cr50_i2c_write(chip, TPM_I2C_STS(chip->locality), tpm_go, sizeof(tpm_go)); if (rc < 0) { dev_err(&chip->dev, "Start command failed\n"); @@ -611,7 +618,6 @@ out_err: if (tpm_cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY) tpm_cr50_i2c_tis_set_ready(chip); - tpm_cr50_release_locality(chip, false); return rc; } @@ -650,6 +656,8 @@ static const struct tpm_class_ops cr50_i2c = { .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_canceled = &tpm_cr50_i2c_req_canceled, + .request_locality = &tpm_cr50_request_locality, + .relinquish_locality = &tpm_cr50_release_locality, }; #ifdef CONFIG_ACPI @@ -684,6 +692,7 @@ static int tpm_cr50_i2c_probe(struct i2c_client *client) u32 vendor; u8 buf[4]; int rc; + int loc; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; @@ -726,24 +735,30 @@ static int tpm_cr50_i2c_probe(struct i2c_client *client) TPM_CR50_TIMEOUT_NOIRQ_MS); } - rc = tpm_cr50_request_locality(chip); - if (rc < 0) { + loc = tpm_cr50_request_locality(chip, TPM_CR50_I2C_DEFAULT_LOC); + if (loc < 0) { dev_err(dev, "Could not request locality\n"); - return rc; + return loc; } /* Read four bytes from DID_VID register */ - rc = tpm_cr50_i2c_read(chip, TPM_I2C_DID_VID(0), buf, sizeof(buf)); + rc = tpm_cr50_i2c_read(chip, TPM_I2C_DID_VID(loc), buf, sizeof(buf)); if (rc < 0) { dev_err(dev, "Could not read vendor id\n"); - tpm_cr50_release_locality(chip, true); + if (tpm_cr50_release_locality(chip, loc)) + dev_err(dev, "Could not release locality\n"); + return rc; + } + + rc = tpm_cr50_release_locality(chip, loc); + if (rc) { + dev_err(dev, "Could not release locality\n"); return rc; } vendor = le32_to_cpup((__le32 *)buf); if (vendor != TPM_CR50_I2C_DID_VID && vendor != TPM_TI50_I2C_DID_VID) { dev_err(dev, "Vendor ID did not match! ID was %08x\n", vendor); - tpm_cr50_release_locality(chip, true); return -ENODEV; } @@ -772,7 +787,6 @@ static void tpm_cr50_i2c_remove(struct i2c_client *client) } tpm_chip_unregister(chip); - tpm_cr50_release_locality(chip, true); } static SIMPLE_DEV_PM_OPS(cr50_i2c_pm, tpm_pm_suspend, tpm_pm_resume); -- 2.50.1 From 44637b0b40f47629ff78a73744755e6535e0970e Mon Sep 17 00:00:00 2001 From: Jan Dabros Date: Thu, 10 Oct 2024 09:15:59 +0000 Subject: [PATCH 12/16] char: tpm: cr50: Move i2c locking to request/relinquish locality ops Move i2c locking primitives to request_locality and relinquish_locality callbacks, what effectively blocks TPM bus for the whole duration of logical TPM operation. With this in place, cr50-equipped TPM may be shared with external CPUs - assuming that underneath i2c controller driver is aware of this setup (see i2c-designware-amdpsp as an example). Signed-off-by: Jan Dabros Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_i2c_cr50.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_i2c_cr50.c b/drivers/char/tpm/tpm_tis_i2c_cr50.c index eed1c296a00c..80b0f41ffb5f 100644 --- a/drivers/char/tpm/tpm_tis_i2c_cr50.c +++ b/drivers/char/tpm/tpm_tis_i2c_cr50.c @@ -201,8 +201,6 @@ static int tpm_cr50_i2c_read(struct tpm_chip *chip, u8 addr, u8 *buffer, size_t }; int rc; - i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); - /* Prepare for completion interrupt */ tpm_cr50_i2c_enable_tpm_irq(chip); @@ -221,7 +219,6 @@ static int tpm_cr50_i2c_read(struct tpm_chip *chip, u8 addr, u8 *buffer, size_t out: tpm_cr50_i2c_disable_tpm_irq(chip); - i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); if (rc < 0) return rc; @@ -263,8 +260,6 @@ static int tpm_cr50_i2c_write(struct tpm_chip *chip, u8 addr, u8 *buffer, priv->buf[0] = addr; memcpy(priv->buf + 1, buffer, len); - i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); - /* Prepare for completion interrupt */ tpm_cr50_i2c_enable_tpm_irq(chip); @@ -278,7 +273,6 @@ static int tpm_cr50_i2c_write(struct tpm_chip *chip, u8 addr, u8 *buffer, out: tpm_cr50_i2c_disable_tpm_irq(chip); - i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); if (rc < 0) return rc; @@ -322,6 +316,7 @@ static int tpm_cr50_check_locality(struct tpm_chip *chip, int loc) */ static int tpm_cr50_release_locality(struct tpm_chip *chip, int loc) { + struct i2c_client *client = to_i2c_client(chip->dev.parent); u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_REQUEST_PENDING; u8 addr = TPM_I2C_ACCESS(loc); u8 buf; @@ -329,13 +324,15 @@ static int tpm_cr50_release_locality(struct tpm_chip *chip, int loc) rc = tpm_cr50_i2c_read(chip, addr, &buf, sizeof(buf)); if (rc < 0) - return rc; + goto unlock_out; if ((buf & mask) == mask) { buf = TPM_ACCESS_ACTIVE_LOCALITY; rc = tpm_cr50_i2c_write(chip, addr, &buf, sizeof(buf)); } +unlock_out: + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); return rc; } @@ -350,16 +347,19 @@ static int tpm_cr50_release_locality(struct tpm_chip *chip, int loc) */ static int tpm_cr50_request_locality(struct tpm_chip *chip, int loc) { + struct i2c_client *client = to_i2c_client(chip->dev.parent); u8 buf = TPM_ACCESS_REQUEST_USE; unsigned long stop; int rc; + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + if (tpm_cr50_check_locality(chip, loc) == loc) return loc; rc = tpm_cr50_i2c_write(chip, TPM_I2C_ACCESS(loc), &buf, sizeof(buf)); if (rc < 0) - return rc; + goto unlock_out; stop = jiffies + chip->timeout_a; do { @@ -369,7 +369,11 @@ static int tpm_cr50_request_locality(struct tpm_chip *chip, int loc) msleep(TPM_CR50_TIMEOUT_SHORT_MS); } while (time_before(jiffies, stop)); - return -ETIMEDOUT; + rc = -ETIMEDOUT; + +unlock_out: + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + return rc; } /** -- 2.50.1 From 2e1827de1b0f22b420c9446a3c94e6cce8eb4da4 Mon Sep 17 00:00:00 2001 From: Jett Rink Date: Tue, 10 Sep 2024 19:11:14 +0000 Subject: [PATCH 13/16] char: tpm: cr50: Add new device/vendor ID 0x50666666 Accept another DID:VID for the next generation Google TPM. This TPM has the same Ti50 firmware and fulfills the same interface. Suggested-by: Jarkko Sakkinen Signed-off-by: Jett Rink Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_i2c_cr50.c | 32 +++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_i2c_cr50.c b/drivers/char/tpm/tpm_tis_i2c_cr50.c index 80b0f41ffb5f..3b55a7b05c46 100644 --- a/drivers/char/tpm/tpm_tis_i2c_cr50.c +++ b/drivers/char/tpm/tpm_tis_i2c_cr50.c @@ -31,8 +31,9 @@ #define TPM_CR50_MAX_BUFSIZE 64 #define TPM_CR50_TIMEOUT_SHORT_MS 2 /* Short timeout during transactions */ #define TPM_CR50_TIMEOUT_NOIRQ_MS 20 /* Timeout for TPM ready without IRQ */ -#define TPM_CR50_I2C_DID_VID 0x00281ae0L /* Device and vendor ID reg value */ -#define TPM_TI50_I2C_DID_VID 0x504a6666L /* Device and vendor ID reg value */ +#define TPM_CR50_I2C_DID_VID 0x00281ae0L /* Device and vendor ID for Cr50 H1 */ +#define TPM_TI50_DT_I2C_DID_VID 0x504a6666L /* Device and vendor ID for Ti50 DT */ +#define TPM_TI50_OT_I2C_DID_VID 0x50666666L /* Device and vendor ID for TI50 OT */ #define TPM_CR50_I2C_MAX_RETRIES 3 /* Max retries due to I2C errors */ #define TPM_CR50_I2C_RETRY_DELAY_LO 55 /* Min usecs between retries on I2C */ #define TPM_CR50_I2C_RETRY_DELAY_HI 65 /* Max usecs between retries on I2C */ @@ -680,6 +681,27 @@ static const struct of_device_id of_cr50_i2c_match[] = { MODULE_DEVICE_TABLE(of, of_cr50_i2c_match); #endif +/** + * tpm_cr50_vid_to_name() - Maps VID to name. + * @vendor: Vendor identifier to map to name + * + * Return: + * A valid string for the vendor or empty string + */ +static const char *tpm_cr50_vid_to_name(u32 vendor) +{ + switch (vendor) { + case TPM_CR50_I2C_DID_VID: + return "cr50"; + case TPM_TI50_DT_I2C_DID_VID: + return "ti50 DT"; + case TPM_TI50_OT_I2C_DID_VID: + return "ti50 OT"; + default: + return "unknown"; + } +} + /** * tpm_cr50_i2c_probe() - Driver probe function. * @client: I2C client information. @@ -761,13 +783,15 @@ static int tpm_cr50_i2c_probe(struct i2c_client *client) } vendor = le32_to_cpup((__le32 *)buf); - if (vendor != TPM_CR50_I2C_DID_VID && vendor != TPM_TI50_I2C_DID_VID) { + if (vendor != TPM_CR50_I2C_DID_VID && + vendor != TPM_TI50_DT_I2C_DID_VID && + vendor != TPM_TI50_OT_I2C_DID_VID) { dev_err(dev, "Vendor ID did not match! ID was %08x\n", vendor); return -ENODEV; } dev_info(dev, "%s TPM 2.0 (i2c 0x%02x irq %d id 0x%x)\n", - vendor == TPM_TI50_I2C_DID_VID ? "ti50" : "cr50", + tpm_cr50_vid_to_name(vendor), client->addr, client->irq, vendor >> 16); return tpm_chip_register(chip); } -- 2.50.1 From 5578b4347bb5d5dfc8eeb8ee2eb8248658707d9b Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Wed, 6 Nov 2024 20:17:42 +0200 Subject: [PATCH 14/16] tpm: atmel: Drop PPC64 specific MMIO setup The PPC64 specific MMIO setup open codes DT address functions rather than using standard address parsing functions. The open-coded version fails to handle any address translation and is not endian safe. I haven't found any evidence of what platform used this. The only thing that turned up was a PPC405 platform, but that is 32-bit and PPC405 support is being removed as well. CONFIG_TCG_ATMEL is not enabled for any powerpc config and never was. The support was added in 2005 and hasn't been touched since. Rather than try to modernize and fix this code, just remove it. [jarkko: fixed couple of style issues reported by checkpatch.pl --strict and put offset into parentheses in the macro declarations.] Signed-off-by: Rob Herring (Arm) Acked-by: Michael Ellerman Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/Kconfig | 2 +- drivers/char/tpm/tpm_atmel.c | 63 +++++++++++++++- drivers/char/tpm/tpm_atmel.h | 140 ----------------------------------- 3 files changed, 61 insertions(+), 144 deletions(-) delete mode 100644 drivers/char/tpm/tpm_atmel.h diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index cf0be8a7939d..0fc9a510e059 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -162,7 +162,7 @@ config TCG_NSC config TCG_ATMEL tristate "Atmel TPM Interface" - depends on PPC64 || HAS_IOPORT_MAP + depends on HAS_IOPORT_MAP depends on HAS_IOPORT help If you have a TPM security chip from Atmel say Yes and it diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index 9fb2defa9dc4..54a0360a3c95 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -15,7 +15,66 @@ */ #include "tpm.h" -#include "tpm_atmel.h" + +struct tpm_atmel_priv { + int region_size; + int have_region; + unsigned long base; + void __iomem *iobase; +}; + +#define atmel_getb(chip, offset) inb(atmel_get_priv(chip)->base + (offset)) +#define atmel_putb(val, chip, offset) \ + outb(val, atmel_get_priv(chip)->base + (offset)) +#define atmel_request_region request_region +#define atmel_release_region release_region +/* Atmel definitions */ +enum tpm_atmel_addr { + TPM_ATMEL_BASE_ADDR_LO = 0x08, + TPM_ATMEL_BASE_ADDR_HI = 0x09 +}; + +static inline int tpm_read_index(int base, int index) +{ + outb(index, base); + return inb(base + 1) & 0xFF; +} + +/* Verify this is a 1.1 Atmel TPM */ +static int atmel_verify_tpm11(void) +{ + /* verify that it is an Atmel part */ + if (tpm_read_index(TPM_ADDR, 4) != 'A' || + tpm_read_index(TPM_ADDR, 5) != 'T' || + tpm_read_index(TPM_ADDR, 6) != 'M' || + tpm_read_index(TPM_ADDR, 7) != 'L') + return 1; + + /* query chip for its version number */ + if (tpm_read_index(TPM_ADDR, 0x00) != 1 || + tpm_read_index(TPM_ADDR, 0x01) != 1) + return 1; + + /* This is an atmel supported part */ + return 0; +} + +/* Determine where to talk to device */ +static void __iomem *atmel_get_base_addr(unsigned long *base, int *region_size) +{ + int lo, hi; + + if (atmel_verify_tpm11() != 0) + return NULL; + + lo = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_LO); + hi = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_HI); + + *base = (hi << 8) | lo; + *region_size = 2; + + return ioport_map(*base, *region_size); +} /* write status bits */ enum tpm_atmel_write_status { @@ -142,7 +201,6 @@ static void atml_plat_remove(void) tpm_chip_unregister(chip); if (priv->have_region) atmel_release_region(priv->base, priv->region_size); - atmel_put_base_addr(priv->iobase); platform_device_unregister(pdev); } @@ -211,7 +269,6 @@ static int __init init_atmel(void) err_unreg_dev: platform_device_unregister(pdev); err_rel_reg: - atmel_put_base_addr(iobase); if (have_region) atmel_release_region(base, region_size); diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h deleted file mode 100644 index 7ac3f69dcf0f..000000000000 --- a/drivers/char/tpm/tpm_atmel.h +++ /dev/null @@ -1,140 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2005 IBM Corporation - * - * Authors: - * Kylene Hall - * - * Maintained by: - * - * Device driver for TCG/TCPA TPM (trusted platform module). - * Specifications at www.trustedcomputinggroup.org - * - * These difference are required on power because the device must be - * discovered through the device tree and iomap must be used to get - * around the need for holes in the io_page_mask. This does not happen - * automatically because the tpm is not a normal pci device and lives - * under the root node. - */ - -struct tpm_atmel_priv { - int region_size; - int have_region; - unsigned long base; - void __iomem *iobase; -}; - -#ifdef CONFIG_PPC64 - -#include - -#define atmel_getb(priv, offset) readb(priv->iobase + offset) -#define atmel_putb(val, priv, offset) writeb(val, priv->iobase + offset) -#define atmel_request_region request_mem_region -#define atmel_release_region release_mem_region - -static inline void atmel_put_base_addr(void __iomem *iobase) -{ - iounmap(iobase); -} - -static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) -{ - struct device_node *dn; - unsigned long address, size; - const unsigned int *reg; - int reglen; - int naddrc; - int nsizec; - - dn = of_find_node_by_name(NULL, "tpm"); - - if (!dn) - return NULL; - - if (!of_device_is_compatible(dn, "AT97SC3201")) { - of_node_put(dn); - return NULL; - } - - reg = of_get_property(dn, "reg", ®len); - naddrc = of_n_addr_cells(dn); - nsizec = of_n_size_cells(dn); - - of_node_put(dn); - - - if (naddrc == 2) - address = ((unsigned long) reg[0] << 32) | reg[1]; - else - address = reg[0]; - - if (nsizec == 2) - size = - ((unsigned long) reg[naddrc] << 32) | reg[naddrc + 1]; - else - size = reg[naddrc]; - - *base = address; - *region_size = size; - return ioremap(*base, *region_size); -} -#else -#define atmel_getb(chip, offset) inb(atmel_get_priv(chip)->base + offset) -#define atmel_putb(val, chip, offset) \ - outb(val, atmel_get_priv(chip)->base + offset) -#define atmel_request_region request_region -#define atmel_release_region release_region -/* Atmel definitions */ -enum tpm_atmel_addr { - TPM_ATMEL_BASE_ADDR_LO = 0x08, - TPM_ATMEL_BASE_ADDR_HI = 0x09 -}; - -static inline int tpm_read_index(int base, int index) -{ - outb(index, base); - return inb(base+1) & 0xFF; -} - -/* Verify this is a 1.1 Atmel TPM */ -static int atmel_verify_tpm11(void) -{ - - /* verify that it is an Atmel part */ - if (tpm_read_index(TPM_ADDR, 4) != 'A' || - tpm_read_index(TPM_ADDR, 5) != 'T' || - tpm_read_index(TPM_ADDR, 6) != 'M' || - tpm_read_index(TPM_ADDR, 7) != 'L') - return 1; - - /* query chip for its version number */ - if (tpm_read_index(TPM_ADDR, 0x00) != 1 || - tpm_read_index(TPM_ADDR, 0x01) != 1) - return 1; - - /* This is an atmel supported part */ - return 0; -} - -static inline void atmel_put_base_addr(void __iomem *iobase) -{ -} - -/* Determine where to talk to device */ -static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) -{ - int lo, hi; - - if (atmel_verify_tpm11() != 0) - return NULL; - - lo = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_LO); - hi = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_HI); - - *base = (hi << 8) | lo; - *region_size = 2; - - return ioport_map(*base, *region_size); -} -#endif -- 2.50.1 From 919464deeca24e5bf13b6c8efd0b1d25cc43866f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 25 Nov 2024 09:21:21 -0800 Subject: [PATCH 15/16] Revert "HID: bpf: allow write access to quirks field in struct hid_device" This reverts commit 6fd47effe92b, and the related self-test update commit e14e0eaeb040 ("selftests/hid: add test for assigning a given device to hid-generic"). It results in things like the scroll wheel on Logitech mice not working after a reboot due to the kernel being confused about the state of the high-resolution mode. Quoting Benjamin Tissoires: "The idea of 6fd47effe92b was to be able to call hid_bpf_rdesc_fixup() once per reprobe of the device. However, because the bpf filter can now change the quirk value, the call had to be moved before the driver gets bound (which was previously ensuring the unicity of the call). The net effect is that now, in the case hid-generic gets loaded first and then the specific driver gets loaded once the disk is available, the value of ->quirks is not reset, but kept to the value that was set by hid-generic (HID_QUIRK_INPUT_PER_APP). Once hid-logitech-hidpp kicks in, that quirk is now set, which creates two inputs for the single mouse: one keyboard for fancy shortcuts, and one mouse node. However, hid-logitech-hidpp expects only one input node to be attached (it stores it into hidpp->input), and when a wheel event is received, because there is some processing with high-resolution wheel events, the wheel event is injected into hidpp->input. And of course, when HID_QUIRK_INPUT_PER_APP is set, hidpp->input gets the keyboard node, which doesn't have wheel event type, and the events are ignored" Reported-and-bisected-by: Mike Galbraith Link: https://lore.kernel.org/all/CAHk-=wiUkQM3uheit2cNM0Y0OOY5qqspJgC8LkmOkJ2p2LDxcw@mail.gmail.com/ Acked-by: Benjamin Tissoires Signed-off-by: Linus Torvalds --- drivers/hid/bpf/hid_bpf_struct_ops.c | 1 - drivers/hid/hid-core.c | 11 +-- tools/testing/selftests/hid/hid_bpf.c | 80 +------------------ tools/testing/selftests/hid/progs/hid.c | 12 --- .../selftests/hid/progs/hid_bpf_helpers.h | 6 +- 5 files changed, 4 insertions(+), 106 deletions(-) diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c index 0e611a9d79d7..702c22fae136 100644 --- a/drivers/hid/bpf/hid_bpf_struct_ops.c +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c @@ -79,7 +79,6 @@ static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log, WRITE_RANGE(hid_device, name, true), WRITE_RANGE(hid_device, uniq, true), WRITE_RANGE(hid_device, phys, true), - WRITE_RANGE(hid_device, quirks, false), }; #undef WRITE_RANGE const struct btf_type *state = NULL; diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 81d6c734c8bc..98bef39642a9 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2692,12 +2692,6 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) int ret; if (!hdev->bpf_rsize) { - unsigned int quirks; - - /* reset the quirks that has been previously set */ - quirks = hid_lookup_quirk(hdev); - hdev->quirks = quirks; - /* in case a bpf program gets detached, we need to free the old one */ hid_free_bpf_rdesc(hdev); @@ -2707,9 +2701,6 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) /* call_hid_bpf_rdesc_fixup will always return a valid pointer */ hdev->bpf_rdesc = call_hid_bpf_rdesc_fixup(hdev, hdev->dev_rdesc, &hdev->bpf_rsize); - if (quirks ^ hdev->quirks) - hid_info(hdev, "HID-BPF toggled quirks on the device: %04x", - quirks ^ hdev->quirks); } if (!hid_check_device_match(hdev, hdrv, &id)) @@ -2719,6 +2710,8 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) if (!hdev->devres_group_id) return -ENOMEM; + /* reset the quirks that has been previously set */ + hdev->quirks = hid_lookup_quirk(hdev); hdev->driver = hdrv; if (hdrv->probe) { diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c index ca58bfa3ca65..1e979fb3542b 100644 --- a/tools/testing/selftests/hid/hid_bpf.c +++ b/tools/testing/selftests/hid/hid_bpf.c @@ -54,41 +54,11 @@ FIXTURE_TEARDOWN(hid_bpf) { hid_bpf_teardown(_metadata, self, variant); \ } while (0) -struct specific_device { - const char test_name[64]; - __u16 bus; - __u32 vid; - __u32 pid; -}; - FIXTURE_SETUP(hid_bpf) { - const struct specific_device *match = NULL; int err; - const struct specific_device devices[] = { - { - .test_name = "test_hid_driver_probe", - .bus = BUS_BLUETOOTH, - .vid = 0x05ac, /* USB_VENDOR_ID_APPLE */ - .pid = 0x022c, /* USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI */ - }, { - .test_name = "*", - .bus = BUS_USB, - .vid = 0x0001, - .pid = 0x0a36, - }}; - - for (int i = 0; i < ARRAY_SIZE(devices); i++) { - match = &devices[i]; - if (!strncmp(_metadata->name, devices[i].test_name, sizeof(devices[i].test_name))) - break; - } - - ASSERT_OK_PTR(match); - - err = setup_uhid(_metadata, &self->hid, match->bus, match->vid, match->pid, - rdesc, sizeof(rdesc)); + err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a36, rdesc, sizeof(rdesc)); ASSERT_OK(err); } @@ -885,54 +855,6 @@ TEST_F(hid_bpf, test_hid_attach_flags) ASSERT_EQ(buf[3], 3); } -static bool is_using_driver(struct __test_metadata *_metadata, struct uhid_device *hid, - const char *driver) -{ - char driver_line[512]; - char uevent[1024]; - char temp[512]; - int fd, nread; - bool found = false; - - sprintf(uevent, "/sys/bus/hid/devices/%04X:%04X:%04X.%04X/uevent", - hid->bus, hid->vid, hid->pid, hid->hid_id); - - fd = open(uevent, O_RDONLY | O_NONBLOCK); - if (fd < 0) { - TH_LOG("couldn't open '%s': %d, %d", uevent, fd, errno); - return false; - } - - sprintf(driver_line, "DRIVER=%s", driver); - - nread = read(fd, temp, ARRAY_SIZE(temp)); - if (nread > 0 && (strstr(temp, driver_line)) != NULL) - found = true; - - close(fd); - - return found; -} - -/* - * Attach hid_driver_probe to the given uhid device, - * check that the device is now using hid-generic. - */ -TEST_F(hid_bpf, test_hid_driver_probe) -{ - const struct test_program progs[] = { - { - .name = "hid_test_driver_probe", - }, - }; - - ASSERT_TRUE(is_using_driver(_metadata, &self->hid, "apple")); - - LOAD_PROGRAMS(progs); - - ASSERT_TRUE(is_using_driver(_metadata, &self->hid, "hid-generic")); -} - /* * Attach hid_rdesc_fixup to the given uhid device, * retrieve and open the matching hidraw node, diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c index 9b22e9a0e658..5ecc845ef792 100644 --- a/tools/testing/selftests/hid/progs/hid.c +++ b/tools/testing/selftests/hid/progs/hid.c @@ -598,15 +598,3 @@ SEC(".struct_ops.link") struct hid_bpf_ops test_infinite_loop_input_report = { .hid_device_event = (void *)hid_test_infinite_loop_input_report, }; - -SEC("?struct_ops.s/hid_rdesc_fixup") -int BPF_PROG(hid_test_driver_probe, struct hid_bpf_ctx *hid_ctx) -{ - hid_ctx->hid->quirks |= HID_QUIRK_IGNORE_SPECIAL_DRIVER; - return 0; -} - -SEC(".struct_ops.link") -struct hid_bpf_ops test_driver_probe = { - .hid_rdesc_fixup = (void *)hid_test_driver_probe, -}; diff --git a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h index 1a645684a117..e5db897586bb 100644 --- a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h +++ b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h @@ -84,14 +84,10 @@ struct hid_bpf_ops { struct hid_device *hdev; }; -#define BIT(n) (1U << n) - #ifndef BPF_F_BEFORE -#define BPF_F_BEFORE BIT(3) +#define BPF_F_BEFORE (1U << 3) #endif -#define HID_QUIRK_IGNORE_SPECIAL_DRIVER BIT(22) - /* following are kfuncs exported by HID for HID-BPF */ extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, -- 2.50.1 From 43a43faf5376114161aa684834d24e06da596287 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 22 Nov 2024 11:18:25 -0800 Subject: [PATCH 16/16] futex: improve user space accesses Josh Poimboeuf reports that he got a "will-it-scale.per_process_ops 1.9% improvement" report for his patch that changed __get_user() to use pointer masking instead of the explicit speculation barrier. However, that patch doesn't actually work in the general case, because some (very bad) architecture-specific code actually depends on __get_user() also working on kernel addresses. A profile showed that the offending __get_user() was the futex code, which really should be fixed up to not use that horrid legacy case. Rewrite futex_get_value_locked() to use the modern user acccess helpers, and inline it so that the compiler not only avoids the function call for a few instructions, but can do CSE on the address masking. It also turns out the x86 futex functions have unnecessary barriers in other places, so let's fix those up too. Link: https://lore.kernel.org/all/20241115230653.hfvzyf3aqqntgp63@jpoimboe/ Reported-by: Josh Poimboeuf Signed-off-by: Linus Torvalds --- arch/x86/include/asm/futex.h | 8 +++-- kernel/futex/core.c | 22 -------------- kernel/futex/futex.h | 59 ++++++++++++++++++++++++++++++++++-- 3 files changed, 63 insertions(+), 26 deletions(-) diff --git a/arch/x86/include/asm/futex.h b/arch/x86/include/asm/futex.h index 99d345b686fa..6e2458088800 100644 --- a/arch/x86/include/asm/futex.h +++ b/arch/x86/include/asm/futex.h @@ -48,7 +48,9 @@ do { \ static __always_inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr) { - if (!user_access_begin(uaddr, sizeof(u32))) + if (can_do_masked_user_access()) + uaddr = masked_user_access_begin(uaddr); + else if (!user_access_begin(uaddr, sizeof(u32))) return -EFAULT; switch (op) { @@ -84,7 +86,9 @@ static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, { int ret = 0; - if (!user_access_begin(uaddr, sizeof(u32))) + if (can_do_masked_user_access()) + uaddr = masked_user_access_begin(uaddr); + else if (!user_access_begin(uaddr, sizeof(u32))) return -EFAULT; asm volatile("\n" "1:\t" LOCK_PREFIX "cmpxchgl %3, %2\n" diff --git a/kernel/futex/core.c b/kernel/futex/core.c index 6de57246760e..ebdd76b4ecbb 100644 --- a/kernel/futex/core.c +++ b/kernel/futex/core.c @@ -451,28 +451,6 @@ struct futex_q *futex_top_waiter(struct futex_hash_bucket *hb, union futex_key * return NULL; } -int futex_cmpxchg_value_locked(u32 *curval, u32 __user *uaddr, u32 uval, u32 newval) -{ - int ret; - - pagefault_disable(); - ret = futex_atomic_cmpxchg_inatomic(curval, uaddr, uval, newval); - pagefault_enable(); - - return ret; -} - -int futex_get_value_locked(u32 *dest, u32 __user *from) -{ - int ret; - - pagefault_disable(); - ret = __get_user(*dest, from); - pagefault_enable(); - - return ret ? -EFAULT : 0; -} - /** * wait_for_owner_exiting - Block until the owner has exited * @ret: owner's current futex lock status diff --git a/kernel/futex/futex.h b/kernel/futex/futex.h index 8b195d06f4e8..618ce1fe870e 100644 --- a/kernel/futex/futex.h +++ b/kernel/futex/futex.h @@ -6,6 +6,7 @@ #include #include #include +#include #ifdef CONFIG_PREEMPT_RT #include @@ -225,10 +226,64 @@ extern bool __futex_wake_mark(struct futex_q *q); extern void futex_wake_mark(struct wake_q_head *wake_q, struct futex_q *q); extern int fault_in_user_writeable(u32 __user *uaddr); -extern int futex_cmpxchg_value_locked(u32 *curval, u32 __user *uaddr, u32 uval, u32 newval); -extern int futex_get_value_locked(u32 *dest, u32 __user *from); extern struct futex_q *futex_top_waiter(struct futex_hash_bucket *hb, union futex_key *key); +static inline int futex_cmpxchg_value_locked(u32 *curval, u32 __user *uaddr, u32 uval, u32 newval) +{ + int ret; + + pagefault_disable(); + ret = futex_atomic_cmpxchg_inatomic(curval, uaddr, uval, newval); + pagefault_enable(); + + return ret; +} + +/* + * This does a plain atomic user space read, and the user pointer has + * already been verified earlier by get_futex_key() to be both aligned + * and actually in user space, just like futex_atomic_cmpxchg_inatomic(). + * + * We still want to avoid any speculation, and while __get_user() is + * the traditional model for this, it's actually slower than doing + * this manually these days. + * + * We could just have a per-architecture special function for it, + * the same way we do futex_atomic_cmpxchg_inatomic(), but rather + * than force everybody to do that, write it out long-hand using + * the low-level user-access infrastructure. + * + * This looks a bit overkill, but generally just results in a couple + * of instructions. + */ +static __always_inline int futex_read_inatomic(u32 *dest, u32 __user *from) +{ + u32 val; + + if (can_do_masked_user_access()) + from = masked_user_access_begin(from); + else if (!user_read_access_begin(from, sizeof(*from))) + return -EFAULT; + unsafe_get_user(val, from, Efault); + user_access_end(); + *dest = val; + return 0; +Efault: + user_access_end(); + return -EFAULT; +} + +static inline int futex_get_value_locked(u32 *dest, u32 __user *from) +{ + int ret; + + pagefault_disable(); + ret = futex_read_inatomic(dest, from); + pagefault_enable(); + + return ret; +} + extern void __futex_unqueue(struct futex_q *q); extern void __futex_queue(struct futex_q *q, struct futex_hash_bucket *hb); extern int futex_unqueue(struct futex_q *q); -- 2.50.1