lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <86ebd184-1f65-488a-8d5a-5d203be70097@arm.com>
Date: Fri, 10 Jan 2025 09:21:59 +0000
From: Ryan Roberts <ryan.roberts@....com>
To: Johannes Berg <johannes@...solutions.net>, linux-kernel@...r.kernel.org
Cc: Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
 "Rafael J. Wysocki" <rafael@...nel.org>,
 Johannes Berg <johannes.berg@...el.com>,
 Geert Uytterhoeven <geert@...ux-m68k.org>,
 Aishwarya TCV <Aishwarya.TCV@....com>, Mark Brown <broonie@...nel.org>
Subject: Re: [PATCH] fs: debugfs: differentiate short fops with proxy ops

Hi Johannes,

On 29/11/2024 12:15, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg@...el.com>
> 
> 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 <geert@...ux-m68k.org>
> 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 <johannes.berg@...el.com>
> ---
>  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 47dc96dfe386..bdb4f2ca0506 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;

I'm seeing a problem on arm64 where opening /sys/kernel/debug/gup_test has 
started failing with EINVAL after this patch is applied. Additionally this 
warning is triggering (see below).

gup_test is a debugfs file created with CONFIG_GUP_TEST and used by the mm 
selftest (tools/testing/selftests/mm/gup_test.c). As a result, the tests are now 
getting skipped. Everything works as expected without this patch.

I guess you're expecting to always take the "fsd = d_fsd;" path for 
mode=DBGFS_GET_ALREADY, which we are not doing for some reason in this case?

---8<---
[   34.393031] ------------[ cut here ]------------
[   34.393036] WARNING: CPU: 2 PID: 1408 at fs/debugfs/file.c:90 __debugfs_file_get+0x1d0/0x1f8
[   34.393044] Modules linked in:
[   34.393047] CPU: 2 UID: 0 PID: 1408 Comm: gup_test Not tainted 6.13.0-rc3-00001-gf8f25893a477 #61
[   34.393049] Hardware name: linux,dummy-virt (DT)
[   34.393050] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[   34.393052] pc : __debugfs_file_get+0x1d0/0x1f8
[   34.393055] lr : open_proxy_open+0x38/0x130
[   34.393057] sp : ffff80008d5b3ac0
[   34.393058] x29: ffff80008d5b3ac0 x28: ffff80008d5b3c78 x27: 0000000000000000
[   34.393061] x26: 0000000000000006 x25: ffff800083354cf8 x24: 0000000000000000
[   34.393064] x23: ffff000141390000 x22: 0000000000000000 x21: ffff800081670229
[   34.393067] x20: ffff00018b35ce40 x19: ffff0001411823c0 x18: 0000000000000000
[   34.393069] x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000
[   34.393071] x14: ffffffffffffffff x13: ffff0001424cd021 x12: ffff80008d5b3cbc
[   34.393074] x11: 00000003c3a7f0df x10: 00007ffeff8cd810 x9 : ffff80008071b258
[   34.393077] x8 : d0d0d0d0d0d0d0d0 x7 : 808080808080807f x6 : 0000000000000000
[   34.393079] x5 : 0000000000000064 x4 : ffff000141390158 x3 : 0000000000000001
[   34.393082] x2 : 0000000000000000 x1 : 0000000000000000 x0 : 0000000000400000
[   34.393085] Call trace:
[   34.393086]  __debugfs_file_get+0x1d0/0x1f8 (P)
[   34.393089]  open_proxy_open+0x38/0x130
[   34.393092]  do_dentry_open+0x22c/0x690
[   34.393095]  vfs_open+0x34/0xf8
[   34.393097]  path_openat+0x2b8/0xe58
[   34.393101]  do_filp_open+0x94/0x158
[   34.393103]  do_sys_openat2+0xbc/0xf0
[   34.393106]  __arm64_sys_openat+0x68/0xb8
[   34.393108]  invoke_syscall+0x50/0x120
[   34.393111]  el0_svc_common.constprop.0+0x48/0xf8
[   34.393114]  do_el0_svc+0x28/0x40
[   34.393117]  el0_svc+0x30/0xd0
[   34.393120]  el0t_64_sync_handler+0x144/0x168
[   34.393122]  el0t_64_sync+0x198/0x1a0
[   34.393124] ---[ end trace 0000000000000000 ]---
---8<---

Thanks,
Ryan


> +
>  		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 38a9c7eb97e6..65e46c7b6bf1 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 a3edfa4f0d8e..bbae4a228ef4 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)


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ