[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