Skip to content

Commit b5fda08

Browse files
Zhihao Chengrichardweinberger
authored andcommitted
ubifs: Fix memleak when insert_old_idx() failed
Following process will cause a memleak for copied up znode: dirty_cow_znode zn = copy_znode(c, znode); err = insert_old_idx(c, zbr->lnum, zbr->offs); if (unlikely(err)) return ERR_PTR(err); // No one refers to zn. Fetch a reproducer in [Link]. Function copy_znode() is split into 2 parts: resource allocation and znode replacement, insert_old_idx() is split in similar way, so resource cleanup could be done in error handling path without corrupting metadata(mem & disk). It's okay that old index inserting is put behind of add_idx_dirt(), old index is used in layout_leb_in_gaps(), so the two processes do not depend on each other. Link: https://bugzilla.kernel.org/show_bug.cgi?id=216705 Fixes: 1e51764 ("UBIFS: add new flash file system") Cc: stable@vger.kernel.org Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com> Signed-off-by: Richard Weinberger <richard@nod.at>
1 parent 7d01cb2 commit b5fda08

1 file changed

Lines changed: 87 additions & 50 deletions

File tree

fs/ubifs/tnc.c

Lines changed: 87 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,33 @@ enum {
4444
NOT_ON_MEDIA = 3,
4545
};
4646

47+
static void do_insert_old_idx(struct ubifs_info *c,
48+
struct ubifs_old_idx *old_idx)
49+
{
50+
struct ubifs_old_idx *o;
51+
struct rb_node **p, *parent = NULL;
52+
53+
p = &c->old_idx.rb_node;
54+
while (*p) {
55+
parent = *p;
56+
o = rb_entry(parent, struct ubifs_old_idx, rb);
57+
if (old_idx->lnum < o->lnum)
58+
p = &(*p)->rb_left;
59+
else if (old_idx->lnum > o->lnum)
60+
p = &(*p)->rb_right;
61+
else if (old_idx->offs < o->offs)
62+
p = &(*p)->rb_left;
63+
else if (old_idx->offs > o->offs)
64+
p = &(*p)->rb_right;
65+
else {
66+
ubifs_err(c, "old idx added twice!");
67+
kfree(old_idx);
68+
}
69+
}
70+
rb_link_node(&old_idx->rb, parent, p);
71+
rb_insert_color(&old_idx->rb, &c->old_idx);
72+
}
73+
4774
/**
4875
* insert_old_idx - record an index node obsoleted since the last commit start.
4976
* @c: UBIFS file-system description object
@@ -69,35 +96,15 @@ enum {
6996
*/
7097
static int insert_old_idx(struct ubifs_info *c, int lnum, int offs)
7198
{
72-
struct ubifs_old_idx *old_idx, *o;
73-
struct rb_node **p, *parent = NULL;
99+
struct ubifs_old_idx *old_idx;
74100

75101
old_idx = kmalloc(sizeof(struct ubifs_old_idx), GFP_NOFS);
76102
if (unlikely(!old_idx))
77103
return -ENOMEM;
78104
old_idx->lnum = lnum;
79105
old_idx->offs = offs;
106+
do_insert_old_idx(c, old_idx);
80107

81-
p = &c->old_idx.rb_node;
82-
while (*p) {
83-
parent = *p;
84-
o = rb_entry(parent, struct ubifs_old_idx, rb);
85-
if (lnum < o->lnum)
86-
p = &(*p)->rb_left;
87-
else if (lnum > o->lnum)
88-
p = &(*p)->rb_right;
89-
else if (offs < o->offs)
90-
p = &(*p)->rb_left;
91-
else if (offs > o->offs)
92-
p = &(*p)->rb_right;
93-
else {
94-
ubifs_err(c, "old idx added twice!");
95-
kfree(old_idx);
96-
return 0;
97-
}
98-
}
99-
rb_link_node(&old_idx->rb, parent, p);
100-
rb_insert_color(&old_idx->rb, &c->old_idx);
101108
return 0;
102109
}
103110

@@ -199,23 +206,6 @@ static struct ubifs_znode *copy_znode(struct ubifs_info *c,
199206
__set_bit(DIRTY_ZNODE, &zn->flags);
200207
__clear_bit(COW_ZNODE, &zn->flags);
201208

202-
ubifs_assert(c, !ubifs_zn_obsolete(znode));
203-
__set_bit(OBSOLETE_ZNODE, &znode->flags);
204-
205-
if (znode->level != 0) {
206-
int i;
207-
const int n = zn->child_cnt;
208-
209-
/* The children now have new parent */
210-
for (i = 0; i < n; i++) {
211-
struct ubifs_zbranch *zbr = &zn->zbranch[i];
212-
213-
if (zbr->znode)
214-
zbr->znode->parent = zn;
215-
}
216-
}
217-
218-
atomic_long_inc(&c->dirty_zn_cnt);
219209
return zn;
220210
}
221211

@@ -233,6 +223,42 @@ static int add_idx_dirt(struct ubifs_info *c, int lnum, int dirt)
233223
return ubifs_add_dirt(c, lnum, dirt);
234224
}
235225

226+
/**
227+
* replace_znode - replace old znode with new znode.
228+
* @c: UBIFS file-system description object
229+
* @new_zn: new znode
230+
* @old_zn: old znode
231+
* @zbr: the branch of parent znode
232+
*
233+
* Replace old znode with new znode in TNC.
234+
*/
235+
static void replace_znode(struct ubifs_info *c, struct ubifs_znode *new_zn,
236+
struct ubifs_znode *old_zn, struct ubifs_zbranch *zbr)
237+
{
238+
ubifs_assert(c, !ubifs_zn_obsolete(old_zn));
239+
__set_bit(OBSOLETE_ZNODE, &old_zn->flags);
240+
241+
if (old_zn->level != 0) {
242+
int i;
243+
const int n = new_zn->child_cnt;
244+
245+
/* The children now have new parent */
246+
for (i = 0; i < n; i++) {
247+
struct ubifs_zbranch *child = &new_zn->zbranch[i];
248+
249+
if (child->znode)
250+
child->znode->parent = new_zn;
251+
}
252+
}
253+
254+
zbr->znode = new_zn;
255+
zbr->lnum = 0;
256+
zbr->offs = 0;
257+
zbr->len = 0;
258+
259+
atomic_long_inc(&c->dirty_zn_cnt);
260+
}
261+
236262
/**
237263
* dirty_cow_znode - ensure a znode is not being committed.
238264
* @c: UBIFS file-system description object
@@ -265,21 +291,32 @@ static struct ubifs_znode *dirty_cow_znode(struct ubifs_info *c,
265291
return zn;
266292

267293
if (zbr->len) {
268-
err = insert_old_idx(c, zbr->lnum, zbr->offs);
269-
if (unlikely(err))
270-
return ERR_PTR(err);
294+
struct ubifs_old_idx *old_idx;
295+
296+
old_idx = kmalloc(sizeof(struct ubifs_old_idx), GFP_NOFS);
297+
if (unlikely(!old_idx)) {
298+
err = -ENOMEM;
299+
goto out;
300+
}
301+
old_idx->lnum = zbr->lnum;
302+
old_idx->offs = zbr->offs;
303+
271304
err = add_idx_dirt(c, zbr->lnum, zbr->len);
272-
} else
273-
err = 0;
305+
if (err) {
306+
kfree(old_idx);
307+
goto out;
308+
}
274309

275-
zbr->znode = zn;
276-
zbr->lnum = 0;
277-
zbr->offs = 0;
278-
zbr->len = 0;
310+
do_insert_old_idx(c, old_idx);
311+
}
312+
313+
replace_znode(c, zn, znode, zbr);
279314

280-
if (unlikely(err))
281-
return ERR_PTR(err);
282315
return zn;
316+
317+
out:
318+
kfree(zn);
319+
return ERR_PTR(err);
283320
}
284321

285322
/**

0 commit comments

Comments
 (0)