Re: [PATCH] Revert "libfs: Use d_children list to iterate simple_offset directories"

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 2/26/25 9:29 AM, Greg Kroah-Hartman wrote:
> This reverts commit b9b588f22a0c049a14885399e27625635ae6ef91.
> 
> There are reports of this commit breaking Chrome's rendering mode.  As
> no one seems to want to do a root-cause, let's just revert it for now as
> it is affecting people using the latest release as well as the stable
> kernels that it has been backported to.

NACK. This re-introduces a CVE.

The problem is we don't have a reproducer yet.


> Link: https://lore.kernel.org/r/874j0lvy89.wl-tiwai@xxxxxxx
> Fixes: b9b588f22a0c ("libfs: Use d_children list to iterate simple_offset directories")
> Cc: stable <stable@xxxxxxxxxx>
> Reported-by: Takashi Iwai <tiwai@xxxxxxx>
> Cc: Chuck Lever <chuck.lever@xxxxxxxxxx>
> Cc: Christian Brauner <brauner@xxxxxxxxxx>
> Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
> ---
>  fs/libfs.c | 90 ++++++++++++++++++------------------------------------
>  1 file changed, 29 insertions(+), 61 deletions(-)
> 
> diff --git a/fs/libfs.c b/fs/libfs.c
> index 8444f5cc4064..96f491f82f99 100644
> --- a/fs/libfs.c
> +++ b/fs/libfs.c
> @@ -247,13 +247,12 @@ EXPORT_SYMBOL(simple_dir_inode_operations);
>  
>  /* simple_offset_add() never assigns these to a dentry */
>  enum {
> -	DIR_OFFSET_FIRST	= 2,		/* Find first real entry */
>  	DIR_OFFSET_EOD		= S32_MAX,
>  };
>  
>  /* simple_offset_add() allocation range */
>  enum {
> -	DIR_OFFSET_MIN		= DIR_OFFSET_FIRST + 1,
> +	DIR_OFFSET_MIN		= 2,
>  	DIR_OFFSET_MAX		= DIR_OFFSET_EOD - 1,
>  };
>  
> @@ -458,82 +457,51 @@ static loff_t offset_dir_llseek(struct file *file, loff_t offset, int whence)
>  	return vfs_setpos(file, offset, LONG_MAX);
>  }
>  
> -static struct dentry *find_positive_dentry(struct dentry *parent,
> -					   struct dentry *dentry,
> -					   bool next)
> +static struct dentry *offset_find_next(struct offset_ctx *octx, loff_t offset)
>  {
> -	struct dentry *found = NULL;
> -
> -	spin_lock(&parent->d_lock);
> -	if (next)
> -		dentry = d_next_sibling(dentry);
> -	else if (!dentry)
> -		dentry = d_first_child(parent);
> -	hlist_for_each_entry_from(dentry, d_sib) {
> -		if (!simple_positive(dentry))
> -			continue;
> -		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
> -		if (simple_positive(dentry))
> -			found = dget_dlock(dentry);
> -		spin_unlock(&dentry->d_lock);
> -		if (likely(found))
> -			break;
> -	}
> -	spin_unlock(&parent->d_lock);
> -	return found;
> -}
> -
> -static noinline_for_stack struct dentry *
> -offset_dir_lookup(struct dentry *parent, loff_t offset)
> -{
> -	struct inode *inode = d_inode(parent);
> -	struct offset_ctx *octx = inode->i_op->get_offset_ctx(inode);
> +	MA_STATE(mas, &octx->mt, offset, offset);
>  	struct dentry *child, *found = NULL;
>  
> -	MA_STATE(mas, &octx->mt, offset, offset);
> -
> -	if (offset == DIR_OFFSET_FIRST)
> -		found = find_positive_dentry(parent, NULL, false);
> -	else {
> -		rcu_read_lock();
> -		child = mas_find(&mas, DIR_OFFSET_MAX);
> -		found = find_positive_dentry(parent, child, false);
> -		rcu_read_unlock();
> -	}
> +	rcu_read_lock();
> +	child = mas_find(&mas, DIR_OFFSET_MAX);
> +	if (!child)
> +		goto out;
> +	spin_lock(&child->d_lock);
> +	if (simple_positive(child))
> +		found = dget_dlock(child);
> +	spin_unlock(&child->d_lock);
> +out:
> +	rcu_read_unlock();
>  	return found;
>  }
>  
>  static bool offset_dir_emit(struct dir_context *ctx, struct dentry *dentry)
>  {
>  	struct inode *inode = d_inode(dentry);
> +	long offset = dentry2offset(dentry);
>  
> -	return dir_emit(ctx, dentry->d_name.name, dentry->d_name.len,
> -			inode->i_ino, fs_umode_to_dtype(inode->i_mode));
> +	return ctx->actor(ctx, dentry->d_name.name, dentry->d_name.len, offset,
> +			  inode->i_ino, fs_umode_to_dtype(inode->i_mode));
>  }
>  
> -static void offset_iterate_dir(struct file *file, struct dir_context *ctx)
> +static void offset_iterate_dir(struct inode *inode, struct dir_context *ctx)
>  {
> -	struct dentry *dir = file->f_path.dentry;
> +	struct offset_ctx *octx = inode->i_op->get_offset_ctx(inode);
>  	struct dentry *dentry;
>  
> -	dentry = offset_dir_lookup(dir, ctx->pos);
> -	if (!dentry)
> -		goto out_eod;
>  	while (true) {
> -		struct dentry *next;
> -
> -		ctx->pos = dentry2offset(dentry);
> -		if (!offset_dir_emit(ctx, dentry))
> -			break;
> -
> -		next = find_positive_dentry(dir, dentry, true);
> -		dput(dentry);
> -
> -		if (!next)
> +		dentry = offset_find_next(octx, ctx->pos);
> +		if (!dentry)
>  			goto out_eod;
> -		dentry = next;
> +
> +		if (!offset_dir_emit(ctx, dentry)) {
> +			dput(dentry);
> +			break;
> +		}
> +
> +		ctx->pos = dentry2offset(dentry) + 1;
> +		dput(dentry);
>  	}
> -	dput(dentry);
>  	return;
>  
>  out_eod:
> @@ -572,7 +540,7 @@ static int offset_readdir(struct file *file, struct dir_context *ctx)
>  	if (!dir_emit_dots(file, ctx))
>  		return 0;
>  	if (ctx->pos != DIR_OFFSET_EOD)
> -		offset_iterate_dir(file, ctx);
> +		offset_iterate_dir(d_inode(dir), ctx);
>  	return 0;
>  }
>  


-- 
Chuck Lever




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux