COMMAND

    kernel

SYSTEMS AFFECTED

    386BSD-derived OSes, including all versions of FreeBSD, NetBSD and OpenBSD

PROBLEM

    Following is based on  FreeBSD Security Advisory (by  Peter Wemm).
    System V IPC  is a set  of interfaces for  providing inter-process
    communication,  in  the  form  of  shared memory segments, message
    queues and semaphores.  These are managed in user-space by ipcs(1)
    and related utilities.

    An  undocumented  system  call  is  incorrectly  exported from the
    kernel without access-control checks.   This operation causes  the
    acquisition in the kernel of  a global semaphore which causes  all
    processes on the system  to block during exit()  handling, thereby
    preventing  any  process  from  exiting  until  the  corresponding
    "unblock" system call is issued.

    This operation was intended for use only by ipcs(1) to  atomically
    sample the state of System V IPC resources on the system (i.e., to
    ensure that resources are not allocated or deallocated during  the
    process of sampling  itself).  In  the future, this  functionality
    may be reimplemented as a sysctl() node.

    An unprivileged local user can  cause every process on the  system
    to hang during exiting. In  other words, after the system  call is
    issued, no process on the  system will be able to  exit completely
    until another  user issues  the "unblock"  call or  the system  is
    rebooted.  This is a denial-of-service attack.

    The advisory is somehow wrong.  If you examine the code in  NetBSD
    (which FreeBSD should  have done before  claiming that NetBSD  was
    vulnerable as  claimed in  the alert),  you will  note that if the
    exiting process is  not using semaphores  (i.e. has no  `sem_undo'
    structure allocated  for it),  then the  exiting process  will not
    block, but rather semexit() will simply return.  The corresponding
    commit log message for kern/sysv_sem.c is:

        revision 1.14
        date: 1994/12/05 07:22:12;  author: mycroft;  state: Exp;  lines:  +83 -16
        Merge changes from Daniel Boulet to avoid waiting for the semaphore lock in
        semexit() if there are no undo records to process.

    Technically speaking, if the exiting process was using semaphores,
    yes, it is vulnerable.   But there aren't very  many that do.   In
    any  case,  it's  a  far  cry  from  "prevent  all  processes from
    exiting".

SOLUTION

    In  OpenBSD  land,  they  have  discovered  the  same thing.  Only
    processes  which  are  using  semaphores  get wedged and unable to
    exit.  Once the wedging is undone, those processes exit  normally.
    Processes  not  using  semaphores  are  unaffected.  Their testing
    shows that  FreeBSD complete  wedges solid.   It looks  like  they
    missed  a  patch  merged  into  NetBSD  in 1994 (and which OpenBSD
    inherited).  In  any case, a  patch is available  which stops that
    behaviour in 2.6, and 2.7 does not have this problem.  (2.7 is out
    June 15 2000):

        http://www.openbsd.org/errata26.html#semconfig

    At  the  moment,  they  do  not  care too much that ipcs(1) cannot
    provide an  atomic snapshot  of information;  many other utilities
    do not claim atomic information either.  This bug does not  affect
    the upcoming OpenBSD  2.7 release, or  any of the  recent 2.7-beta
    snapshots that many users are already running.

    If you  are unsure  if any  applications use  semaphores, use  the
    ipcs(1) command; if it  displays more than simple  column headers,
    there is active use of semaphores on your system.  For NetBSD 1.4,
    1.4.1, and 1.4.2 a patch is available in

        ftp://ftp.NetBSD.ORG/pub/NetBSD/misc/security/patches/20000527-sysvsem

    NetBSD-current since 20000527 contains  all the fixes, and  is not
    vulnerable.  Users  of NetBSD-current should  upgrade to a  source
    tree dated 20000527 or later.

    As for FreeBSD,  upgrade to FreeBSD  2.1.7.1-STABLE, 2.2.8-STABLE,
    3.4-STABLE, 4.0-STABLE or  5.0-CURRENT after the  correction date.
    Alternatively, apply  the following  patch and  rebuild the kernel
    and  the   src/usr.bin/ipcs  utility.   This  patch   removes  the
    semconfig() syscall.  It has been tested to apply cleanly  against
    3.4-RELEASE, 3.4-STABLE, 4.0-RELEASE and 4.0-STABLE systems.

    1) Save this advisory as a file, and run the following commands as
       root:

        # cd /usr/src
        # patch -p < /path/to/advisory
        # cd usr.bin/ipcs
        # make all install

    2) Rebuild  and  reinstall  the  kernel  and  kernel  modules   as
       described in the FreeBSD handbook
    3) Reboot the system

    Patches for FreeBSD systems before the resolution date:

    --- sys/kern/syscalls.master	2000/01/19 06:01:07	1.72
    +++ sys/kern/syscalls.master	2000/05/01 11:15:10	1.72.2.1
    @@ -342,7 +342,7 @@
     221	STD	BSD	{ int semget(key_t key, int nsems, int semflg); }
     222	STD	BSD	{ int semop(int semid, struct sembuf *sops, \
 			        u_int nsops); }
    -223	STD	BSD	{ int semconfig(int flag); }
    +223	UNIMPL	NOHIDE	semconfig
     224	STD	BSD	{ int msgctl(int msqid, int cmd, \
 			        struct msqid_ds *buf); }
     225	STD	BSD	{ int msgget(key_t key, int msgflg); }
    --- sys/kern/init_sysent.c	2000/01/19 06:02:29	1.79
    +++ sys/kern/init_sysent.c	2000/05/01 11:15:56	1.79.2.1
    @@ -243,7 +243,7 @@
 	    { 4, (sy_call_t *)__semctl },			/* 220 = __semctl */
 	    { 3, (sy_call_t *)semget },			/* 221 = semget */
 	    { 3, (sy_call_t *)semop },			/* 222 = semop */
    -	{ 1, (sy_call_t *)semconfig },			/* 223 = semconfig */
    +	{ 0, (sy_call_t *)nosys },			/* 223 = semconfig */
 	    { 3, (sy_call_t *)msgctl },			/* 224 = msgctl */
 	    { 2, (sy_call_t *)msgget },			/* 225 = msgget */
 	    { 4, (sy_call_t *)msgsnd },			/* 226 = msgsnd */
    --- sys/kern/syscalls.c	2000/01/19 06:02:29	1.71
    +++ sys/kern/syscalls.c	2000/05/01 11:15:56	1.71.2.1
    @@ -230,7 +230,7 @@
 	    "__semctl",			/* 220 = __semctl */
 	    "semget",			/* 221 = semget */
 	    "semop",			/* 222 = semop */
    -	"semconfig",			/* 223 = semconfig */
    +	"#223",			/* 223 = semconfig */
 	    "msgctl",			/* 224 = msgctl */
 	    "msgget",			/* 225 = msgget */
 	    "msgsnd",			/* 226 = msgsnd */
    --- sys/kern/sysv_ipc.c	2000/02/29 22:58:59	1.13
    +++ sys/kern/sysv_ipc.c	2000/05/01 11:15:56	1.13.2.1
    @@ -107,15 +107,6 @@
     semsys(p, uap)
 	    struct proc *p;
 	    struct semsys_args *uap;
    -{
    -	sysv_nosys(p, "SYSVSEM");
    -	return nosys(p, (struct nosys_args *)uap);
    -};
    -
    -int
    -semconfig(p, uap)
    -	struct proc *p;
    -	struct semconfig_args *uap;
     {
 	    sysv_nosys(p, "SYSVSEM");
 	    return nosys(p, (struct nosys_args *)uap);
    --- sys/kern/sysv_sem.c	2000/04/02 08:47:08	1.24.2.1
    +++ sys/kern/sysv_sem.c	2000/05/01 11:15:56	1.24.2.2
    @@ -26,8 +26,6 @@
     int semget __P((struct proc *p, struct semget_args *uap));
     struct semop_args;
     int semop __P((struct proc *p, struct semop_args *uap));
    -struct semconfig_args;
    -int semconfig __P((struct proc *p, struct semconfig_args *uap));
     #endif

     static struct sem_undo *semu_alloc __P((struct proc *p));
    @@ -38,7 +36,7 @@
     /* XXX casting to (sy_call_t *) is bogus, as usual. */
     static sy_call_t *semcalls[] = {
 	    (sy_call_t *)__semctl, (sy_call_t *)semget,
    -	(sy_call_t *)semop, (sy_call_t *)semconfig
    +	(sy_call_t *)semop
     };

     static int	semtot = 0;
    @@ -47,8 +45,6 @@
     static struct sem_undo *semu_list; 	/* list of active undo structures */
     int	*semu;			/* undo structure pool */

    -static struct proc *semlock_holder = NULL;
    -
     void
     seminit(dummy)
 	    void *dummy;
    @@ -87,64 +83,12 @@
 	    } */ *uap;
     {

    -	while (semlock_holder != NULL && semlock_holder != p)
    -		(void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "semsys", 0);
    -
 	    if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0]))
 		    return (EINVAL);
 	    return ((*semcalls[uap->which])(p, &uap->a2));
     }

     /*
    - * Lock or unlock the entire semaphore facility.
    - *
    - * This will probably eventually evolve into a general purpose semaphore
    - * facility status enquiry mechanism (I don't like the "read /dev/kmem"
    - * approach currently taken by ipcs and the amount of info that we want
    - * to be able to extract for ipcs is probably beyond what the capability
    - * of the getkerninfo facility.
    - *
    - * At the time that the current version of semconfig was written, ipcs is
    - * the only user of the semconfig facility.  It uses it to ensure that the
    - * semaphore facility data structures remain static while it fishes around
    - * in /dev/kmem.
    - */
    -
    -#ifndef _SYS_SYSPROTO_H_
    -struct semconfig_args {
    -	semconfig_ctl_t	flag;
    -};
    -#endif
    -
    -int
    -semconfig(p, uap)
    -	struct proc *p;
    -	struct semconfig_args *uap;
    -{
    -	int eval = 0;
    -
    -	switch (uap->flag) {
    -	case SEM_CONFIG_FREEZE:
    -		semlock_holder = p;
    -		break;
    -
    -	case SEM_CONFIG_THAW:
    -		semlock_holder = NULL;
    -		wakeup((caddr_t)&semlock_holder);
    -		break;
    -
    -	default:
    -		printf("semconfig: unknown flag parameter value (%d) - ignored\n",
    -		    uap->flag);
    -		eval = EINVAL;
    -		break;
    -	}
    -
    -	p->p_retval[0] = 0;
    -	return(eval);
    -}
    -
    -/*
      * Allocate a new sem_undo structure for a process
      * (returns ptr to structure or NULL if no more room)
      */
    @@ -873,17 +817,6 @@
 	    register struct sem_undo **supptr;
 	    int did_something;

    -	/*
    -	 * If somebody else is holding the global semaphore facility lock
    -	 * then sleep until it is released.
    -	 */
    -	while (semlock_holder != NULL && semlock_holder != p) {
    -#ifdef SEM_DEBUG
    -		printf("semaphore facility locked - sleeping ...\n");
    -#endif
    -		(void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "semext", 0);
    -	}
    -
 	    did_something = 0;

 	    /*
    @@ -898,7 +831,7 @@
 	    }

 	    if (suptr == NULL)
    -		goto unlock;
    +		return;

     #ifdef SEM_DEBUG
 	    printf("proc @%08x has undo structure with %d entries\n", p,
    @@ -955,14 +888,4 @@
     #endif
 	    suptr->un_proc = NULL;
 	    *supptr = suptr->un_next;
    -
    -unlock:
    -	/*
    -	 * If the exiting process is holding the global semaphore facility
    -	 * lock then release it.
    -	 */
    -	if (semlock_holder == p) {
    -		semlock_holder = NULL;
    -		wakeup((caddr_t)&semlock_holder);
    -	}
     }

    --- sys/sys/sem.h	1999/12/29 04:24:46	1.20
    +++ sys/sys/sem.h	2000/05/01 11:15:58	1.20.2.1
    @@ -163,13 +163,5 @@
      * Process sem_undo vectors at proc exit.
      */
     void	semexit __P((struct proc *p));
    -
    -/*
    - * Parameters to the semconfig system call
    - */
    -typedef enum {
    -	SEM_CONFIG_FREEZE,	/* Freeze the semaphore facility. */
    -	SEM_CONFIG_THAW		/* Thaw the semaphore facility. */
    -} semconfig_ctl_t;
     #endif /* _KERNEL */

    --- sys/sys/syscall-hide.h	2000/01/19 06:02:31	1.65
    +++ sys/sys/syscall-hide.h	2000/05/01 11:15:58	1.65.2.1
    @@ -191,7 +191,6 @@
     HIDE_BSD(__semctl)
     HIDE_BSD(semget)
     HIDE_BSD(semop)
    -HIDE_BSD(semconfig)
     HIDE_BSD(msgctl)
     HIDE_BSD(msgget)
     HIDE_BSD(msgsnd)
    --- sys/sys/syscall.h	2000/01/19 06:02:31	1.69
    +++ sys/sys/syscall.h	2000/05/01 11:15:59	1.69.2.1
    @@ -196,7 +196,6 @@
     #define	SYS___semctl	220
     #define	SYS_semget	221
     #define	SYS_semop	222
    -#define	SYS_semconfig	223
     #define	SYS_msgctl	224
     #define	SYS_msgget	225
     #define	SYS_msgsnd	226
    --- sys/sys/syscall.mk	2000/01/19 06:07:34	1.23
    +++ sys/sys/syscall.mk	2000/05/01 11:15:59	1.23.2.1
    @@ -148,7 +148,6 @@
 	    __semctl.o \
 	    semget.o \
 	    semop.o \
    -	semconfig.o \
 	    msgctl.o \
 	    msgget.o \
 	    msgsnd.o \
    --- sys/sys/sysproto.h	2000/01/19 06:02:31	1.59
    +++ sys/sys/sysproto.h	2000/05/01 11:16:00	1.59.2.1
    @@ -662,9 +662,6 @@
 	    struct sembuf *	sops;	char sops_[PAD_(struct sembuf *)];
 	    u_int	nsops;	char nsops_[PAD_(u_int)];
     };
    -struct	semconfig_args {
    -	int	flag;	char flag_[PAD_(int)];
    -};
     struct	msgctl_args {
 	    int	msqid;	char msqid_[PAD_(int)];
 	    int	cmd;	char cmd_[PAD_(int)];
    @@ -1158,7 +1155,6 @@
     int	__semctl __P((struct proc *, struct __semctl_args *));
     int	semget __P((struct proc *, struct semget_args *));
     int	semop __P((struct proc *, struct semop_args *));
    -int	semconfig __P((struct proc *, struct semconfig_args *));
     int	msgctl __P((struct proc *, struct msgctl_args *));
     int	msgget __P((struct proc *, struct msgget_args *));
     int	msgsnd __P((struct proc *, struct msgsnd_args *));
    --- usr.bin/ipcs/ipcs.c	1999/12/29 05:05:32	1.12
    +++ usr.bin/ipcs/ipcs.c	2000/05/01 10:51:37	1.12.2.1
    @@ -56,7 +56,6 @@
     struct shminfo	shminfo;
     struct shmid_ds	*shmsegs;

    -int	semconfig __P((int,...));
     void	usage __P((void));

     static struct nlist symbols[] = {
    @@ -420,11 +419,6 @@
 			        seminfo.semaem);
 		    }
 		    if (display & SEMINFO) {
    -			if (semconfig(SEM_CONFIG_FREEZE) != 0) {
    -				perror("semconfig");
    -				fprintf(stderr,
    -				    "Can't lock semaphore facility - winging it...\n");
    -			}
 			    kvm_read(kd, symbols[X_SEMA].n_value, &sema, sizeof(sema));
 			    xsema = malloc(sizeof(struct semid_ds) * seminfo.semmni);
 			    kvm_read(kd, (u_long) sema, xsema, sizeof(struct semid_ds) * seminfo.semmni);
    @@ -470,8 +464,6 @@
 					    printf("\n");
 				    }
 			    }
    -
    -			(void) semconfig(SEM_CONFIG_THAW);

 			    printf("\n");
 		    }