Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 29 May 1998 11:44:37 -0700 (PDT)
From:      cts@internetcds.com
To:        freebsd-gnats-submit@FreeBSD.ORG
Subject:   bin/6787: race condition in pw command 
Message-ID:  <199805291844.LAA07680@hub.freebsd.org>

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

>Number:         6787
>Category:       bin
>Synopsis:       race condition in pw command
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:
>Keywords:
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri May 29 11:50:03 PDT 1998
>Last-Modified:
>Originator:     Craig Spannring
>Organization:
InternetCDS
>Release:        2.2.6
>Environment:
FreeBSD bangkok.office.cdsnet.net 2.2.6-STABLE FreeBSD 2.2.6-STABLE #1: Tue Apr 28 16:38:57 PDT 1998     root@bangkok.office.cdsnet.net:/usr/src/sys/compile/BANGKOK  i386

>Description:
vipw, chpass and passwd all lock the /etc/master.passwd file for the
entire time they run.  pw tries not to hold the lock the entire time.
Specifically it does not have the file locked when it calls pwd_mkdb.
This causes some race conditions since multiple instances of pwd_mkdb
can run at the same time.

I think pw should take a pessimistic approach and keep the
/etc/master.passwd file locked the entire time just like passwd, vipw
and chpass.  My quick fix for pw opens /etc/master.passwd in main()
with the O_EXLOCK flag in pw.c and doesn't close the file descriptor.
I also had to make sure that fileupdate() in fileupd.c does not try to
lock the /etc/master.passwd file.

I tried to contact David L. Nugent, <davidn@blaze.net.au> last week
but I have heard back from him yet.

I believe this problem exists in the -current tree as well as -stable.
>How-To-Repeat:
The following perl5 script will demonstrate the race condition.  It
forks 10 copies of itself and each one tries to create and delete 100
users.  With a fixed version of pw there will be no error messages
from pwd_mkdb and it will delete all of the new accounts before it finishes.  

With the current version of pw you wind up with a large number of
accounts that aren't deleted because of the race condition and a large
number of messages from pwd_mkdb such as-

  pwd_mkdb: /etc/pwd.db.tmp: File exists
  pwd_mkdb: /etc/pwd.db.tmp to /etc/pwd.db: No such file or directory
  pwd_mkdb: /etc/pwd.db.tmp: No such file or directory
  pwd_mkdb: /etc/spwd.db.tmp: File exists



#!/usr/local/bin/perl -w
#
#
# Demonstrate pw glitch that occurs when multiple instances of pw run
# at the same time.
#

use strict 'vars';
use strict 'subs';

use diagnostics;
use diagnostics -verbose;
enable diagnostics;

my $a;
my $b;
my $i;
my $notAdded      = 0;
my $notDeleted    = 0;
my $wasAdded      = 0;
my $pwAddFailures = 0;
my $pwDelFailures = 0;

my $PW="/usr/sbin/pw";

for($i=0; $i<10; $i++)
{
    my $rc = fork();
    if ($rc == -1)
    {
        print STDERR "Couldn't fork\n";
        exit(1);
    }
    elsif ($rc == 0)
    {
        next;
    }
    else
    {
        foreach $a ('0'..'9')
        {
            foreach $b ('0'..'9')
            {
                my $username = "u$a$b$$";
                my $cmd = "$PW useradd $username -d /tmp/$a/$b/$username";
                
                mkdir("/tmp/$a", 0755);
                mkdir("/tmp/$a/$b", 0755);

                print "system($cmd);\n";
                system($cmd)==0 || $pwAddFailures++;
                if (!defined(getpwnam($username)))
                {
                    $notAdded++;
                    $wasAdded=1;
                }
                else
                {
                    $wasAdded=1;
                }
                $cmd = "$PW userdel $username";
                print "system($cmd);\n";
                system($cmd)==0 || $pwDelFailures++;
                if ($wasAdded && defined(getpwnam($username)))
                {
                    $notDeleted++;
                }
            }
        }
        open (STATUS, ">/tmp/status.$$");
        print STATUS "'pw adduser' recorded $pwAddFailures failures.\n";
        print STATUS "'pw deluser' recorded $pwDelFailures failures.\n";
        print STATUS "$notAdded accounts weren't added, $notDeleted accounts weren't deleted\n";
        print STATUS "$notAdded accounts weren't added, $notDeleted accounts weren't deleted\n";
        
    }
}


>Fix:
main() in pw.c should be modified to set an exclusive lock 
on /etc/master.passwd.

The function fileupdate in fileupd.c should be modified so that it does not 
lock /etc/master.passwd.

>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?199805291844.LAA07680>