diff mbox series

[2/6] hfsplus: fix BUG on bnode parent update

Message ID 5ee1db09b60373a15890f6a7c835d00e76bf601d.1535682461.git.ernesto.mnd.fernandez@gmail.com (mailing list archive)
State New, archived
Headers show
Series [1/6] hfsplus: prevent btree data loss on root split | expand

Commit Message

Ernesto A. Fernández Aug. 31, 2018, 3:59 a.m. UTC
Creating, renaming or deleting a file may hit BUG_ON() if the first
record of both a leaf node and its parent are changed, and if this
forces the parent to be split.  This bug is triggered by xfstests
generic/027, somewhat rarely; here is a more reliable reproducer:

  truncate -s 50M fs.iso
  mkfs.hfsplus fs.iso
  mount fs.iso /mnt
  i=1000
  while [ $i -le 2400 ]; do
    touch /mnt/$i &>/dev/null
    ((++i))
  done
  i=2400
  while [ $i -ge 1000 ]; do
    mv /mnt/$i /mnt/$(perl -e "print $i x61") &>/dev/null
    ((--i))
  done

The issue is that a newly created bnode is being put twice.  Reset
new_node to NULL in hfs_brec_update_parent() before reaching goto again.

Signed-off-by: Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
---
 fs/hfsplus/brec.c | 1 +
 1 file changed, 1 insertion(+)

Comments

Viacheslav Dubeyko Oct. 24, 2018, 1:33 a.m. UTC | #1
On Fri, 2018-08-31 at 00:59 -0300, Ernesto A. Fernández wrote:
> Creating, renaming or deleting a file may hit BUG_ON() if the first
> record of both a leaf node and its parent are changed, and if this
> forces the parent to be split.  This bug is triggered by xfstests
> generic/027, somewhat rarely; here is a more reliable reproducer:
> 
>   truncate -s 50M fs.iso
>   mkfs.hfsplus fs.iso
>   mount fs.iso /mnt
>   i=1000
>   while [ $i -le 2400 ]; do
>     touch /mnt/$i &>/dev/null
>     ((++i))
>   done
>   i=2400
>   while [ $i -ge 1000 ]; do
>     mv /mnt/$i /mnt/$(perl -e "print $i x61") &>/dev/null
>     ((--i))
>   done
> 
> The issue is that a newly created bnode is being put twice.  Reset
> new_node to NULL in hfs_brec_update_parent() before reaching goto again.
> 
> Signed-off-by: Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
> ---
>  fs/hfsplus/brec.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c
> index aa17a392b414..1918544a7871 100644
> --- a/fs/hfsplus/brec.c
> +++ b/fs/hfsplus/brec.c
> @@ -449,6 +449,7 @@ static int hfs_brec_update_parent(struct hfs_find_data *fd)
>  			/* restore search_key */
>  			hfs_bnode_read_key(node, fd->search_key, 14);
>  		}
> +		new_node = NULL;

Sorry, I don't follow where the new_node is put twice. Could you explain
in more details? Currently, it looks unclear. I like to assign the NULL
value to the pointer. But are you sure that it's proper place? Maybe it
makes sense to place this assignment in the beginning of the function?

Thanks,
Vyacheslav Dubeyko.


>  	}
>  
>  	if (!rec && node->parent)
Ernesto A. Fernández Oct. 24, 2018, 2:48 a.m. UTC | #2
On Tue, Oct 23, 2018 at 06:33:50PM -0700, Viacheslav Dubeyko wrote:
> On Fri, 2018-08-31 at 00:59 -0300, Ernesto A. Fernández wrote:
> > Creating, renaming or deleting a file may hit BUG_ON() if the first
> > record of both a leaf node and its parent are changed, and if this
> > forces the parent to be split.  This bug is triggered by xfstests
> > generic/027, somewhat rarely; here is a more reliable reproducer:
> > 
> >   truncate -s 50M fs.iso
> >   mkfs.hfsplus fs.iso
> >   mount fs.iso /mnt
> >   i=1000
> >   while [ $i -le 2400 ]; do
> >     touch /mnt/$i &>/dev/null
> >     ((++i))
> >   done
> >   i=2400
> >   while [ $i -ge 1000 ]; do
> >     mv /mnt/$i /mnt/$(perl -e "print $i x61") &>/dev/null
> >     ((--i))
> >   done
> > 
> > The issue is that a newly created bnode is being put twice.  Reset
> > new_node to NULL in hfs_brec_update_parent() before reaching goto again.
> > 
> > Signed-off-by: Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
> > ---
> >  fs/hfsplus/brec.c | 1 +
> >  1 file changed, 1 insertion(+)
> > 
> > diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c
> > index aa17a392b414..1918544a7871 100644
> > --- a/fs/hfsplus/brec.c
> > +++ b/fs/hfsplus/brec.c
> > @@ -449,6 +449,7 @@ static int hfs_brec_update_parent(struct hfs_find_data *fd)
> >  			/* restore search_key */
> >  			hfs_bnode_read_key(node, fd->search_key, 14);
> >  		}
> > +		new_node = NULL;
> 
> Sorry, I don't follow where the new_node is put twice. Could you explain
> in more details? Currently, it looks unclear.

There is a 'goto again', as I said in the commit message.  Follow the code
and you'll see that hfs_bnode_put() is called again on the same node.

> I like to assign the NULL value to the pointer.

This isn't a matter of taste.

> But are you sure that it's proper place?

Yes, but it's always better if somebody reviews the code...

> Maybe it
> makes sense to place this assignment in the beginning of the function?

Without knowing what you mean by "beginning of the function", I can't
tell if your idea would work or not.

> Thanks,
> Vyacheslav Dubeyko.
> 
> 
> >  	}
> >  
> >  	if (!rec && node->parent)
> 
>
Ernesto A. Fernández Oct. 24, 2018, 10:45 p.m. UTC | #3
On Wed, Oct 24, 2018 at 02:39:47PM -0700, Andrew Morton wrote:
> On Tue, 23 Oct 2018 23:48:46 -0300 Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com> wrote:
> 
> > > Sorry, I don't follow where the new_node is put twice. Could you explain
> > > in more details? Currently, it looks unclear.
> > 
> > There is a 'goto again', as I said in the commit message.  Follow the code
> > and you'll see that hfs_bnode_put() is called again on the same node.
> > 
> > > I like to assign the NULL value to the pointer.
> > 
> > This isn't a matter of taste.
> > 
> > > But are you sure that it's proper place?
> > 
> > Yes, but it's always better if somebody reviews the code...
> > 
> > > Maybe it
> > > makes sense to place this assignment in the beginning of the function?
> > 
> > Without knowing what you mean by "beginning of the function", I can't
> > tell if your idea would work or not.
> 
> I think it would be clearer to do it this way:
> 
> --- a/fs/hfs/brec.c~a
> +++ a/fs/hfs/brec.c
> @@ -359,11 +359,11 @@ static int hfs_brec_update_parent(struct
>  
>  	tree = fd->tree;
>  	node = fd->bnode;
> -	new_node = NULL;
>  	if (!node->parent)
>  		return 0;
>  
>  again:
> +	new_node = NULL;
>  	parent = hfs_bnode_find(tree, node->parent);
>  	if (IS_ERR(parent))
>  		return PTR_ERR(parent);
> 
> But it doesn't matter much...

Right, that looks better.  I don't remember why I did it this way.  If
you want me to resend I'd rather run some tests again, just in case.
diff mbox series

Patch

diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c
index aa17a392b414..1918544a7871 100644
--- a/fs/hfsplus/brec.c
+++ b/fs/hfsplus/brec.c
@@ -449,6 +449,7 @@  static int hfs_brec_update_parent(struct hfs_find_data *fd)
 			/* restore search_key */
 			hfs_bnode_read_key(node, fd->search_key, 14);
 		}
+		new_node = NULL;
 	}
 
 	if (!rec && node->parent)