Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 5 Apr 1999 18:29:42 -0600 (MDT)
From:      Stephen Clawson <sclawson@cs.utah.edu>
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   bin/10971: ypserv segfaults regularly (really: Race condition in the Berkeley db library).
Message-ID:  <199904060029.SAA19824@ibapah.cs.utah.edu>

next in thread | raw e-mail | index | archive | help

>Number:         10971
>Category:       bin
>Synopsis:       ypserv segfaults regularly (really: Race condition in the Berkeley db library).
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Apr  5 17:30:01 PDT 1999
>Closed-Date:
>Last-Modified:
>Originator:     Stephen Clawson
>Release:        FreeBSD 3.0-CURRENT i386 (jan 27, 1999)
>Organization:
University of Utah Computer Science
>Environment:

We've been running a slave yp server on a dual Pentium II/350 for a
group of twenty or so FreeBSD machines and a few assorted linux boxes.
The underlying network is switched 100BaseT to the server.  

>Description:

After setting up a slave yp server for our group and switching all of
our machines to use it, I started to notice ypserv.core files in /.
It turns out that there's a race in Berkeley DB that gets tickled
through a combination of the DB_CACHE code in ypserv, running
ypserv on an SMP box and a bug in ypserv (PR bin/10970).

The problem comes down to the DB_CACHE code keeping heavily used
databases open.  This is usually a good thing, but because the
databases are already open, when the child forks it shares a file
descriptor (and thus a file description, and thus a file pointer) for
the database file with it's parent.  If a lot of requests for the same
database come in at the same time, this means that multiple children
will also be sharing the same file description, since they all came
from the same parent.

With all the children accessing the database concurrently, they react
badly to the race in libc/db/hash/hash_page.c:__get_page():

    if ((lseek(fd, (off_t)page << hashp->BSHIFT, SEEK_SET) == -1) ||
        ((rsize = read(fd, p, size)) == -1))
                return (-1);

The problem shows up in this fragment from a ktrace:

 26527 ypserv   CALL  lseek(0x7,0,0x1e000,0,0)
 26527 ypserv   RET   lseek 122880/0x1e000
 26533 ypserv   CALL  lseek(0x7,0,0x8000,0,0)
 26533 ypserv   RET   lseek 32768/0x8000
 26527 ypserv   CALL  read(0x7,0x80a2000,0x1000)
 26527 ypserv   GIO   fd 7 read 4096 bytes
       "0\0\M-{\^O\M-?\^O\M-7\^Oq\^Ol\^O)\^O#\^O\M-_\^N\M-\\^N\M-#\^N\M^\\^NZ\
        \^NR\^N\r\^N\^E\^N"
 26527 ypserv   RET   read 4096/0x1000
 26533 ypserv   CALL  read(0x7,0x80ad000,0x1000)
 26533 ypserv   GIO   fd 7 read 4096 bytes
       "\M-_\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\
        \M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?\M^?"
 26533 ypserv   RET   read 4096/0x1000
 26527 ypserv   CALL  lseek(0x7,0,0x1f000,0,0)
 26527 ypserv   RET   lseek 126976/0x1f000
 26533 ypserv   PSIG  SIGSEGV SIG_DFL
 26533 ypserv   NAMI  "ypserv.core"

Neither process is getting the correct data, but in the case of
the second process, it's getting whatever is 0x1000 bytes after the
data it really wants, causing hash_seq() to return a pointer into
unallocated memory, causing a segfault when it's dereferenced.

>How-To-Repeat:

Set up a yp server and barrage it with as many yp_all requests that
you can.  The simplest way to do this is to fork off a bunch of `ypcat
passwd' processes from a client machine (20 is usually sufficent).

You've got to get enough going at a time that the forked children will
respond to a bunch of them at once and interleave their reads from the 
database.

>Fix:

NetBSD's fix to this problem is to introduce a new system call,
pread, which takes an offset so that the lseek and read can be done
atomicly.

Some other fixes are either to put something in the manpage that warns
about making concurrent accesses to shared open databases and how you
will eventually loose, or locking the file descriptor before the lseek
and unlocking it after the read.  The problem with the first is that
it'll be easy to miss and the problem with the second is that you have
to have the fd opened read/write to get an exclusive lock. =)

If you only want to fix ypserv, then closing and reopening the
database on a fork should do the trick, since the problem dosen't show
up if you're not sharing a file pointer.

>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




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