AW: [PATCH] jffs: initialize inocache to NULL when initializing a jffs2_inode_info

From: Richard Weinberger

Date: Wed Sep 09 2026 - 01:57:22 EST


Lucas,

Am Wed, Sep 9, 2026, 04:54 schrieb sashiko-bot@xxxxxxxxxx:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>
> Pre-existing issues:
> - [Critical] Double free of `f->target` on early VFS failure paths due to uninitialized slab memory.
> --
>
> commit 9fd2012cfec1b8d15df193fea97deabda99aa5e5
> Author: Lucas Jeffrey <luquijeffrey@xxxxxxxxx>
>
> jffs: initialize inocache to NULL when initializing a jffs2_inode_info
>
> When a jffs2_inode_info is allocated via new_inode(), due to memory reuse it
> can retain stale values from its previous use.
>
> If the new jffs2_inode_info object allocated retains an old reference to a
> freed inocache, the function jffs2_new_inode may crash if either
> jffs2_init_acl_pre or jffs2_do_new_inode returns an error value because
> when calling iput() with the inode it will eventually attempt to free again
> the inocache.
>
> Reported-by: syzbot+3a8099322b09d8d073d1@xxxxxxxxxxxxxxxxxxxxxxxxx
> Closes: https://syzkaller.appspot.com/bug?extid=3a8099322b09d8d073d1
> Signed-off-by: Lucas Jeffrey <luquijeffrey@xxxxxxxxx>
>
> > diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h
> > index 86ab014a349c5..40f17218a276b 100644
> > --- a/fs/jffs2/os-linux.h
> > +++ b/fs/jffs2/os-linux.h
> > @@ -52,6 +52,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
> > {
> > f->highest_version = 0;
> > f->fragtree = RB_ROOT;
> > + f->inocache = NULL;
> > f->metadata = NULL;
> > f->dents = NULL;
> > f->target = NULL;
>
> [Severity: Critical]
> This isn't a bug introduced by this patch, but doesn't f->target suffer from
> a similar issue due to uninitialized slab memory on early VFS failure paths?
>
> When the VFS allocates a new inode via jffs2_alloc_inode() in
> fs/jffs2/super.c, the object is returned from the SLAB cache without
> __GFP_ZERO. It retains stale values from its previous lifecycle, including
> a dangling pointer in f->target, because jffs2_free_inode() frees f->target
> but does not nullify it.
>
> JFFS2 defers initializing f->target = NULL to jffs2_init_inode_info(), which
> is called after VFS initialization completes. If the VFS fails early (e.g.,
> inode_init_always() fails due to an LSM memory allocation, or iget_locked()
> in fs/inode.c encounters a hash collision race and drops the newly allocated
> inode), the VFS destroys the inode via jffs2_free_inode() before JFFS2 ever
> initializes it.
>
> This causes a double free when kfree(f->target) executes again on the
> dangling pointer:
>
> fs/jffs2/super.c:jffs2_free_inode() {
> ...
> kfree(f->target);
> kmem_cache_free(jffs2_inode_cachep, f);
> }
>
> Userspace can reliably trigger the iget_locked() hash collision race by
> performing concurrent lookups (e.g., stat() or open()) on the same
> non-resident inode:
>
> fs/inode.c:iget_locked() {
> ...
> if (unlikely(test_bit(__I_NEW, &inode->i_state))) {
> /* Uhhuh, somebody else created the same inode under us... */
> spin_unlock(&inode_hash_lock);
> destroy_inode(inode);
> ...
> }
>
> Would it be safer to zero these fields in jffs2_alloc_inode() or ensure
> jffs2_free_inode() nullifies them before returning the object to the slab
> cache?

I think this is a legit bug pointed out by Sashiko.
Do you want to double check and (if needed) fix it?

Thanks,
//richard