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

bug in Berkeley DB



[Preliminaries:

This information was originally posted to comp.unix.bsd.openbsd.misc with
message ID <chq8b402lmd_(_at_)_enews2_(_dot_)_newsguy_(_dot_)_com>, Subject "DB 1.85 bug (patch
included)"

I don't have an OpenBSD box myself; I am involved as the author of a program
that uses Berkeley DB through the perl module "DB_File" and has had problems
when used with Berkeley DB 1.x. The bug is not specific to OpenBSD, but
sleepycat/Bostic doesn't care about DB 1.x bugs so there is no better place
to report it. This bug probably also affects NetBSD and FreeBSD and any other
system that still uses DB 1.x.]

The bug: In the hash access method, if there are many DB->get() calls in
between two consecutive DB->seq() calls, it is possible for the cursor page
to become the LRU, and then get overwritten by a new page read from disk.
After that happens, the next DB->seq() returns fragments of the new page
instead of the correct key and data. Under some circumstances it can
segfault.

I have a standalone test case which demonstrates the existence of the bug.
Because it requires a somewhat large DB (over 100k) I am not including it in
this mail. You can download it from <http://proxypot.org/db1bug.tgz> which
includes test programs in C and perl to trigger the bug.

Because the bug depends on the cursor page being the LRU in the cache, the
test program will probably not trigger the bug if the page size or cache
size is not the same as the original test system, which was OpenBSD 3.5 i386.

This patch seems to work, although I'm not extremely familiar with DB
internals; there is probably a more elegant way to fix the problem (like
having hash_seq() check the validity of the page and reload it from disk if
necessary - I tried that but couldn't make it work so I switched to the easy
way).

--- libdb-1.85.4.orig/hash/hash_buf.c	Fri Jul 15 07:23:46 1994
+++ libdb-1.85.4.pac/hash/hash_buf.c	Thu Sep  9 01:41:44 2004
@@ -171,11 +171,19 @@
 
 	oaddr = 0;
 	bp = LRU;
+
+        /* It is bad to overwrite the page under the cursor. */
+        if(bp==hashp->cpage) {
+                BUF_REMOVE(bp);
+                MRU_INSERT(bp);
+                bp = LRU;
+        }
+
 	/*
 	 * If LRU buffer is pinned, the buffer pool is too small. We need to
 	 * allocate more buffers.
 	 */
-	if (hashp->nbufs || (bp->flags & BUF_PIN)) {
+	if (hashp->nbufs || (bp->flags & BUF_PIN) || bp==hashp->cpage) {
 		/* Allocate a new one */
 		if ((bp = (BUFHEAD *)malloc(sizeof(BUFHEAD))) == NULL)
 			return (NULL);



Visit your host, monkey.org