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