Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 17 Aug 2005 10:37:58 +1000 (EST)
From:      Bruce Evans <bde@zeta.org.au>
To:        Andriy Gapon <avg@icyb.net.ua>
Cc:        freebsd-bugs@freebsd.org, FreeBSD-gnats-submit@freebsd.org
Subject:   Re: kern/84983: udf filesystem: stat-ting files could randomly fail
Message-ID:  <20050817101317.U988@epsplex.bde.org>
In-Reply-To: <200508160942.j7G9gPJe054914@oddity.topspin.kiev.ua>
References:  <200508160942.j7G9gPJe054914@oddity.topspin.kiev.ua>

next in thread | previous in thread | raw e-mail | index | archive | help
On Tue, 16 Aug 2005, Andriy Gapon wrote:

> System:
> FreeBSD 5.4-RELEASE-p3 #3: Sat Jul 9 17:02:15 EEST 2005 i386
>
>> Description:
> Sometimes stat(2) on files located on UDF fs unexpectedly fails, at the same
> time "udf: invalid FID fragment" kernel messages are produced.
> umount+mount in such situation usually cures the problem, but sometimes
> I have to do kldunload udf in between.
> The symptom very much looks like use of unitialized variable/memory.

Indeed.

> Brief search for a culprit made me suspect udf_node.diroff field.
> This is how udf_node structures are allocated:
>
> udf_vfsops.c:   struct udf_node *unode;
> 		...
>                unode = uma_zalloc(udf_zone_node, M_WAITOK);
>
> i.e. there is no M_ZERO while allocating udf_node and diroff field does
> not seem to be initialized explicitely:
>
> $ fgrep diroff *.[ch]
> udf.h:  long            diroff;
> udf_vnops.c:    if (nameiop != LOOKUP || node->diroff == 0 || node->diroff > fsize) {
> udf_vnops.c:            offset = node->diroff;
> udf_vnops.c:                            node->diroff = ds->offset + ds->off;
>
> as you can see diroff could be used before it is assigned and it is used in
> udf_lookup() function as follows:
>
> 	if (nameiop != LOOKUP || node->diroff == 0 || node->diroff > fsize) {
>        	offset = 0;
>        	numdirpasses = 1;
> 	} else {
>        	offset = node->diroff;
>        	numdirpasses = 2;
>        	nchstats.ncs_2passes++;
> 	}
>
> lookloop:
>        ds = udf_opendir(node, offset, fsize, udfmp);
>
> as you can see, if diroff belongs to interval (0, fsize] and nameiop is LOOKUP,
> then offset variable will be assigned with its (random) value that, in turn,
> would be passed down to udf_opendir and, thus, directory stream would contain
> incorrect (arbitrary) data.
>
>> How-To-Repeat:
> because of probabalistic nature of the bug, there is no definite recipe of
> reproducing it. you could try to do a lot of operations (ls, stat) on files
> on UDF fs and see if eventually directory udf_node will be allocated with
> "good" junk at diroff position.
> for me, this happens from time to time on its own.
>
>> Fix:
>
> if my theory is correct, then the following patch should fix the problem by
> adding proper initialization to diroff field of udf_node:
>
> --- lookup.patch begins here ---
> --- sys/fs/udf/udf_vfsops.c.orig	Mon Aug 15 21:06:50 2005
> +++ sys/fs/udf/udf_vfsops.c		Mon Aug 15 21:07:07 2005
> @@ -648,6 +648,7 @@
> 		return (error);
> 	}
>
> +	unode->diroff = 0;
> 	unode->i_vnode = vp;
> 	unode->hash_id = ino;
> 	unode->i_devvp = udfmp->im_devvp;

This is fixed accidentally in in -current.

I sent essentially the above fix together with fixes for many other bugs
in udf to the udf maintainer 4 months ago:

% Date: Tue, 19 Apr 2005 21:24:34 +1000 (EST)
% From: Bruce Evans <bde@zeta.org.au>
% To: scottl@freebsd.org
% Subject: Re: lots of bugs in udf
% 
% On Sun, 10 Apr 2005, Bruce Evans wrote:
% 
% > ...
% > Here is my work-in-progress patch:
% 
% Here is a better patch with some other bugs fixed:
% 
% % Index: udf_vfsops.c
% % ===================================================================
% % RCS file: /home/ncvs/src/sys/fs/udf/udf_vfsops.c,v
% % retrieving revision 1.18
% % diff -u -2 -r1.18 udf_vfsops.c
% % --- udf_vfsops.c	23 Jun 2004 19:36:09 -0000	1.18
% % +++ udf_vfsops.c	19 Apr 2005 09:38:36 -0000
% % @@ -654,4 +654,5 @@
% %  	unode->i_dev = udfmp->im_dev;
% %  	unode->udfmp = udfmp;
% % +	unode->diroff = 0;
% %  	vp->v_data = unode;
% %  	VREF(udfmp->im_devvp);
% 
% unode->diroff was not initialized, so udf_lookup() tended to use garbage
% offsets and fail after vnodes were recycled.
% 
% The second pass in udf_lookup() should hide this problem, but doesn't
% always because the second pass is not attempted after a bad fid.  If
% the stale offset is for a file in the same directory, then the fid is
% not bad and the second pass works.
% 
% This was fixed as an undocumented side effect of rev.1.28.  uma_zalloc()
% now M_ZEROs everything, although this is less needed than before because
% changes associated with rev.1.28 removed fields from the struct, leaving
% only 5 fields so they are very easy to initialized explicitly.  A similar
% change was made to ffs.  It may be more needed there.  I prefer the old
% code.
% 
% [... description of other bugs and fixes deleted]

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20050817101317.U988>