Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 12 Aug 2007 03:31:29 +0200
From:      "Timur I. Bakeyev" <timur@FreeBSD.org>
To:        "FreeBSD gnats submit" <FreeBSD-gnats-submit@FreeBSD.org>
Subject:   bin/115430: rpc.statd core dumps if unable to mmap() /var/db/statd.status file
Message-ID:  <1186882289.87499@timur.home.bat.ru>
Resent-Message-ID: <200708120140.l7C1e1Sk046076@freefall.freebsd.org>

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

>Number:         115430
>Category:       bin
>Synopsis:       rpc.statd core dumps if unable to mmap() /var/db/statd.status file
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sun Aug 12 01:40:01 GMT 2007
>Closed-Date:
>Last-Modified:
>Originator:     Timur I. Bakeyev
>Release:        FreeBSD 6.2-STABLE i386
>Organization:
>Environment:


System: FreeBSD 6.2-STABLE #0: Tue Jun 26 16:02:16 CEST 2007
    root@timur.home.bat.ru:/usr/src/sys/i386/compile/COMMON



>Description:


Somehow with my recent enough build rpc.statd is unable to mmap() "/var/db/statd.status" file and core dumps. Possibly, the failure of mmap() is fixed in latest STABLE, but this problem revealed a race condition in the rpc.statd. Here is the stack trace and piece of offending code:

timur# gdb `which rpc.statd` rpc.statd.0.1092.core 
GNU gdb 6.1.1 [FreeBSD]
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-marcel-freebsd"...
Core was generated by `rpc.statd'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /usr/lib/librpcsvc.so.3...done.
Loaded symbols for /usr/lib/librpcsvc.so.3
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /libexec/ld-elf.so.1...done.
Loaded symbols for /libexec/ld-elf.so.1
#0  0x08049531 in init_file (filename=0x804a880 "/var/db/statd.status") at /usr/src/usr.sbin/rpc.statd/file.c:170
(gdb) l 150
145
146       /* try to open existing file - if not present, create one             */
147       status_fd = open(filename, O_RDWR);
148       if ((status_fd < 0) && (errno == ENOENT))
149       {
150         status_fd = open(filename, O_RDWR | O_CREAT, 0644);
151         new_file = TRUE;
152       }
153       if (status_fd < 0)
154         errx(1, "unable to open status file %s", filename);
155
156       /* File now open.  mmap() it, with a generous size to allow for       */
157       /* later growth, where we will extend the file but not re-map it.     */
158       status_info = (FileLayout *)
159         mmap(NULL, 0x10000000, PROT_READ | PROT_WRITE, MAP_SHARED, status_fd, 0);
160
161       if (status_info == (FileLayout *) MAP_FAILED)
162         warn("unable to mmap() status file");
163
164       status_file_len = lseek(status_fd, 0L, SEEK_END);
165
166       /* If the file was not newly created, validate the contents, and if   */
167       /* defective, re-create from scratch.                                 */
168       if (!new_file)
169       {
170         if ((status_file_len < HEADER_LEN) || (status_file_len
171           < (HEADER_LEN + sizeof(HostInfo) * status_info->noOfHosts)) )
172         {
173           warnx("status file is corrupt");
174           new_file = TRUE;

On line 170 we have a guardian condition:

170         if ((status_file_len < HEADER_LEN) ||

But it doesn't work after second invokation of rpc.statd, when status file was already created.

statd.h:#define HEADER_LEN (sizeof(FileLayout) - sizeof(HostInfo))

(gdb) p status_file_len 
$1 = 256
(gdb) p sizeof(FileLayout) - sizeof(HostInfo)
$2 = 256

As it's seen on line 161, in case of mmap() failure status_info variable takes special value MAP_FAILED:

mman.h:#define MAP_FAILED       ((void *)-1) 

And a warning issued. Possibly, such a failure shouldn't be a fatal event, we just loose some speed in processing. But the fact of a failure isn't taken into account on line 171, when we try to dereference status_info unconditionally:

   sizeof(HostInfo) * status_info->noOfHosts

which leads to the core dump. I don't think, this bug is exploitable, but definetly, it have to be fixed one way or another.



>How-To-Repeat:


I wish I know what's wrong with mmap() functino in my build. If it is reproduced then rpc.statd will crash for sure. But the bug is visible on a first glance.




>Fix:


Simplest solution would be to croak on mmap() failure, but possibly it's too taugh. 

The better approach would be to have a branch in the code, which executed only when mmap() is succeded.


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



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