[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: ext3 and ls in a deletted directory



Qing Liu wrote:
> 
> Hi,
> 
> With kernel 2.4.10, when I do
> 
> $ mkdir foo; cd foo; rmdir ../foo; ls
> 
> then ls becomes zombie.
> 

You oopsed.  When I do it the machine instantaneously reboots.
Oh Dear.

It's an error in the new directory readhead optimisation.  When
it's dealing with a zero-length directory it overindexes the
local array `bh_use[]' and uses an uninitialised variable as
a buffer-head pointer and dies in this code:


                if ((bh = bh_use[ra_ptr++]) == NULL)
                        goto next;
                wait_on_buffer(bh);

in ext3_find_entry().

I think in fact this overindexing happens quite often, but it is
only with zero-length directories that the zeroeth entry of the
local array contains uninitialised garbage.  For other directory
sizes, it will conveniently contain a pointer to a previously-read
buffer and the error won't be noticed.

Thanks for the report.  This patch should fix it.  Does it
look OK to you, Ted?



--- fs/ext3/namei.c.orig	Sun Oct  7 21:15:27 2001
+++ fs/ext3/namei.c	Sun Oct  7 21:41:08 2001
@@ -112,7 +112,11 @@
 	struct buffer_head * bh_use[NAMEI_RA_SIZE];
 	struct buffer_head * bh, *ret = NULL;
 	unsigned long start, block, b;
-	int ra_ptr = 0, ra_max = 0, num = 0;
+	int ra_max = 0;		/* Number of bh's in the readahead
+				   buffer, bh_use[] */
+	int ra_ptr = 0;		/* Current index into readahead
+				   buffer */
+	int num = 0;
 	int nblocks, i, err;
 	struct inode *dir = dentry->d_parent->d_inode;
 
@@ -130,12 +134,19 @@
 		 * We deal with the read-ahead logic here.
 		 */
 		if (ra_ptr >= ra_max) {
+			/* Refill the readahead buffer */
 			ra_ptr = 0;
 			b = block;
 			for (ra_max = 0; ra_max < NAMEI_RA_SIZE; ra_max++) {
-				if (b >= nblocks ||
-				    (num && block == start))
+				/*
+				 * Terminate if we reach the end of the
+				 * directory and must wrap, or if our
+				 * search has finished at this block.
+				 */
+				if (b >= nblocks || (num && block == start)) {
+					bh_use[ra_max] = NULL;
 					break;
+				}
 				num++;
 				bh = ext3_getblk(NULL, dir, b++, 0, &err);
 				bh_use[ra_max] = bh;





[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]