From f8f25893a477a4da4414c3e40ddd51d77fac9cfc Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 29 Nov 2024 13:15:37 +0100 Subject: [PATCH 1/5] fs: debugfs: differentiate short fops with proxy ops Geert reported that my previous short fops debugfs changes broke m68k, because it only has mandatory alignement of two, so we can't stash the "is it short" information into the pointer (as we already did with the "is it real" bit.) Instead, exploit the fact that debugfs_file_get() called on an already open file will already find that the fsdata is no longer the real fops but rather the allocated data that already distinguishes full/short ops, so only open() needs to be able to distinguish. We can achieve that by using two different open functions. Unfortunately this requires another set of full file ops, increasing the size by 536 bytes (x86-64), but that's still a reasonable trade-off given that only converting some of the wireless stack gained over 28k. This brings the total cost of this to around 1k, for wins of 28k (all x86-64). Reported-and-tested-by: Geert Uytterhoeven Link: https://lore.kernel.org/CAMuHMdWu_9-L2Te101w8hU7H_2yobJFPXSwwUmGHSJfaPWDKiQ@mail.gmail.com Fixes: 8dc6d81c6b2a ("debugfs: add small file operations for most files") Signed-off-by: Johannes Berg Tested-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20241129121536.30989-2-johannes@sipsolutions.net Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/file.c | 72 ++++++++++++++++++++++++++++++------------- fs/debugfs/inode.c | 11 +++---- fs/debugfs/internal.h | 6 +--- 3 files changed, 55 insertions(+), 34 deletions(-) diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 47dc96dfe386b..bdb4f2ca05060 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -64,22 +64,13 @@ const struct file_operations *debugfs_real_fops(const struct file *filp) } EXPORT_SYMBOL_GPL(debugfs_real_fops); -/** - * debugfs_file_get - mark the beginning of file data access - * @dentry: the dentry object whose data is being accessed. - * - * Up to a matching call to debugfs_file_put(), any successive call - * into the file removing functions debugfs_remove() and - * debugfs_remove_recursive() will block. Since associated private - * file data may only get freed after a successful return of any of - * the removal functions, you may safely access it after a successful - * call to debugfs_file_get() without worrying about lifetime issues. - * - * If -%EIO is returned, the file has already been removed and thus, - * it is not safe to access any of its data. If, on the other hand, - * it is allowed to access the file data, zero is returned. - */ -int debugfs_file_get(struct dentry *dentry) +enum dbgfs_get_mode { + DBGFS_GET_ALREADY, + DBGFS_GET_REGULAR, + DBGFS_GET_SHORT, +}; + +static int __debugfs_file_get(struct dentry *dentry, enum dbgfs_get_mode mode) { struct debugfs_fsdata *fsd; void *d_fsd; @@ -96,15 +87,17 @@ int debugfs_file_get(struct dentry *dentry) if (!((unsigned long)d_fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)) { fsd = d_fsd; } else { + if (WARN_ON(mode == DBGFS_GET_ALREADY)) + return -EINVAL; + fsd = kmalloc(sizeof(*fsd), GFP_KERNEL); if (!fsd) return -ENOMEM; - if ((unsigned long)d_fsd & DEBUGFS_FSDATA_IS_SHORT_FOPS_BIT) { + if (mode == DBGFS_GET_SHORT) { fsd->real_fops = NULL; fsd->short_fops = (void *)((unsigned long)d_fsd & - ~(DEBUGFS_FSDATA_IS_REAL_FOPS_BIT | - DEBUGFS_FSDATA_IS_SHORT_FOPS_BIT)); + ~DEBUGFS_FSDATA_IS_REAL_FOPS_BIT); } else { fsd->real_fops = (void *)((unsigned long)d_fsd & ~DEBUGFS_FSDATA_IS_REAL_FOPS_BIT); @@ -138,6 +131,26 @@ int debugfs_file_get(struct dentry *dentry) return 0; } + +/** + * debugfs_file_get - mark the beginning of file data access + * @dentry: the dentry object whose data is being accessed. + * + * Up to a matching call to debugfs_file_put(), any successive call + * into the file removing functions debugfs_remove() and + * debugfs_remove_recursive() will block. Since associated private + * file data may only get freed after a successful return of any of + * the removal functions, you may safely access it after a successful + * call to debugfs_file_get() without worrying about lifetime issues. + * + * If -%EIO is returned, the file has already been removed and thus, + * it is not safe to access any of its data. If, on the other hand, + * it is allowed to access the file data, zero is returned. + */ +int debugfs_file_get(struct dentry *dentry) +{ + return __debugfs_file_get(dentry, DBGFS_GET_ALREADY); +} EXPORT_SYMBOL_GPL(debugfs_file_get); /** @@ -424,7 +437,8 @@ static void __full_proxy_fops_init(struct file_operations *proxy_fops, proxy_fops->unlocked_ioctl = full_proxy_unlocked_ioctl; } -static int full_proxy_open(struct inode *inode, struct file *filp) +static int full_proxy_open(struct inode *inode, struct file *filp, + enum dbgfs_get_mode mode) { struct dentry *dentry = F_DENTRY(filp); const struct file_operations *real_fops; @@ -432,7 +446,7 @@ static int full_proxy_open(struct inode *inode, struct file *filp) struct debugfs_fsdata *fsd; int r; - r = debugfs_file_get(dentry); + r = __debugfs_file_get(dentry, mode); if (r) return r == -EIO ? -ENOENT : r; @@ -491,8 +505,22 @@ static int full_proxy_open(struct inode *inode, struct file *filp) return r; } +static int full_proxy_open_regular(struct inode *inode, struct file *filp) +{ + return full_proxy_open(inode, filp, DBGFS_GET_REGULAR); +} + const struct file_operations debugfs_full_proxy_file_operations = { - .open = full_proxy_open, + .open = full_proxy_open_regular, +}; + +static int full_proxy_open_short(struct inode *inode, struct file *filp) +{ + return full_proxy_open(inode, filp, DBGFS_GET_SHORT); +} + +const struct file_operations debugfs_full_short_proxy_file_operations = { + .open = full_proxy_open_short, }; ssize_t debugfs_attr_read(struct file *file, char __user *buf, diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 38a9c7eb97e60..65e46c7b6bf19 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -455,8 +455,7 @@ struct dentry *debugfs_create_file_full(const char *name, umode_t mode, const struct file_operations *fops) { if (WARN_ON((unsigned long)fops & - (DEBUGFS_FSDATA_IS_SHORT_FOPS_BIT | - DEBUGFS_FSDATA_IS_REAL_FOPS_BIT))) + DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)) return ERR_PTR(-EINVAL); return __debugfs_create_file(name, mode, parent, data, @@ -471,15 +470,13 @@ struct dentry *debugfs_create_file_short(const char *name, umode_t mode, const struct debugfs_short_fops *fops) { if (WARN_ON((unsigned long)fops & - (DEBUGFS_FSDATA_IS_SHORT_FOPS_BIT | - DEBUGFS_FSDATA_IS_REAL_FOPS_BIT))) + DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)) return ERR_PTR(-EINVAL); return __debugfs_create_file(name, mode, parent, data, - fops ? &debugfs_full_proxy_file_operations : + fops ? &debugfs_full_short_proxy_file_operations : &debugfs_noop_file_operations, - (const void *)((unsigned long)fops | - DEBUGFS_FSDATA_IS_SHORT_FOPS_BIT)); + fops); } EXPORT_SYMBOL_GPL(debugfs_create_file_short); diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h index a3edfa4f0d8e6..bbae4a228ef4e 100644 --- a/fs/debugfs/internal.h +++ b/fs/debugfs/internal.h @@ -15,6 +15,7 @@ struct file_operations; extern const struct file_operations debugfs_noop_file_operations; extern const struct file_operations debugfs_open_proxy_file_operations; extern const struct file_operations debugfs_full_proxy_file_operations; +extern const struct file_operations debugfs_full_short_proxy_file_operations; struct debugfs_fsdata { const struct file_operations *real_fops; @@ -40,11 +41,6 @@ struct debugfs_fsdata { * pointer gets its lowest bit set. */ #define DEBUGFS_FSDATA_IS_REAL_FOPS_BIT BIT(0) -/* - * A dentry's ->d_fsdata, when pointing to real fops, is with - * short fops instead of full fops. - */ -#define DEBUGFS_FSDATA_IS_SHORT_FOPS_BIT BIT(1) /* Access BITS */ #define DEBUGFS_ALLOW_API BIT(0) From 24edfbdedf19998366205130cfc93158a475497e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 29 Dec 2024 08:12:04 +0000 Subject: [PATCH 2/5] debugfs: fix missing mutex_destroy() in short_fops case we need that in ->real_fops == NULL, ->short_fops != NULL case Fixes: 8dc6d81c6b2a "debugfs: add small file operations for most files" Signed-off-by: Al Viro Link: https://lore.kernel.org/r/20241229081223.3193228-1-viro@zeniv.linux.org.uk Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 65e46c7b6bf19..e752009de9292 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -229,7 +229,7 @@ static void debugfs_release_dentry(struct dentry *dentry) return; /* check it wasn't a dir (no fsdata) or automount (no real_fops) */ - if (fsd && fsd->real_fops) { + if (fsd && (fsd->real_fops || fsd->short_fops)) { WARN_ON(!list_empty(&fsd->cancellations)); mutex_destroy(&fsd->cancellations_mtx); } From cbd399f78e23ad4492c174fc5e6b3676dba74a52 Mon Sep 17 00:00:00 2001 From: Li Huafei Date: Thu, 14 Nov 2024 19:01:41 +0800 Subject: [PATCH 3/5] topology: Keep the cpumask unchanged when printing cpumap During fuzz testing, the following warning was discovered: different return values (15 and 11) from vsnprintf("%*pbl ", ...) test:keyward is WARNING in kvasprintf WARNING: CPU: 55 PID: 1168477 at lib/kasprintf.c:30 kvasprintf+0x121/0x130 Call Trace: kvasprintf+0x121/0x130 kasprintf+0xa6/0xe0 bitmap_print_to_buf+0x89/0x100 core_siblings_list_read+0x7e/0xb0 kernfs_file_read_iter+0x15b/0x270 new_sync_read+0x153/0x260 vfs_read+0x215/0x290 ksys_read+0xb9/0x160 do_syscall_64+0x56/0x100 entry_SYSCALL_64_after_hwframe+0x78/0xe2 The call trace shows that kvasprintf() reported this warning during the printing of core_siblings_list. kvasprintf() has several steps: (1) First, calculate the length of the resulting formatted string. (2) Allocate a buffer based on the returned length. (3) Then, perform the actual string formatting. (4) Check whether the lengths of the formatted strings returned in steps (1) and (2) are consistent. If the core_cpumask is modified between steps (1) and (3), the lengths obtained in these two steps may not match. Indeed our test includes cpu hotplugging, which should modify core_cpumask while printing. To fix this issue, cache the cpumask into a temporary variable before calling cpumap_print_{list, cpumask}_to_buf(), to keep it unchanged during the printing process. Fixes: bb9ec13d156e ("topology: use bin_attribute to break the size limitation of cpumap ABI") Cc: stable Signed-off-by: Li Huafei Reviewed-by: Jonathan Cameron Link: https://lore.kernel.org/r/20241114110141.94725-1-lihuafei1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/topology.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/base/topology.c b/drivers/base/topology.c index cf160dd2c27bd..b962da263eeef 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -27,9 +27,17 @@ static ssize_t name##_read(struct file *file, struct kobject *kobj, \ loff_t off, size_t count) \ { \ struct device *dev = kobj_to_dev(kobj); \ + cpumask_var_t mask; \ + ssize_t n; \ \ - return cpumap_print_bitmask_to_buf(buf, topology_##mask(dev->id), \ - off, count); \ + if (!alloc_cpumask_var(&mask, GFP_KERNEL)) \ + return -ENOMEM; \ + \ + cpumask_copy(mask, topology_##mask(dev->id)); \ + n = cpumap_print_bitmask_to_buf(buf, mask, off, count); \ + free_cpumask_var(mask); \ + \ + return n; \ } \ \ static ssize_t name##_list_read(struct file *file, struct kobject *kobj, \ @@ -37,9 +45,17 @@ static ssize_t name##_list_read(struct file *file, struct kobject *kobj, \ loff_t off, size_t count) \ { \ struct device *dev = kobj_to_dev(kobj); \ + cpumask_var_t mask; \ + ssize_t n; \ + \ + if (!alloc_cpumask_var(&mask, GFP_KERNEL)) \ + return -ENOMEM; \ + \ + cpumask_copy(mask, topology_##mask(dev->id)); \ + n = cpumap_print_list_to_buf(buf, mask, off, count); \ + free_cpumask_var(mask); \ \ - return cpumap_print_list_to_buf(buf, topology_##mask(dev->id), \ - off, count); \ + return n; \ } define_id_show_func(physical_package_id, "%d"); From b4aee757f1baf20fa2650fc23a7b0335696e005c Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 4 Dec 2024 16:22:47 +0100 Subject: [PATCH 4/5] MAINTAINERS: align Danilo's maintainer entries Some entries use my kernel.org address, while others use my Red Hat one. Since this is a bit of an inconvinience for me, align them to all use the same (kernel.org) address. Signed-off-by: Danilo Krummrich Link: https://lore.kernel.org/r/20241204152248.8644-1-dakr@kernel.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index baf0eeb9a3554..03a21163d9528 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7347,7 +7347,7 @@ F: drivers/gpu/drm/panel/panel-novatek-nt36672a.c DRM DRIVER FOR NVIDIA GEFORCE/QUADRO GPUS M: Karol Herbst M: Lyude Paul -M: Danilo Krummrich +M: Danilo Krummrich L: dri-devel@lists.freedesktop.org L: nouveau@lists.freedesktop.org S: Supported @@ -8924,7 +8924,7 @@ F: include/linux/arm_ffa.h FIRMWARE LOADER (request_firmware) M: Luis Chamberlain M: Russ Weight -M: Danilo Krummrich +M: Danilo Krummrich L: linux-kernel@vger.kernel.org S: Maintained F: Documentation/firmware_class/ From 67510d7e2e5f5bdc020bf9d759aa575cce48c8e1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 10 Jan 2025 08:58:14 +0100 Subject: [PATCH 5/5] fs: debugfs: fix open proxy for unsafe files In the previous commit referenced below, I had to split the short fops handling into different proxy fops. This necessitated knowing out-of-band whether or not the ops are short or full, when attempting to convert from fops to allocated fsdata. Unfortunately, I only converted full_proxy_open() which is used for the new full_proxy_open_regular() and full_proxy_open_short(), but forgot about the call in open_proxy_open(), used for debugfs_create_file_unsafe(). Fix that, it never has short fops. Fixes: f8f25893a477 ("fs: debugfs: differentiate short fops with proxy ops") Reported-by: Suresh Kumar Kurmi Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202501101055.bb8bf3e7-lkp@intel.com Reported-by: Venkat Rao Bagalkote Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20250110085826.cd74f3b7a36b.I430c79c82ec3f954c2ff9665753bf6ac9e63eef8@changeid Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index bdb4f2ca05060..16e198a263398 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -280,7 +280,7 @@ static int open_proxy_open(struct inode *inode, struct file *filp) const struct file_operations *real_fops = NULL; int r; - r = debugfs_file_get(dentry); + r = __debugfs_file_get(dentry, DBGFS_GET_REGULAR); if (r) return r == -EIO ? -ENOENT : r;