Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 28 Mar 2009 06:30:43 +0000 (UTC)
From:      Xin LI <delphij@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r190491 - head/lib/libc/db/hash
Message-ID:  <200903280630.n2S6Uhrr086987@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: delphij
Date: Sat Mar 28 06:30:43 2009
New Revision: 190491
URL: http://svn.freebsd.org/changeset/base/190491

Log:
  Fix a crash when iterating over a hash and removing its elements.
  
  Obtained from:	OpenBSD

Modified:
  head/lib/libc/db/hash/hash.c
  head/lib/libc/db/hash/hash_page.c

Modified: head/lib/libc/db/hash/hash.c
==============================================================================
--- head/lib/libc/db/hash/hash.c	Sat Mar 28 06:25:33 2009	(r190490)
+++ head/lib/libc/db/hash/hash.c	Sat Mar 28 06:30:43 2009	(r190491)
@@ -539,8 +539,7 @@ hash_put(const DB *dbp, DBT *key, const 
 
 	hashp = (HTAB *)dbp->internal;
 	if (flag && flag != R_NOOVERWRITE) {
-		hashp->error = EINVAL;
-		errno = EINVAL;
+		hashp->error = errno = EINVAL;
 		return (ERROR);
 	}
 	if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
@@ -719,7 +718,7 @@ hash_seq(const DB *dbp, DBT *key, DBT *d
 		hashp->cndx = 1;
 		hashp->cpage = NULL;
 	}
-
+ next_bucket:
 	for (bp = NULL; !bp || !bp[0]; ) {
 		if (!(bufp = hashp->cpage)) {
 			for (bucket = hashp->cbucket;
@@ -738,8 +737,18 @@ hash_seq(const DB *dbp, DBT *key, DBT *d
 				hashp->cbucket = -1;
 				return (ABNORMAL);
 			}
-		} else
+		} else {
 			bp = (u_int16_t *)hashp->cpage->page;
+			if (flag == R_NEXT) {
+				hashp->cndx += 2;
+				if (hashp->cndx > bp[0]) {
+					hashp->cpage = NULL;
+					hashp->cbucket++;
+					hashp->cndx = 1;
+					goto next_bucket;
+				}
+			}
+		}
 
 #ifdef DEBUG
 		assert(bp);
@@ -767,13 +776,6 @@ hash_seq(const DB *dbp, DBT *key, DBT *d
 		key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx];
 		data->data = (u_char *)hashp->cpage->page + bp[ndx + 1];
 		data->size = bp[ndx] - bp[ndx + 1];
-		ndx += 2;
-		if (ndx > bp[0]) {
-			hashp->cpage = NULL;
-			hashp->cbucket++;
-			hashp->cndx = 1;
-		} else
-			hashp->cndx = ndx;
 	}
 	return (SUCCESS);
 }

Modified: head/lib/libc/db/hash/hash_page.c
==============================================================================
--- head/lib/libc/db/hash/hash_page.c	Sat Mar 28 06:25:33 2009	(r190490)
+++ head/lib/libc/db/hash/hash_page.c	Sat Mar 28 06:30:43 2009	(r190491)
@@ -155,6 +155,14 @@ __delpair(HTAB *hashp, BUFHEAD *bufp, in
 				bp[i - 1] = bp[i + 1] + pairlen;
 			}
 		}
+		if (ndx == hashp->cndx) {
+			/*
+			 * We just removed pair we were "pointing" to.
+			 * By moving back the cndx we ensure subsequent
+			 * hash_seq() calls won't skip over any entries.
+			 */
+			hashp->cndx -= 2;
+		}
 	}
 	/* Finally adjust the page data */
 	bp[n] = OFFSET(bp) + pairlen;



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