lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <ef87d43380a08f86be080ba4bc438db9c83b63f5.camel@ibm.com>
Date: Fri, 29 Aug 2025 21:53:36 +0000
From: Viacheslav Dubeyko <Slava.Dubeyko@....com>
To: "glaubitz@...sik.fu-berlin.de" <glaubitz@...sik.fu-berlin.de>,
        "yang.chenzhi@...o.com" <yang.chenzhi@...o.com>,
        "slava@...eyko.com"
	<slava@...eyko.com>,
        "frank.li@...o.com" <frank.li@...o.com>
CC: "syzbot+005d2a9ecd9fbf525f6a@...kaller.appspotmail.com"
	<syzbot+005d2a9ecd9fbf525f6a@...kaller.appspotmail.com>,
        "linux-fsdevel@...r.kernel.org" <linux-fsdevel@...r.kernel.org>,
        "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>
Subject: Re:  [PATCH] hfsplus: fix missing hfs_bnode_get() in
 __hfs_bnode_create

On Fri, 2025-08-29 at 17:39 +0800, Chenzhi Yang wrote:
> From: Yang Chenzhi <yang.chenzhi@...o.com>
> 
> When sync() and link() are called concurrently, both threads may
> enter hfs_bnode_find() without finding the node in the hash table
> and proceed to create it.
> 

It's clear why link() could try to create a b-tree node. But it's not completely
clear why sync() should try to create a b-tree node? Usually, sync() should try
to flush already existed inodes. Especially, if we imply any system inodes that
are created mostly during mount operation. How is it possible that sync() could
try to create the node? Moreover, node_id 0 (root node) should be already
created if we created the b-tree.

I am investigating right now the issue when root node is re-written by another
node during inserting the record in HFS (but I assume that HFS+ could have the
same issue). So, I think your fix is good but we have some more serious issue in
the background.

So, if you are fixing the syzbot reported issue, then it could be good to have
call trace in the comment to understand the fix environment. And it could be
more clear in what environment sync() is trying to create the b-tree node. I
like your an analysis of the issue but not all details are shared. And I still
have questions how sync() could try to create the b-tree's node. Probably, this
fix could be justified by concurrent link() requests or something like this. But
concurrent creation of b-tree node by sync() and link() looks really suspicious.

Thanks,
Slava.

> Thread A:
>   hfsplus_write_inode()
>     -> hfsplus_write_system_inode()
>       -> hfs_btree_write()
>         -> hfs_bnode_find(tree, 0)
>           -> __hfs_bnode_create(tree, 0)
> 
> Thread B:
>   hfsplus_create_cat()
>     -> hfs_brec_insert()
>       -> hfs_bnode_split()
>         -> hfs_bmap_alloc()
>           -> hfs_bnode_find(tree, 0)
>             -> __hfs_bnode_create(tree, 0)
> 
> In this case, thread A creates the bnode, sets refcnt=1, and hashes it.
> Thread B also tries to create the same bnode, notices it has already
> been inserted, drops its own instance, and uses the hashed one without
> getting the node.
> 
> ```
> 
> 	node2 = hfs_bnode_findhash(tree, cnid);
> 	if (!node2) {                                 <- Thread A
> 		hash = hfs_bnode_hash(cnid);
> 		node->next_hash = tree->node_hash[hash];
> 		tree->node_hash[hash] = node;
> 		tree->node_hash_cnt++;
> 	} else {                                      <- Thread B
> 		spin_unlock(&tree->hash_lock);
> 		kfree(node);
> 		wait_event(node2->lock_wq,
> 			!test_bit(HFS_BNODE_NEW, &node2->flags));
> 		return node2;
> 	}
> ```
> 
> However, hfs_bnode_find() requires each call to take a reference.
> Here both threads end up setting refcnt=1. When they later put the node,
> this triggers:
> 
> BUG_ON(!atomic_read(&node->refcnt))
> 
> In this scenario, Thread B in fact finds the node in the hash table
> rather than creating a new one, and thus must take a reference.
> 
> Fix this by calling hfs_bnode_get() when reusing a bnode newly created by
> another thread to ensure the refcount is updated correctly.
> 
> A similar bug was fixed in HFS long ago in commit
> a9dc087fd3c4 ("fix missing hfs_bnode_get() in __hfs_bnode_create")
> but the same issue remained in HFS+ until now.
> 
> Reported-by: syzbot+005d2a9ecd9fbf525f6a@...kaller.appspotmail.com
> Signed-off-by: Yang Chenzhi <yang.chenzhi@...o.com>
> ---
>  fs/hfsplus/bnode.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c
> index 14f4995588ff..e774bd4f40c3 100644
> --- a/fs/hfsplus/bnode.c
> +++ b/fs/hfsplus/bnode.c
> @@ -522,6 +522,7 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)
>  		tree->node_hash[hash] = node;
>  		tree->node_hash_cnt++;
>  	} else {
> +		hfs_bnode_get(node2);
>  		spin_unlock(&tree->hash_lock);
>  		kfree(node);
>  		wait_event(node2->lock_wq,

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ