[PATCH 2/2] ubifs: tnc_delete: Don't update collapsed znodes if memory allocation fails

Zhihao Cheng chengzhihao1 at huawei.com
Tue Oct 13 23:02:52 PDT 2026


A collapsed znode is updated with 'znode->parent = NULL' before calling
insert_old_idx(), if a memory allocation failure happens, there will
exists non-root znode(without parent) in TNC, which leads a null-ptr-def
problem for TNC traversing, the details shown as:

 file: consists of many data nodes, TNC is:
                   znode_A (lv 2)
  	     /                         \
       znode_B (lvl 1)              znode (lv 1)
        /             \                 |
    znode_C(lv 0)...znode(lv 0)     znode (lv 0)
     |   |     |     |       |          |
  ino_1 ino_f dent dn_0 ... dn_i      dn_i+1

     P1
  truncate(file, 0)
   ubifs_setattr
    ubifs_jnl_truncate
     ubifs_tnc_remove_range
      tnc_delete // dn_* are removed
       do-while

  the TNC looks like:
          znode_A (lv 2, parent = NULL)
		|
	  znode_B (lvl 1, parent = znode_A)
		|
	  znode_C (lv 0, parent = znode_B)
	  |     |     |
	ino_1 ino_f dent

  tnc_delete will collapse the tree:
  // while (znode->child_cnt == 1 && znode->level != 0)
   // round 1
   zp = znode_A
   znode_B = get_znode(c, znode_A, 0)
   znode_B->parent = NULL
   znode_B->iip = 0
   insert_old_idx
   c->zroot = znode_B
   kfree(znode_A)
   // round 2
   zp = znode_B
   znode_C = get_znode(c, znode_B, 0)
   znode_C->parent = NULL
   znode_C->iip = 0
   ENOMEM = insert_old_idx
    return
   c->zroot = znode_C // skip

  the TNC looks like:
          znode_B (lv 1, parent = NULL)
		|
	  znode_C (lv 0, parent = NULL)
	  |     |     |
	ino_1 ino_f dent

      P2
  umount
   kill_ubifs_super
    ubifs_put_super
     ubifs_umount
      destroy_journal
       ubifs_tnc_close
        ubifs_destroy_tnc_tree
	 ubifs_destroy_tnc_subtree
	  znode_C = ubifs_tnc_postorder_first
	  if (zn == znode) // false, zn = znode_C, znode = znode_B
	  zn = ubifs_tnc_postorder_next(c, znode_C) // zn = NULL
	  for (n = 0; n < zn->child_cnt; n++) // null-ptr-def !

The root cause is that zroot & znode->parent updating is not atomic,
which could be interrupted by the insert_old_idx() failure, fix it by
making zroot & znode->parent updating together.

Reproducer is https://bugzilla.kernel.org/show_bug.cgi?id=222130.

Fixes: 1e51764a3c2a ("UBIFS: add new flash file system")
Signed-off-by: Zhihao Cheng <chengzhihao1 at huawei.com>
---
 fs/ubifs/tnc.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c
index 679afddadf82..c9480d963e4c 100644
--- a/fs/ubifs/tnc.c
+++ b/fs/ubifs/tnc.c
@@ -2611,14 +2611,14 @@ static int tnc_delete(struct ubifs_info *c, struct ubifs_znode *znode, int n)
 			znode = dirty_cow_znode(c, zbr);
 			if (IS_ERR(znode))
 				return PTR_ERR(znode);
-			znode->parent = NULL;
-			znode->iip = 0;
 			if (c->zroot.len) {
 				err = insert_old_idx(c, c->zroot.lnum,
 						     c->zroot.offs);
 				if (err)
 					return err;
 			}
+			znode->parent = NULL;
+			znode->iip = 0;
 			c->zroot.lnum = zbr->lnum;
 			c->zroot.offs = zbr->offs;
 			c->zroot.len = zbr->len;
-- 
2.52.0




More information about the linux-mtd mailing list