Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 27 May 1998 13:02:01 -0700 (PDT)
From:      Craig Spannring <cts@internetcds.com>
To:        freebsd-hackers@FreeBSD.ORG
Subject:   Race conditions in pw
Message-ID:  <199805272002.NAA18714@bangkok.office.cdsnet.net>

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


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.

The following perl 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";
        
    }
}


-- 
=======================================================================
 Life is short.                  | Craig Spannring 
      Ski hard, Bike fast.       | cts@internetcds.com
 --------------------------------+------------------------------------
 Any sufficiently perverted technology is indistinguishable from Perl.
=======================================================================

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



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