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");
}