summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2006-06-27 18:28:50 +0000
committerjhb <jhb@FreeBSD.org>2006-06-27 18:28:50 +0000
commitdff69a853ef2a388e03aefe1ccf2d04909ef8405 (patch)
treecb6f2ab0b9fc8b11fc6fae3e937411e62c1f6d28 /sys
parente3f66d999d6f25cdbeb14c8bf651286bd6c7aff3 (diff)
downloadFreeBSD-src-dff69a853ef2a388e03aefe1ccf2d04909ef8405.zip
FreeBSD-src-dff69a853ef2a388e03aefe1ccf2d04909ef8405.tar.gz
- Add a kern_semctl() helper function for __semctl(). It accepts a pointer
to a copied-in copy of the 'union semun' and a uioseg to indicate which memory space the 'buf' pointer of the union points to. This is then used in linux_semctl() and svr4_sys_semctl() to eliminate use of the stackgap. - Mark linux_ipc() and svr4_sys_semsys() MPSAFE.
Diffstat (limited to 'sys')
-rw-r--r--sys/amd64/linux32/syscalls.master2
-rw-r--r--sys/compat/linux/linux_ipc.c64
-rw-r--r--sys/compat/svr4/svr4_ipc.c115
-rw-r--r--sys/compat/svr4/syscalls.master2
-rw-r--r--sys/i386/linux/syscalls.master2
-rw-r--r--sys/kern/sysv_sem.c116
-rw-r--r--sys/sys/syscallsubr.h3
7 files changed, 142 insertions, 162 deletions
diff --git a/sys/amd64/linux32/syscalls.master b/sys/amd64/linux32/syscalls.master
index 9af73d9..99c9cb2 100644
--- a/sys/amd64/linux32/syscalls.master
+++ b/sys/amd64/linux32/syscalls.master
@@ -212,7 +212,7 @@
struct l_rusage *rusage); }
115 AUE_SWAPOFF MSTD { int linux_swapoff(void); }
116 AUE_NULL MSTD { int linux_sysinfo(struct l_sysinfo *info); }
-117 AUE_NULL STD { int linux_ipc(l_uint what, l_int arg1, \
+117 AUE_NULL MSTD { int linux_ipc(l_uint what, l_int arg1, \
l_int arg2, l_int arg3, void *ptr, \
l_long arg5); }
118 AUE_FSYNC MNOPROTO { int fsync(int fd); }
diff --git a/sys/compat/linux/linux_ipc.c b/sys/compat/linux/linux_ipc.c
index 0989847..2e6dabf 100644
--- a/sys/compat/linux/linux_ipc.c
+++ b/sys/compat/linux/linux_ipc.c
@@ -491,69 +491,56 @@ int
linux_semctl(struct thread *td, struct linux_semctl_args *args)
{
struct l_semid_ds linux_semid;
- struct __semctl_args /* {
- int semid;
- int semnum;
- int cmd;
- union semun *arg;
- } */ bsd_args;
struct l_seminfo linux_seminfo;
- int error;
- union semun *unptr;
- caddr_t sg;
-
- sg = stackgap_init();
-
- /* Make sure the arg parameter can be copied in. */
- unptr = stackgap_alloc(&sg, sizeof(union semun));
- bcopy(&args->arg, unptr, sizeof(union semun));
-
- bsd_args.semid = args->semid;
- bsd_args.semnum = args->semnum;
- bsd_args.arg = unptr;
+ struct semid_ds semid;
+ union semun semun;
+ int cmd, error;
switch (args->cmd & ~LINUX_IPC_64) {
case LINUX_IPC_RMID:
- bsd_args.cmd = IPC_RMID;
+ cmd = IPC_RMID;
break;
case LINUX_GETNCNT:
- bsd_args.cmd = GETNCNT;
+ cmd = GETNCNT;
break;
case LINUX_GETPID:
- bsd_args.cmd = GETPID;
+ cmd = GETPID;
break;
case LINUX_GETVAL:
- bsd_args.cmd = GETVAL;
+ cmd = GETVAL;
break;
case LINUX_GETZCNT:
- bsd_args.cmd = GETZCNT;
+ cmd = GETZCNT;
break;
case LINUX_SETVAL:
- bsd_args.cmd = SETVAL;
+ cmd = SETVAL;
+ semun.val = args->arg.val;
break;
case LINUX_IPC_SET:
- bsd_args.cmd = IPC_SET;
+ cmd = IPC_SET;
error = linux_semid_pullup(args->cmd & LINUX_IPC_64,
&linux_semid, (caddr_t)PTRIN(args->arg.buf));
if (error)
return (error);
- unptr->buf = stackgap_alloc(&sg, sizeof(struct semid_ds));
- linux_to_bsd_semid_ds(&linux_semid, unptr->buf);
- return __semctl(td, &bsd_args);
+ linux_to_bsd_semid_ds(&linux_semid, &semid);
+ semun.buf = &semid;
+ return kern_semctl(td, args->semid, args->semnum, cmd, &semun,
+ UIO_SYSSPACE);
case LINUX_IPC_STAT:
case LINUX_SEM_STAT:
if((args->cmd & ~LINUX_IPC_64) == LINUX_IPC_STAT)
- bsd_args.cmd = IPC_STAT;
+ cmd = IPC_STAT;
else
- bsd_args.cmd = SEM_STAT;
- unptr->buf = stackgap_alloc(&sg, sizeof(struct semid_ds));
- error = __semctl(td, &bsd_args);
+ cmd = SEM_STAT;
+ semun.buf = &semid;
+ error = kern_semctl(td, args->semid, args->semnum, cmd, &semun,
+ UIO_SYSSPACE);
if (error)
- return error;
- td->td_retval[0] = (bsd_args.cmd == SEM_STAT) ?
- IXSEQ_TO_IPCID(bsd_args.semid, unptr->buf->sem_perm) :
+ return (error);
+ td->td_retval[0] = (cmd == SEM_STAT) ?
+ IXSEQ_TO_IPCID(args->semid, semid.sem_perm) :
0;
- bsd_to_linux_semid_ds(unptr->buf, &linux_semid);
+ bsd_to_linux_semid_ds(&semid, &linux_semid);
return (linux_semid_pushdown(args->cmd & LINUX_IPC_64,
&linux_semid, (caddr_t)PTRIN(args->arg.buf)));
case LINUX_IPC_INFO:
@@ -580,7 +567,8 @@ linux_semctl(struct thread *td, struct linux_semctl_args *args)
args->cmd & ~LINUX_IPC_64);
return EINVAL;
}
- return __semctl(td, &bsd_args);
+ return kern_semctl(td, args->semid, args->semnum, cmd, &semun,
+ UIO_USERSPACE);
}
int
diff --git a/sys/compat/svr4/svr4_ipc.c b/sys/compat/svr4/svr4_ipc.c
index 0b6acbf..9eeaf24 100644
--- a/sys/compat/svr4/svr4_ipc.c
+++ b/sys/compat/svr4/svr4_ipc.c
@@ -105,8 +105,6 @@ static void bsd_to_svr4_semid_ds(const struct semid_ds *,
struct svr4_semid_ds *);
static void svr4_to_bsd_semid_ds(const struct svr4_semid_ds *,
struct semid_ds *);
-static int svr4_setsemun(caddr_t *sgp, union semun **argp,
- union semun *usp);
static int svr4_semop(struct thread *, void *);
static int svr4_semget(struct thread *, void *);
static int svr4_semctl(struct thread *, void *);
@@ -194,16 +192,6 @@ svr4_to_bsd_semid_ds(sds, bds)
bds->sem_pad2 = sds->sem_pad2;
}
-static int
-svr4_setsemun(sgp, argp, usp)
- caddr_t *sgp;
- union semun **argp;
- union semun *usp;
-{
- *argp = stackgap_alloc(sgp, sizeof(union semun));
- return copyout((caddr_t)usp, *argp, sizeof(union semun));
-}
-
struct svr4_sys_semctl_args {
int what;
int semid;
@@ -217,108 +205,71 @@ svr4_semctl(td, v)
struct thread *td;
void *v;
{
- int error;
struct svr4_sys_semctl_args *uap = v;
- struct __semctl_args ap;
struct svr4_semid_ds ss;
- struct semid_ds bs, *bsp;
- caddr_t sg = stackgap_init();
-
- ap.semid = uap->semid;
- ap.semnum = uap->semnum;
+ struct semid_ds bs;
+ union semun semun;
+ int cmd, error;
switch (uap->cmd) {
case SVR4_SEM_GETZCNT:
+ cmd = GETZCNT;
+ break;
+
case SVR4_SEM_GETNCNT:
+ cmd = GETNCNT;
+ break;
+
case SVR4_SEM_GETPID:
+ cmd = GETPID;
+ break;
+
case SVR4_SEM_GETVAL:
- switch (uap->cmd) {
- case SVR4_SEM_GETZCNT:
- ap.cmd = GETZCNT;
- break;
- case SVR4_SEM_GETNCNT:
- ap.cmd = GETNCNT;
- break;
- case SVR4_SEM_GETPID:
- ap.cmd = GETPID;
- break;
- case SVR4_SEM_GETVAL:
- ap.cmd = GETVAL;
- break;
- }
- return __semctl(td, &ap);
+ cmd = GETVAL;
+ break;
case SVR4_SEM_SETVAL:
- error = svr4_setsemun(&sg, &ap.arg, &uap->arg);
- if (error)
- return error;
- ap.cmd = SETVAL;
- return __semctl(td, &ap);
+ cmd = SETVAL;
+ break;
case SVR4_SEM_GETALL:
- error = svr4_setsemun(&sg, &ap.arg, &uap->arg);
- if (error)
- return error;
- ap.cmd = GETVAL;
- return __semctl(td, &ap);
+ cmd = GETVAL;
+ break;
case SVR4_SEM_SETALL:
- error = svr4_setsemun(&sg, &ap.arg, &uap->arg);
- if (error)
- return error;
- ap.cmd = SETVAL;
- return __semctl(td, &ap);
+ cmd = SETVAL;
+ break;
case SVR4_IPC_STAT:
- ap.cmd = IPC_STAT;
- bsp = stackgap_alloc(&sg, sizeof(bs));
- error = svr4_setsemun(&sg, &ap.arg,
- (union semun *)&bsp);
+ cmd = IPC_STAT;
+ semun.buf = &bs;
+ error = kern_semctl(td, uap->semid, uap->semnum, cmd, &semun,
+ UIO_SYSSPACE);
if (error)
- return error;
- if ((error = __semctl(td, &ap)) != 0)
- return error;
- error = copyin((caddr_t)bsp, (caddr_t)&bs, sizeof(bs));
- if (error)
return error;
bsd_to_svr4_semid_ds(&bs, &ss);
return copyout(&ss, uap->arg.buf, sizeof(ss));
case SVR4_IPC_SET:
- ap.cmd = IPC_SET;
- bsp = stackgap_alloc(&sg, sizeof(bs));
- error = svr4_setsemun(&sg, &ap.arg,
- (union semun *)&bsp);
- if (error)
- return error;
+ cmd = IPC_SET;
error = copyin(uap->arg.buf, (caddr_t) &ss, sizeof ss);
if (error)
return error;
svr4_to_bsd_semid_ds(&ss, &bs);
- error = copyout(&bs, bsp, sizeof(bs));
- if (error)
- return error;
- return __semctl(td, &ap);
+ semun.buf = &bs;
+ return kern_semctl(td, uap->semid, uap->semnum, cmd, &semun,
+ UIO_SYSSPACE);
case SVR4_IPC_RMID:
- ap.cmd = IPC_RMID;
- bsp = stackgap_alloc(&sg, sizeof(bs));
- error = svr4_setsemun(&sg, &ap.arg,
- (union semun *)&bsp);
- if (error)
- return error;
- error = copyin(uap->arg.buf, &ss, sizeof ss);
- if (error)
- return error;
- svr4_to_bsd_semid_ds(&ss, &bs);
- error = copyout(&bs, bsp, sizeof(bs));
- if (error)
- return error;
- return __semctl(td, &ap);
+ cmd = IPC_RMID;
+ break;
default:
return EINVAL;
}
+
+ return kern_semctl(td, uap->semid, uap->semnum, cmd, &uap->arg,
+ UIO_USERSPACE);
}
struct svr4_sys_semget_args {
diff --git a/sys/compat/svr4/syscalls.master b/sys/compat/svr4/syscalls.master
index 4086a36..944ec75 100644
--- a/sys/compat/svr4/syscalls.master
+++ b/sys/compat/svr4/syscalls.master
@@ -102,7 +102,7 @@
51 AUE_NULL UNIMPL acct
52 AUE_NULL STD { int svr4_sys_shmsys(int what, int a2, \
int a3, int a4, int a5); }
-53 AUE_NULL STD { int svr4_sys_semsys(int what, int a2, \
+53 AUE_NULL MSTD { int svr4_sys_semsys(int what, int a2, \
int a3, int a4, int a5); }
54 AUE_NULL STD { int svr4_sys_ioctl(int fd, u_long com, \
caddr_t data); }
diff --git a/sys/i386/linux/syscalls.master b/sys/i386/linux/syscalls.master
index 285cb66..1855e98 100644
--- a/sys/i386/linux/syscalls.master
+++ b/sys/i386/linux/syscalls.master
@@ -213,7 +213,7 @@
struct l_rusage *rusage); }
115 AUE_SWAPOFF MSTD { int linux_swapoff(void); }
116 AUE_NULL MSTD { int linux_sysinfo(struct l_sysinfo *info); }
-117 AUE_NULL STD { int linux_ipc(l_uint what, l_int arg1, \
+117 AUE_NULL MSTD { int linux_ipc(l_uint what, l_int arg1, \
l_int arg2, l_int arg3, void *ptr, \
l_long arg5); }
118 AUE_FSYNC MNOPROTO { int fsync(int fd); }
diff --git a/sys/kern/sysv_sem.c b/sys/kern/sysv_sem.c
index 12e47aa..a842d32 100644
--- a/sys/kern/sysv_sem.c
+++ b/sys/kern/sysv_sem.c
@@ -53,8 +53,10 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/sem.h>
#include <sys/syscall.h>
+#include <sys/syscallsubr.h>
#include <sys/sysent.h>
#include <sys/sysctl.h>
+#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/jail.h>
#include <sys/mac.h>
@@ -554,12 +556,35 @@ __semctl(td, uap)
struct thread *td;
struct __semctl_args *uap;
{
- int semid = uap->semid;
- int semnum = uap->semnum;
- int cmd = uap->cmd;
- u_short *array;
- union semun *arg = uap->arg;
union semun real_arg;
+ union semun *arg;
+ int error;
+
+ switch (uap->cmd) {
+ case SEM_STAT:
+ case IPC_SET:
+ case IPC_STAT:
+ case GETALL:
+ case SETVAL:
+ case SETALL:
+ error = copyin(uap->arg, &real_arg, sizeof(real_arg));
+ if (error)
+ return (error);
+ arg = &real_arg;
+ break;
+ default:
+ arg = NULL;
+ break;
+ }
+ return (kern_semctl(td, uap->semid, uap->semnum, uap->cmd, arg,
+ UIO_USERSPACE));
+}
+
+int
+kern_semctl(struct thread *td, int semid, int semnum, int cmd, union semun *arg,
+ enum uio_seg bufseg)
+{
+ u_short *array;
struct ucred *cred = td->td_ucred;
int i, rval, error;
struct semid_ds sbuf;
@@ -578,8 +603,6 @@ __semctl(td, uap)
case SEM_STAT:
if (semid < 0 || semid >= seminfo.semmni)
return (EINVAL);
- if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
- return (error);
semakptr = &sema[semid];
sema_mtxp = &sema_mtx[semid];
mtx_lock(sema_mtxp);
@@ -598,8 +621,11 @@ __semctl(td, uap)
}
#endif
mtx_unlock(sema_mtxp);
- error = copyout(&semakptr->u, real_arg.buf,
- sizeof(struct semid_ds));
+ if (bufseg == UIO_USERSPACE)
+ error = copyout(&semakptr->u, arg->buf,
+ sizeof(struct semid_ds));
+ else
+ bcopy(&semakptr->u, arg->buf, sizeof(struct semid_ds));
rval = IXSEQ_TO_IPCID(semid, semakptr->u.sem_perm);
if (error == 0)
td->td_retval[0] = rval;
@@ -629,7 +655,7 @@ __semctl(td, uap)
switch (cmd) {
case IPC_RMID:
mtx_lock(sema_mtxp);
- if ((error = semvalid(uap->semid, semakptr)) != 0)
+ if ((error = semvalid(semid, semakptr)) != 0)
goto done2;
if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_M)))
goto done2;
@@ -654,12 +680,14 @@ __semctl(td, uap)
break;
case IPC_SET:
- if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
- goto done2;
- if ((error = copyin(real_arg.buf, &sbuf, sizeof(sbuf))) != 0)
- goto done2;
+ if (bufseg == UIO_USERSPACE) {
+ error = copyin(arg->buf, &sbuf, sizeof(sbuf));
+ if (error)
+ goto done2;
+ } else
+ bcopy(arg->buf, &sbuf, sizeof(sbuf));
mtx_lock(sema_mtxp);
- if ((error = semvalid(uap->semid, semakptr)) != 0)
+ if ((error = semvalid(semid, semakptr)) != 0)
goto done2;
if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_M)))
goto done2;
@@ -671,22 +699,23 @@ __semctl(td, uap)
break;
case IPC_STAT:
- if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
- goto done2;
mtx_lock(sema_mtxp);
- if ((error = semvalid(uap->semid, semakptr)) != 0)
+ if ((error = semvalid(semid, semakptr)) != 0)
goto done2;
if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
goto done2;
sbuf = semakptr->u;
mtx_unlock(sema_mtxp);
- error = copyout(&semakptr->u, real_arg.buf,
- sizeof(struct semid_ds));
+ if (bufseg == UIO_USERSPACE)
+ error = copyout(&semakptr->u, arg->buf,
+ sizeof(struct semid_ds));
+ else
+ bcopy(&semakptr->u, arg->buf, sizeof(struct semid_ds));
break;
case GETNCNT:
mtx_lock(sema_mtxp);
- if ((error = semvalid(uap->semid, semakptr)) != 0)
+ if ((error = semvalid(semid, semakptr)) != 0)
goto done2;
if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
goto done2;
@@ -699,7 +728,7 @@ __semctl(td, uap)
case GETPID:
mtx_lock(sema_mtxp);
- if ((error = semvalid(uap->semid, semakptr)) != 0)
+ if ((error = semvalid(semid, semakptr)) != 0)
goto done2;
if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
goto done2;
@@ -712,7 +741,7 @@ __semctl(td, uap)
case GETVAL:
mtx_lock(sema_mtxp);
- if ((error = semvalid(uap->semid, semakptr)) != 0)
+ if ((error = semvalid(semid, semakptr)) != 0)
goto done2;
if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
goto done2;
@@ -724,25 +753,31 @@ __semctl(td, uap)
break;
case GETALL:
- if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
- goto done2;
+ /*
+ * Given the fact that this copies out a variable amount
+ * of data into userland, I don't see any feasible way of
+ * doing this with a kmem copy of the userland buffer.
+ */
+ if (bufseg == UIO_SYSSPACE)
+ return (EINVAL);
+
array = malloc(sizeof(*array) * semakptr->u.sem_nsems, M_TEMP,
M_WAITOK);
mtx_lock(sema_mtxp);
- if ((error = semvalid(uap->semid, semakptr)) != 0)
+ if ((error = semvalid(semid, semakptr)) != 0)
goto done2;
if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
goto done2;
for (i = 0; i < semakptr->u.sem_nsems; i++)
array[i] = semakptr->u.sem_base[i].semval;
mtx_unlock(sema_mtxp);
- error = copyout(array, real_arg.array,
- i * sizeof(real_arg.array[0]));
+ error = copyout(array, arg->array,
+ i * sizeof(arg->array[0]));
break;
case GETZCNT:
mtx_lock(sema_mtxp);
- if ((error = semvalid(uap->semid, semakptr)) != 0)
+ if ((error = semvalid(semid, semakptr)) != 0)
goto done2;
if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R)))
goto done2;
@@ -754,10 +789,8 @@ __semctl(td, uap)
break;
case SETVAL:
- if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
- goto done2;
mtx_lock(sema_mtxp);
- if ((error = semvalid(uap->semid, semakptr)) != 0)
+ if ((error = semvalid(semid, semakptr)) != 0)
goto done2;
if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_W)))
goto done2;
@@ -765,11 +798,11 @@ __semctl(td, uap)
error = EINVAL;
goto done2;
}
- if (real_arg.val < 0 || real_arg.val > seminfo.semvmx) {
+ if (arg->val < 0 || arg->val > seminfo.semvmx) {
error = ERANGE;
goto done2;
}
- semakptr->u.sem_base[semnum].semval = real_arg.val;
+ semakptr->u.sem_base[semnum].semval = arg->val;
SEMUNDO_LOCK();
semundo_clear(semid, semnum);
SEMUNDO_UNLOCK();
@@ -777,20 +810,25 @@ __semctl(td, uap)
break;
case SETALL:
+ /*
+ * Given the fact that this copies in variable amounts of
+ * userland data in a loop, I don't see any feasible way
+ * of doing this with a kmem copy of the userland buffer.
+ */
+ if (bufseg == UIO_SYSSPACE)
+ return (EINVAL);
mtx_lock(sema_mtxp);
raced:
- if ((error = semvalid(uap->semid, semakptr)) != 0)
+ if ((error = semvalid(semid, semakptr)) != 0)
goto done2;
count = semakptr->u.sem_nsems;
mtx_unlock(sema_mtxp);
- if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
- goto done2;
array = malloc(sizeof(*array) * count, M_TEMP, M_WAITOK);
- error = copyin(real_arg.array, array, count * sizeof(*array));
+ error = copyin(arg->array, array, count * sizeof(*array));
if (error)
break;
mtx_lock(sema_mtxp);
- if ((error = semvalid(uap->semid, semakptr)) != 0)
+ if ((error = semvalid(semid, semakptr)) != 0)
goto done2;
/* we could have raced? */
if (count != semakptr->u.sem_nsems) {
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
index 7329f6c..d4f818b 100644
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -41,6 +41,7 @@ struct msghdr;
struct msqid_ds;
struct rlimit;
struct rusage;
+union semun;
struct sockaddr;
struct stat;
struct kevent;
@@ -123,6 +124,8 @@ int kern_rename(struct thread *td, char *from, char *to,
int kern_rmdir(struct thread *td, char *path, enum uio_seg pathseg);
int kern_sched_rr_get_interval(struct thread *td, pid_t pid,
struct timespec *ts);
+int kern_semctl(struct thread *td, int semid, int semnum, int cmd,
+ union semun *arg, enum uio_seg bufseg);
int kern_select(struct thread *td, int nd, fd_set *fd_in, fd_set *fd_ou,
fd_set *fd_ex, struct timeval *tvp);
int kern_sendfile(struct thread *td, struct sendfile_args *uap,
OpenPOWER on IntegriCloud