2020-01-06 08:06:00

by Zhang Yi

[permalink] [raw]
Subject: [PATCH] jffs2: move jffs2_init_inode_info() just after allocating inode

After commit 4fdcfab5b553 ("jffs2: fix use-after-free on symlink
traversal"), it expose a freeing uninitialized memory problem due to
this commit move the operaion of freeing f->target to
jffs2_i_callback(), which may not be initialized in some error path of
allocating jffs2 inode (eg: jffs2_iget()->iget_locked()->
destroy_inode()->..->jffs2_i_callback()->kfree(f->target)).

Fix this by initialize the jffs2_inode_info just after allocating it.

Reported-by: Guohua Zhong <[email protected]>
Reported-by: Huaijie Yi <[email protected]>
Signed-off-by: zhangyi (F) <[email protected]>
Cc: [email protected]
---
fs/jffs2/fs.c | 2 --
fs/jffs2/super.c | 2 ++
2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index ab8cdd9e9325..50a9df7d43a5 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -270,7 +270,6 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
f = JFFS2_INODE_INFO(inode);
c = JFFS2_SB_INFO(inode->i_sb);

- jffs2_init_inode_info(f);
mutex_lock(&f->sem);

ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
@@ -438,7 +437,6 @@ struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_r
return ERR_PTR(-ENOMEM);

f = JFFS2_INODE_INFO(inode);
- jffs2_init_inode_info(f);
mutex_lock(&f->sem);

memset(ri, 0, sizeof(*ri));
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index 0e6406c4f362..90373898587f 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -42,6 +42,8 @@ static struct inode *jffs2_alloc_inode(struct super_block *sb)
f = kmem_cache_alloc(jffs2_inode_cachep, GFP_KERNEL);
if (!f)
return NULL;
+
+ jffs2_init_inode_info(f);
return &f->vfs_inode;
}

--
2.17.2


2020-01-20 11:46:12

by Zhang Yi

[permalink] [raw]
Subject: Re: [PATCH] jffs2: move jffs2_init_inode_info() just after allocating inode

ping.

On 2020/1/6 16:04, zhangyi (F) wrote:
> After commit 4fdcfab5b553 ("jffs2: fix use-after-free on symlink
> traversal"), it expose a freeing uninitialized memory problem due to
> this commit move the operaion of freeing f->target to
> jffs2_i_callback(), which may not be initialized in some error path of
> allocating jffs2 inode (eg: jffs2_iget()->iget_locked()->
> destroy_inode()->..->jffs2_i_callback()->kfree(f->target)).
>
> Fix this by initialize the jffs2_inode_info just after allocating it.
>
> Reported-by: Guohua Zhong <[email protected]>
> Reported-by: Huaijie Yi <[email protected]>
> Signed-off-by: zhangyi (F) <[email protected]>
> Cc: [email protected]
> ---
> fs/jffs2/fs.c | 2 --
> fs/jffs2/super.c | 2 ++
> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
> index ab8cdd9e9325..50a9df7d43a5 100644
> --- a/fs/jffs2/fs.c
> +++ b/fs/jffs2/fs.c
> @@ -270,7 +270,6 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
> f = JFFS2_INODE_INFO(inode);
> c = JFFS2_SB_INFO(inode->i_sb);
>
> - jffs2_init_inode_info(f);
> mutex_lock(&f->sem);
>
> ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
> @@ -438,7 +437,6 @@ struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_r
> return ERR_PTR(-ENOMEM);
>
> f = JFFS2_INODE_INFO(inode);
> - jffs2_init_inode_info(f);
> mutex_lock(&f->sem);
>
> memset(ri, 0, sizeof(*ri));
> diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
> index 0e6406c4f362..90373898587f 100644
> --- a/fs/jffs2/super.c
> +++ b/fs/jffs2/super.c
> @@ -42,6 +42,8 @@ static struct inode *jffs2_alloc_inode(struct super_block *sb)
> f = kmem_cache_alloc(jffs2_inode_cachep, GFP_KERNEL);
> if (!f)
> return NULL;
> +
> + jffs2_init_inode_info(f);
> return &f->vfs_inode;
> }
>
>

2020-07-23 03:20:12

by Jubin Zhong

[permalink] [raw]
Subject: Re: [PATCH] jffs2: move jffs2_init_inode_info() just after allocating inode

On 2020/1/6 16:04, zhangyi (F) wrote:
> After commit 4fdcfab5b553 ("jffs2: fix use-after-free on symlink
> traversal"), it expose a freeing uninitialized memory problem due to
> this commit move the operaion of freeing f->target to
> jffs2_i_callback(), which may not be initialized in some error path of
> allocating jffs2 inode (eg: jffs2_iget()->iget_locked()->
> destroy_inode()->..->jffs2_i_callback()->kfree(f->target)).
>
> Fix this by initialize the jffs2_inode_info just after allocating it.

We are having the same problem. After commit 4fdcfab5b553 ("jffs2: fix use-after-free on symlink
> traversal"), f->target is freed before it is initialized in the iget_locked() path. This is dangerous and may trigger slub BUG_ON:

kernel BUG at mm/slub.c:3824!
Internal error: Oops - BUG: 0 [#1] SMP ARM
CPU: 2 PID: 9 Comm: rcuos/0 Tainted: P O 4.4.185 #1
task: cf4a3f68 task.stack: cf4ca000
PC is at kfree+0xfc/0x264
LR is at jffs2_i_callback+0x10/0x28 [jffs2]
pc : [<c032a4f0>] lr : [<bf0ab188>] psr: 400e0213
sp : cf4cbec8 ip : 00000000 fp : c0273df8
r10: ceb12848 r9 : 0000000c r8 : cdd52000
r7 : bf0ab188 r6 : 0000000c r5 : e7fddef0 r4 : c1121ba0
r3 : 00000100 r2 : c0ac4010 r1 : 00000002 r0 : e7fddef0
Flags: nZcv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
Control: 32c5387d Table: 0e315940 DAC: 55555555
Process rcuos/0 (pid: 9, stack limit = 0xcf4ca190)
Stack: (0xcf4cbec8 to 0xcf4cc000)
bec0: c086efa8 c032a3a8 00000001 c0273e9c c0a29214 c3931db8
bee0: 00000000 0000000c ffffe000 cdd52000 0000000c ceb12848 c0273df8 bf0ab188
bf00: c0adf980 c0273e9c c0adf980 00000001 00000000 ffffff7c 00000000 cf4a3f68
bf20: c025bc18 cf4cbf24 cf4cbf24 c0a22448 c0adf980 cf4ca000 cf485ac0 00000000
bf40: c0adf980 c02739b0 00000000 00000000 00000000 c02380bc 00000000 c0adf380
bf60: c0adf980 00000000 00000000 00000000 00008001 cf4cbf74 cf4cbf74 00000000
bf80: 00000000 00000000 00008001 cf4cbf8c cf4cbf8c c0a22448 cf485ac0 c0237fb8
bfa0: 00000000 00000000 00000000 c0202db4 00000000 00000000 00000000 00000000
bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 00000000 00000000
[<c032a4f0>] (kfree) from [<bf0ab188>] (jffs2_i_callback+0x10/0x28 [jffs2])
[<bf0ab188>] (jffs2_i_callback [jffs2]) from [<c0273e9c>] (rcu_nocb_kthread+0x4ec/0x504)
[<c0273e9c>] (rcu_nocb_kthread) from [<c02380bc>] (kthread+0x104/0x118)
[<c02380bc>] (kthread) from [<c0202db4>] (ret_from_fork+0x14/0x20)
Code: 0300001a 143094e5 010013e3 0000001a (f201f0e7)

2020-07-23 10:01:43

by Hou Tao

[permalink] [raw]
Subject: Re: [PATCH] jffs2: move jffs2_init_inode_info() just after allocating inode

Hi,

Cc +Richard +David

On 2020/1/6 16:04, zhangyi (F) wrote:
> After commit 4fdcfab5b553 ("jffs2: fix use-after-free on symlink
> traversal"), it expose a freeing uninitialized memory problem due to
> this commit move the operaion of freeing f->target to
> jffs2_i_callback(), which may not be initialized in some error path of
> allocating jffs2 inode (eg: jffs2_iget()->iget_locked()->
> destroy_inode()->..->jffs2_i_callback()->kfree(f->target)).
>
Could you please elaborate the scenario in which the use of a uninitialized
f->target is possible ? IMO one case is that there are concurrent
jffs2_lookup() and jffs2 GC on an evicted inode, and two new inodes
are created, and then one needless inode is destroyed.

> Fix this by initialize the jffs2_inode_info just after allocating it.
>
> Reported-by: Guohua Zhong <[email protected]>
> Reported-by: Huaijie Yi <[email protected]>
> Signed-off-by: zhangyi (F) <[email protected]>
> Cc: [email protected]
> ---
A Fixes tag is also needed here.

> fs/jffs2/fs.c | 2 --
> fs/jffs2/super.c | 2 ++
> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
> index ab8cdd9e9325..50a9df7d43a5 100644
> --- a/fs/jffs2/fs.c
> +++ b/fs/jffs2/fs.c
> @@ -270,7 +270,6 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
> f = JFFS2_INODE_INFO(inode);
> c = JFFS2_SB_INFO(inode->i_sb);
>
> - jffs2_init_inode_info(f);
> mutex_lock(&f->sem);
>
> ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
> @@ -438,7 +437,6 @@ struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_r
> return ERR_PTR(-ENOMEM);
>
> f = JFFS2_INODE_INFO(inode);
> - jffs2_init_inode_info(f);
> mutex_lock(&f->sem);
>
> memset(ri, 0, sizeof(*ri));
> diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
> index 0e6406c4f362..90373898587f 100644
> --- a/fs/jffs2/super.c
> +++ b/fs/jffs2/super.c
> @@ -42,6 +42,8 @@ static struct inode *jffs2_alloc_inode(struct super_block *sb)
> f = kmem_cache_alloc(jffs2_inode_cachep, GFP_KERNEL);
> if (!f)
> return NULL;
> +
> + jffs2_init_inode_info(f);
> return &f->vfs_inode;
> }
>
>