getgid.o getpid.o geteuid.o getuid.o gettimeofday.o getvfsstat.o \
init.o kernel_utils.o kill.o link.o loadname.o lseek.o _mcontext.o \
minix_rs.o mknod.o mmap.o nanosleep.o open.o pread.o pwrite.o read.o \
- sbrk.o select.o sem.o setuid.o shmctl.o sigprocmask.o stack_utils.o \
+ sbrk.o select.o setuid.o sigprocmask.o stack_utils.o \
stat.o stime.o svrctl.o syscall.o __sysctl.o _ucontext.o umask.o \
unlink.o wait4.o write.o
${f} ${f:C/\.o/.bc/}: ${LIBMINIXCDIR}/sys/${f:C/\.o/.c/}
#include <minix/const.h>
#include <minix/type.h>
#include <minix/syslib.h>
+#include <minix/rmib.h>
#include <sys/types.h>
#include <sys/param.h>
int do_shmat(message *);
int do_shmdt(message *);
int do_shmctl(message *);
+int get_shm_mib_info(struct rmib_oldp *);
int is_shm_nil(void);
void update_refcount_and_destroy(void);
int do_semget(message *);
int do_semctl(message *);
int do_semop(message *);
+int get_sem_mib_info(struct rmib_oldp *);
int is_sem_nil(void);
void sem_process_event(endpoint_t, int);
/* utility.c */
int check_perm(struct ipc_perm *, endpoint_t, int);
+void prepare_mib_perm(struct ipc_perm_sysctl *, const struct ipc_perm *);
CALL(IPC_SEMOP) = do_semop,
};
+/*
+ * Remote MIB implementation of CTL_KERN KERN_SYSVIPC KERN_SYSVIPC_INFO. This
+ * function handles all queries on the "kern.ipc.sysvipc_info" sysctl(2) node.
+ */
+static ssize_t
+kern_ipc_info(struct rmib_call * call, struct rmib_node * node __unused,
+ struct rmib_oldp * oldp, struct rmib_newp * newp __unused)
+{
+
+ if (call->call_namelen != 1)
+ return EINVAL;
+
+ /*
+ * Let each IPC submodule provide information through it own function.
+ * An important security note: unlike IPC_STAT and the like, access to
+ * the sysvipc_info node does not require root privileges. That is: on
+ * NetBSD, any user can get a full listing of all IPC objects in the
+ * system. We therefore do not perform any security check here.
+ */
+ switch (call->call_name[0]) {
+ case KERN_SYSVIPC_SEM_INFO:
+ return get_sem_mib_info(oldp);
+
+ case KERN_SYSVIPC_SHM_INFO:
+ return get_shm_mib_info(oldp);
+
+ default:
+ return EOPNOTSUPP;
+ }
+}
+
+/* The CTL_KERN KERN_SYSVIPC subtree. */
+static struct rmib_node kern_ipc_table[] = {
+/* 1*/ [KERN_SYSVIPC_INFO] = RMIB_FUNC(RMIB_RO | CTLTYPE_NODE, 0,
+ kern_ipc_info, "sysvipc_info",
+ "System V style IPC information"),
+/* 2*/ [KERN_SYSVIPC_MSG] = RMIB_INT(RMIB_RO, 0, "sysvmsg", "System V "
+ "style message support available"),
+/* 3*/ [KERN_SYSVIPC_SEM] = RMIB_INT(RMIB_RO, 1, "sysvsem", "System V "
+ "style semaphore support available"),
+/* 4*/ [KERN_SYSVIPC_SHM] = RMIB_INT(RMIB_RO, 1, "sysvshm", "System V "
+ "style shared memory support available"),
+/* 5*/ /* KERN_SYSVIPC_SHMMAX: not yet supported */
+/* 6*/ /* KERN_SYSVIPC_SHMMNI: not yet supported */
+/* 7*/ /* KERN_SYSVIPC_SHMSEG: not yet supported */
+/* 8*/ /* KERN_SYSVIPC_SHMMAXPGS: not yet supported */
+/* 9*/ /* KERN_SYSVIPC_SHMUSEPHYS: not yet supported */
+ /* In addition, NetBSD has a number of dynamic nodes here. */
+};
+
+/* The CTL_KERN KERN_SYSVIPC node. */
+static struct rmib_node kern_ipc_node =
+ RMIB_NODE(RMIB_RO, kern_ipc_table, "ipc", "SysV IPC options");
+
/*
* Initialize the IPC server.
*/
static int
sef_cb_init_fresh(int type __unused, sef_init_info_t * info __unused)
{
+ const int mib[] = { CTL_KERN, KERN_SYSVIPC };
+ int r;
+
+ /*
+ * Register our own "kern.ipc" subtree with the MIB service.
+ *
+ * This call only returns local failures. Remote failures (in the MIB
+ * service) are silently ignored. So, we can safely panic on failure.
+ */
+ if ((r = rmib_register(mib, __arraycount(mib), &kern_ipc_node)) != OK)
+ panic("unable to register remote MIB tree: %d", r);
- /* Nothing to do. */
return OK;
}
* Check if there are still IPC keys around. If not, we can safely
* exit immediately. Otherwise, warn the system administrator.
*/
- if (is_sem_nil() && is_shm_nil())
+ if (is_sem_nil() && is_shm_nil()) {
+ rmib_deregister(&kern_ipc_node);
+
sef_exit(0);
+ }
printf("IPC: exit with unclean state\n");
}
continue;
}
+ /* Remote MIB messages from MIB are handled separately too. */
+ if (m.m_source == MIB_PROC_NR) {
+ rmib_process(&m, ipc_status);
+
+ continue;
+ }
+
/* Dispatch the request. */
call_index = (unsigned int)(m.m_type - IPC_BASE);
} while (woken_up);
}
+/*
+ * Fill a seminfo structure with actual information. The information returned
+ * depends on the given command, which may be either IPC_INFO or SEM_INFO.
+ */
+static void
+fill_seminfo(struct seminfo * sinfo, int cmd)
+{
+ unsigned int i;
+
+ assert(cmd == IPC_INFO || cmd == SEM_INFO);
+
+ memset(sinfo, 0, sizeof(*sinfo));
+
+ sinfo->semmap = SEMMNI;
+ sinfo->semmni = SEMMNI;
+ sinfo->semmns = SEMMNI * SEMMSL;
+ sinfo->semmnu = 0; /* TODO: support for SEM_UNDO */
+ sinfo->semmsl = SEMMSL;
+ sinfo->semopm = SEMOPM;
+ sinfo->semume = 0; /* TODO: support for SEM_UNDO */
+ if (cmd == SEM_INFO) {
+ /*
+ * For SEM_INFO the semusz field is expected to contain the
+ * number of semaphore sets currently in use.
+ */
+ sinfo->semusz = sem_list_nr;
+ } else
+ sinfo->semusz = 0; /* TODO: support for SEM_UNDO */
+ sinfo->semvmx = SEMVMX;
+ if (cmd == SEM_INFO) {
+ /*
+ * For SEM_INFO the semaem field is expected to contain
+ * the total number of allocated semaphores.
+ */
+ for (i = 0; i < sem_list_nr; i++)
+ sinfo->semaem += sem_list[i].semid_ds.sem_nsems;
+ } else
+ sinfo->semaem = 0; /* TODO: support for SEM_UNDO */
+}
+
/*
* Implementation of the semctl(2) system call.
*/
break;
case IPC_INFO:
case SEM_INFO:
- memset(&sinfo, 0, sizeof(sinfo));
- sinfo.semmap = SEMMNI;
- sinfo.semmni = SEMMNI;
- sinfo.semmns = SEMMNI * SEMMSL;
- sinfo.semmnu = 0; /* TODO: support for SEM_UNDO */
- sinfo.semmsl = SEMMSL;
- sinfo.semopm = SEMOPM;
- sinfo.semume = 0; /* TODO: support for SEM_UNDO */
- if (cmd == SEM_INFO) {
- /*
- * For SEM_INFO the semusz field is expected to contain
- * the number of semaphore sets currently in use.
- */
- sinfo.semusz = sem_list_nr;
- } else
- sinfo.semusz = 0; /* TODO: support for SEM_UNDO */
- sinfo.semvmx = SEMVMX;
- if (cmd == SEM_INFO) {
- /*
- * For SEM_INFO the semaem field is expected to contain
- * the total number of allocated semaphores.
- */
- for (i = 0; i < sem_list_nr; i++)
- sinfo.semaem += sem_list[i].semid_ds.sem_nsems;
- } else
- sinfo.semaem = 0; /* TODO: support for SEM_UNDO */
+ fill_seminfo(&sinfo, cmd);
if ((r = sys_datacopy(SELF, (vir_bytes)&sinfo, m->m_source,
opt, sizeof(sinfo))) != OK)
return r;
}
+/*
+ * Return semaphore information for a remote MIB call on the sysvipc_info node
+ * in the kern.ipc subtree. The particular semantics of this call are tightly
+ * coupled to the implementation of the ipcs(1) userland utility.
+ */
+ssize_t
+get_sem_mib_info(struct rmib_oldp * oldp)
+{
+ struct sem_sysctl_info semsi;
+ struct semid_ds *semds;
+ unsigned int i;
+ ssize_t r, off;
+
+ off = 0;
+
+ fill_seminfo(&semsi.seminfo, IPC_INFO);
+
+ /*
+ * As a hackish exception, the requested size may imply that just
+ * general information is to be returned, without throwing an ENOMEM
+ * error because there is no space for full output.
+ */
+ if (rmib_getoldlen(oldp) == sizeof(semsi.seminfo))
+ return rmib_copyout(oldp, 0, &semsi.seminfo,
+ sizeof(semsi.seminfo));
+
+ /*
+ * ipcs(1) blindly expects the returned array to be of size
+ * seminfo.semmni, using the SEM_ALLOC mode flag to see whether each
+ * entry is valid. If we return a smaller size, ipcs(1) will access
+ * arbitrary memory.
+ */
+ assert(semsi.seminfo.semmni > 0);
+
+ if (oldp == NULL)
+ return sizeof(semsi) + sizeof(semsi.semids[0]) *
+ (semsi.seminfo.semmni - 1);
+
+ /*
+ * Copy out entries one by one. For the first entry, copy out the
+ * entire "semsi" structure. For subsequent entries, reuse the single
+ * embedded 'semids' element of "semsi" and copy out only that element.
+ */
+ for (i = 0; i < (unsigned int)semsi.seminfo.semmni; i++) {
+ semds = &sem_list[i].semid_ds;
+
+ memset(&semsi.semids[0], 0, sizeof(semsi.semids[0]));
+ if (i < sem_list_nr && (semds->sem_perm.mode & SEM_ALLOC)) {
+ prepare_mib_perm(&semsi.semids[0].sem_perm,
+ &semds->sem_perm);
+ semsi.semids[0].sem_nsems = semds->sem_nsems;
+ semsi.semids[0].sem_otime = semds->sem_otime;
+ semsi.semids[0].sem_ctime = semds->sem_ctime;
+ }
+
+ if (off == 0)
+ r = rmib_copyout(oldp, off, &semsi, sizeof(semsi));
+ else
+ r = rmib_copyout(oldp, off, &semsi.semids[0],
+ sizeof(semsi.semids[0]));
+
+ if (r < 0)
+ return r;
+ off += r;
+ }
+
+ return off;
+}
+
/*
* Return TRUE iff no semaphore sets are allocated.
*/
return OK;
}
+/*
+ * Fill a shminfo structure with actual information.
+ */
+static void
+fill_shminfo(struct shminfo * sinfo)
+{
+
+ memset(sinfo, 0, sizeof(*sinfo));
+
+ sinfo->shmmax = (unsigned long)-1;
+ sinfo->shmmin = 1;
+ sinfo->shmmni = __arraycount(shm_list);
+ sinfo->shmseg = (unsigned long)-1;
+ sinfo->shmall = (unsigned long)-1;
+}
+
int
do_shmctl(message * m)
{
update_refcount_and_destroy();
break;
case IPC_INFO:
- memset(&sinfo, 0, sizeof(sinfo));
- sinfo.shmmax = (unsigned long) -1;
- sinfo.shmmin = 1;
- sinfo.shmmni = __arraycount(shm_list);
- sinfo.shmseg = (unsigned long) -1;
- sinfo.shmall = (unsigned long) -1;
+ fill_shminfo(&sinfo);
if ((r = sys_datacopy(SELF, (vir_bytes)&sinfo, m->m_source,
buf, sizeof(sinfo))) != OK)
return r;
return OK;
}
+/*
+ * Return shared memory information for a remote MIB call on the sysvipc_info
+ * node in the kern.ipc subtree. The particular semantics of this call are
+ * tightly coupled to the implementation of the ipcs(1) userland utility.
+ */
+ssize_t
+get_shm_mib_info(struct rmib_oldp * oldp)
+{
+ struct shm_sysctl_info shmsi;
+ struct shmid_ds *shmds;
+ unsigned int i;
+ ssize_t r, off;
+
+ off = 0;
+
+ fill_shminfo(&shmsi.shminfo);
+
+ /*
+ * As a hackish exception, the requested size may imply that just
+ * general information is to be returned, without throwing an ENOMEM
+ * error because there is no space for full output.
+ */
+ if (rmib_getoldlen(oldp) == sizeof(shmsi.shminfo))
+ return rmib_copyout(oldp, 0, &shmsi.shminfo,
+ sizeof(shmsi.shminfo));
+
+ /*
+ * ipcs(1) blindly expects the returned array to be of size
+ * shminfo.shmmni, using the SHMSEG_ALLOCATED (aka SHM_ALLOC) mode flag
+ * to see whether each entry is valid. If we return a smaller size,
+ * ipcs(1) will access arbitrary memory.
+ */
+ assert(shmsi.shminfo.shmmni > 0);
+
+ if (oldp == NULL)
+ return sizeof(shmsi) + sizeof(shmsi.shmids[0]) *
+ (shmsi.shminfo.shmmni - 1);
+
+ /*
+ * Copy out entries one by one. For the first entry, copy out the
+ * entire "shmsi" structure. For subsequent entries, reuse the single
+ * embedded 'shmids' element of "shmsi" and copy out only that element.
+ */
+ for (i = 0; i < shmsi.shminfo.shmmni; i++) {
+ shmds = &shm_list[i].shmid_ds;
+
+ memset(&shmsi.shmids[0], 0, sizeof(shmsi.shmids[0]));
+ if (i < shm_list_nr && (shmds->shm_perm.mode & SHM_ALLOC)) {
+ prepare_mib_perm(&shmsi.shmids[0].shm_perm,
+ &shmds->shm_perm);
+ shmsi.shmids[0].shm_segsz = shmds->shm_segsz;
+ shmsi.shmids[0].shm_lpid = shmds->shm_lpid;
+ shmsi.shmids[0].shm_cpid = shmds->shm_cpid;
+ shmsi.shmids[0].shm_atime = shmds->shm_atime;
+ shmsi.shmids[0].shm_dtime = shmds->shm_dtime;
+ shmsi.shmids[0].shm_ctime = shmds->shm_ctime;
+ shmsi.shmids[0].shm_nattch = shmds->shm_nattch;
+ }
+
+ if (off == 0)
+ r = rmib_copyout(oldp, off, &shmsi, sizeof(shmsi));
+ else
+ r = rmib_copyout(oldp, off, &shmsi.shmids[0],
+ sizeof(shmsi.shmids[0]));
+
+ if (r < 0)
+ return r;
+ off += r;
+ }
+
+ return off;
+}
+
#if 0
static void
list_shm_ds(void)
return (mode && ((mode & req_mode) == mode));
}
+
+/*
+ * Copy over an ipc_perm structure to an ipc_perm_sysctl structure.
+ */
+void
+prepare_mib_perm(struct ipc_perm_sysctl * perms, const struct ipc_perm * perm)
+{
+
+ memset(perms, 0, sizeof(*perms));
+ perms->_key = perm->_key;
+ perms->uid = perm->uid;
+ perms->gid = perm->gid;
+ perms->cuid = perm->cuid;
+ perms->cgid = perm->cgid;
+ perms->mode = perm->mode;
+ perms->_seq = perm->_seq;
+}
#include <sys/svrctl.h>
#include <minix/sysinfo.h>
#include <machine/partition.h>
-#include <sys/sem.h>
-#include <sys/shm.h>
#include "servers/vfs/const.h"
#include "servers/vfs/dmap.h"
}
/*
- * Copy over an ipc_perm structure to an ipc_perm_sysctl structure.
- */
-static void
-prepare_ipc_perm(struct ipc_perm_sysctl * perms, const struct ipc_perm * perm)
-{
-
- memset(perms, 0, sizeof(*perms));
- perms->_key = perm->_key;
- perms->uid = perm->uid;
- perms->gid = perm->gid;
- perms->cuid = perm->cuid;
- perms->cgid = perm->cgid;
- perms->mode = perm->mode;
- perms->_seq = perm->_seq;
-}
-
-/*
- * Implementation of CTL_KERN KERN_SYSVIPC KERN_SYSVIPC_INFO.
+ * Mock implementation of CTL_KERN KERN_SYSVIPC KERN_SYSVIPC_INFO. Normally,
+ * the IPC service overrides the entire "kern.ipc" subtree. Therefore, this
+ * function will only ever be called when the IPC service is *not* running.
*/
static ssize_t
mib_kern_ipc_info(struct mib_call * call, struct mib_node * node __unused,
- struct mib_oldp * oldp, struct mib_newp * newp __unused)
+ struct mib_oldp * oldp __unused, struct mib_newp * newp __unused)
{
- struct sem_sysctl_info semsi;
- struct shm_sysctl_info shmsi;
- struct semid_ds semds;
- struct shmid_ds shmds;
- ssize_t r, off;
- int i, max;
+ /* The caller must always specify the resouce type (sem/shm/msg). */
if (call->call_namelen != 1)
return EINVAL;
- /*
- * An important security note: according to the specification, IPC_STAT
- * (and therefore, logically, its SEM_STAT and SHM_STAT siblings)
- * performs read access checks on the caller, meaning that users other
- * than root may not obtain details about IPC objects for which they do
- * not have permission. However, NetBSD's sysctl(2) interface to
- * obtain the same information, which we mimic here, does *not* perform
- * such permission checks. For that reason, neither do we; the MIB
- * service is running as root, so it can freely make stat calls, and
- * expose the results to all users on the system. If this is to be
- * changed in the future, we would probably be better off moving the
- * processing of this sysctl(2) node into the IPC server altogether.
- */
- off = 0;
-
- switch (call->call_name[0]) {
- case KERN_SYSVIPC_SEM_INFO:
- memset(&semsi, 0, sizeof(semsi));
- if ((max = semctl(0, 0, IPC_INFO, &semsi.seminfo)) == -1)
- return -errno;
- /*
- * As a hackish exception, the requested size may imply that
- * just general information is to be returned, without throwing
- * an ENOMEM error because there is no space for full output.
- */
- if (mib_getoldlen(oldp) == sizeof(semsi.seminfo))
- return mib_copyout(oldp, 0, &semsi.seminfo,
- sizeof(semsi.seminfo));
- /*
- * ipcs(1) blindly expects the returned array to be of size
- * seminfo.semmni, using the SEM_ALLOC mode flag to see whether
- * each entry is valid. If we return a smaller size, ipcs(1)
- * will access arbitrary memory.
- */
- if (semsi.seminfo.semmni <= 0)
- return EINVAL;
- if (oldp == NULL)
- return sizeof(semsi) + sizeof(semsi.semids[0]) *
- (semsi.seminfo.semmni - 1);
- /*
- * Copy out entries one by one. For the first entry, copy out
- * the entire "semsi" structure. For subsequent entries, reuse
- * the single embedded 'semids' element of "semsi", and copy
- * out only that element.
- */
- for (i = 0; i < semsi.seminfo.semmni; i++) {
- memset(&semsi.semids[0], 0, sizeof(semsi.semids[0]));
- if (i <= max && semctl(i, 0, SEM_STAT, &semds) >= 0) {
- prepare_ipc_perm(&semsi.semids[0].sem_perm,
- &semds.sem_perm);
- semsi.semids[0].sem_nsems = semds.sem_nsems;
- semsi.semids[0].sem_otime = semds.sem_otime;
- semsi.semids[0].sem_ctime = semds.sem_ctime;
- }
- if (off == 0)
- r = mib_copyout(oldp, off, &semsi,
- sizeof(semsi));
- else
- r = mib_copyout(oldp, off, &semsi.semids[0],
- sizeof(semsi.semids[0]));
- if (r < 0)
- return r;
- off += r;
- }
- break;
- case KERN_SYSVIPC_SHM_INFO:
- memset(&shmsi, 0, sizeof(shmsi));
- if ((max = shmctl(0, IPC_INFO,
- (struct shmid_ds *)&shmsi.shminfo)) == -1)
- return -errno;
- /*
- * As a hackish exception, the requested size may imply that
- * just general information is to be returned, without throwing
- * an ENOMEM error because there is no space for full output.
- */
- if (mib_getoldlen(oldp) == sizeof(shmsi.shminfo))
- return mib_copyout(oldp, 0, &shmsi.shminfo,
- sizeof(shmsi.shminfo));
- /*
- * ipcs(1) blindly expects the returned array to be of size
- * shminfo.shmmni, using the SHMSEG_ALLOCATED (not exposed,
- * 0x0800) mode flag to see whether each entry is valid. If we
- * return a smaller size, ipcs(1) will access arbitrary memory.
- */
- if (shmsi.shminfo.shmmni <= 0)
- return EINVAL;
- if (oldp == NULL)
- return sizeof(shmsi) + sizeof(shmsi.shmids[0]) *
- (shmsi.shminfo.shmmni - 1);
- /*
- * Copy out entries one by one. For the first entry, copy out
- * the entire "shmsi" structure. For subsequent entries, reuse
- * the single embedded 'shmids' element of "shmsi", and copy
- * out only that element.
- */
- for (i = 0; i < (int)shmsi.shminfo.shmmni; i++) {
- memset(&shmsi.shmids[0], 0, sizeof(shmsi.shmids[0]));
- if (i <= max && shmctl(i, SHM_STAT, &shmds) == 0) {
- prepare_ipc_perm(&shmsi.shmids[0].shm_perm,
- &shmds.shm_perm);
- shmsi.shmids[0].shm_perm.mode |= 0x0800;
- shmsi.shmids[0].shm_segsz = shmds.shm_segsz;
- shmsi.shmids[0].shm_lpid = shmds.shm_lpid;
- shmsi.shmids[0].shm_cpid = shmds.shm_cpid;
- shmsi.shmids[0].shm_atime = shmds.shm_atime;
- shmsi.shmids[0].shm_dtime = shmds.shm_dtime;
- shmsi.shmids[0].shm_ctime = shmds.shm_ctime;
- shmsi.shmids[0].shm_nattch = shmds.shm_nattch;
- }
- if (off == 0)
- r = mib_copyout(oldp, off, &shmsi,
- sizeof(shmsi));
- else
- r = mib_copyout(oldp, off, &shmsi.shmids[0],
- sizeof(shmsi.shmids[0]));
- if (r < 0)
- return r;
- off += r;
- }
- break;
- default:
- return EOPNOTSUPP;
- }
-
- return off;
+ return EOPNOTSUPP;
}
-/* The CTL_KERN KERN_SYSVIPC nodes. */
+/* The CTL_KERN KERN_SYSVIPC nodes, when not overridden by the IPC service. */
static struct mib_node mib_kern_ipc_table[] = {
/* 1*/ [KERN_SYSVIPC_INFO] = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0,
mib_kern_ipc_info, "sysvipc_info",
"System V style IPC information"),
/* 2*/ [KERN_SYSVIPC_MSG] = MIB_INT(_P | _RO, 0, "sysvmsg", "System V "
"style message support available"),
-/* 3*/ [KERN_SYSVIPC_SEM] = MIB_INT(_P | _RO, 1, "sysvsem", "System V "
+/* 3*/ [KERN_SYSVIPC_SEM] = MIB_INT(_P | _RO, 0, "sysvsem", "System V "
"style semaphore support available"),
-/* 4*/ [KERN_SYSVIPC_SHM] = MIB_INT(_P | _RO, 1, "sysvshm", "System V "
+/* 4*/ [KERN_SYSVIPC_SHM] = MIB_INT(_P | _RO, 0, "sysvshm", "System V "
"style shared memory support available"),
-/* 5*/ /* KERN_SYSVIPC_SHMMAX: not yet supported */
-/* 6*/ /* KERN_SYSVIPC_SHMMNI: not yet supported */
-/* 7*/ /* KERN_SYSVIPC_SHMSEG: not yet supported */
-/* 8*/ /* KERN_SYSVIPC_SHMMAXPGS: not yet supported */
-/* 9*/ /* KERN_SYSVIPC_SHMUSEPHYS: not yet supported */
- /* In addition, NetBSD has a number of dynamic nodes here. */
};
/* The CTL_KERN nodes. */
#include <sys/sem.h>
#include <sys/wait.h>
#include <sys/mman.h>
+#include <sys/sysctl.h>
#include <signal.h>
#include "common.h"
sub88e(match, resume, aux);
}
+/*
+ * Verify that non-root processes can use sysctl(2) to see semaphore sets
+ * created by root.
+ */
+static void
+test88f_child(struct link * parent)
+{
+ static const int mib[] = { CTL_KERN, KERN_SYSVIPC, KERN_SYSVIPC_INFO,
+ KERN_SYSVIPC_SEM_INFO };
+ struct sem_sysctl_info *semsi;
+ size_t len;
+ int id[2], id2, seen[2];
+ int32_t i;
+
+ id[0] = rcv(parent);
+ id[1] = rcv(parent);
+
+ if (sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0) != 0) e(0);
+
+ if ((semsi = malloc(len)) == NULL) e(0);
+
+ if (sysctl(mib, __arraycount(mib), semsi, &len, NULL, 0) != 0) e(0);
+
+ seen[0] = seen[1] = 0;
+ for (i = 0; i < semsi->seminfo.semmni; i++) {
+ if (!(semsi->semids[i].sem_perm.mode & SEM_ALLOC))
+ continue;
+
+ id2 = IXSEQ_TO_IPCID(i, semsi->semids[i].sem_perm);
+ if (id2 == id[0])
+ seen[0]++;
+ else if (id2 == id[1])
+ seen[1]++;
+ }
+
+ free(semsi);
+
+ if (seen[0] != 1) e(0);
+ if (seen[1] != 1) e(0);
+}
+
+/*
+ * Test sysctl(2) based information retrieval. This test aims to ensure that
+ * in particular ipcs(1) and ipcrm(1) will be able to do their jobs.
+ */
+static void
+test88f(void)
+{
+ static const int mib[] = { CTL_KERN, KERN_SYSVIPC, KERN_SYSVIPC_INFO,
+ KERN_SYSVIPC_SEM_INFO };
+ struct seminfo seminfo, seminfo2;
+ struct sem_sysctl_info *semsi;
+ struct semid_ds_sysctl *semds;
+ struct link child;
+ size_t len, size;
+ int id[2], id2;
+ int32_t i, slot[2];
+
+ /*
+ * Verify that we can retrieve only the general semaphore information,
+ * without any actual semaphore set entries. This is actually a dirty
+ * sysctl-level hack, as sysctl requests should not behave differently
+ * based on the requested length. However, ipcs(1) relies on this.
+ */
+ len = sizeof(seminfo);
+ if (sysctl(mib, __arraycount(mib), &seminfo, &len, NULL, 0) != 0) e(0);
+ if (len != sizeof(seminfo)) e(0);
+
+ if (semctl(0, 0, IPC_INFO, &seminfo2) == -1) e(0);
+
+ if (memcmp(&seminfo, &seminfo2, sizeof(seminfo)) != 0) e(0);
+
+ /* Verify that the correct size estimation is returned. */
+ if (seminfo.semmni <= 0) e(0);
+ if (seminfo.semmni > SHRT_MAX) e(0);
+
+ size = sizeof(*semsi) +
+ sizeof(semsi->semids[0]) * (seminfo.semmni - 1);
+
+ len = 0;
+ if (sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0) != 0) e(0);
+ if (len != size) e(0);
+
+ /* Create two semaphore sets that should show up in the listing. */
+ if ((id[0] = semget(KEY_A, 5, IPC_CREAT | 0612)) < 0) e(0);
+
+ if ((id[1] = semget(IPC_PRIVATE, 3, 0650)) < 0) e(0);
+
+ /*
+ * Retrieve the entire semaphore array, and verify that the general
+ * semaphore information is still correct.
+ */
+ if ((semsi = malloc(size)) == NULL) e(0);
+
+ len = size;
+ if (sysctl(mib, __arraycount(mib), semsi, &len, NULL, 0) != 0) e(0);
+ if (len != size) e(0);
+
+ if (sizeof(semsi->seminfo) != sizeof(seminfo)) e(0);
+ if (memcmp(&semsi->seminfo, &seminfo, sizeof(semsi->seminfo)) != 0)
+ e(0);
+
+ /* Verify that our semaphore sets are each in the array once. */
+ slot[0] = slot[1] = -1;
+ for (i = 0; i < seminfo.semmni; i++) {
+ if (!(semsi->semids[i].sem_perm.mode & SEM_ALLOC))
+ continue;
+
+ id2 = IXSEQ_TO_IPCID(i, semsi->semids[i].sem_perm);
+ if (id2 == id[0]) {
+ if (slot[0] != -1) e(0);
+ slot[0] = i;
+ } else if (id2 == id[1]) {
+ if (slot[1] != -1) e(0);
+ slot[1] = i;
+ }
+ }
+
+ if (slot[0] < 0) e(0);
+ if (slot[1] < 0) e(0);
+
+ /* Check that the semaphore sets have the expected properties. */
+ semds = &semsi->semids[slot[0]];
+ if (semds->sem_perm.uid != geteuid()) e(0);
+ if (semds->sem_perm.gid != getegid()) e(0);
+ if (semds->sem_perm.cuid != geteuid()) e(0);
+ if (semds->sem_perm.cgid != getegid()) e(0);
+ if (semds->sem_perm.mode != (SEM_ALLOC | 0612)) e(0);
+ if (semds->sem_perm._key != KEY_A) e(0);
+ if (semds->sem_nsems != 5) e(0);
+ if (semds->sem_otime != 0) e(0);
+ if (semds->sem_ctime == 0) e(0);
+
+ semds = &semsi->semids[slot[1]];
+ if (semds->sem_perm.uid != geteuid()) e(0);
+ if (semds->sem_perm.gid != getegid()) e(0);
+ if (semds->sem_perm.cuid != geteuid()) e(0);
+ if (semds->sem_perm.cgid != getegid()) e(0);
+ if (semds->sem_perm.mode != (SEM_ALLOC | 0650)) e(0);
+ if (semds->sem_perm._key != IPC_PRIVATE) e(0);
+ if (semds->sem_nsems != 3) e(0);
+ if (semds->sem_otime != 0) e(0);
+ if (semds->sem_ctime == 0) e(0);
+
+ /* Make sure that non-root users can see them as well. */
+ spawn(&child, test88f_child, DROP_ALL);
+
+ snd(&child, id[0]);
+ snd(&child, id[1]);
+
+ collect(&child);
+
+ /* Clean up, and verify that the sets are no longer in the listing. */
+ if (semctl(id[0], 0, IPC_RMID) != 0) e(0);
+ if (semctl(id[1], 0, IPC_RMID) != 0) e(0);
+
+ len = size;
+ if (sysctl(mib, __arraycount(mib), semsi, &len, NULL, 0) != 0) e(0);
+ if (len != size) e(0);
+
+ for (i = 0; i < seminfo.semmni; i++) {
+ if (!(semsi->semids[i].sem_perm.mode & SEM_ALLOC))
+ continue;
+
+ id2 = IXSEQ_TO_IPCID(i, semsi->semids[i].sem_perm);
+ if (id2 == id[0]) e(0);
+ if (id2 == id[1]) e(0);
+ }
+
+ free(semsi);
+}
+
/*
* Initialize the test.
*/
static void
test88_init(void)
{
+ static const int mib[] = { CTL_KERN, KERN_SYSVIPC, KERN_SYSVIPC_SEM };
struct group *gr;
+ size_t len;
+ int i;
/* Start with full root privileges. */
setuid(geteuid());
setgid(gr->gr_gid);
setegid(gr->gr_gid);
+ /*
+ * Verify that the IPC service is running at all. If not, there is
+ * obviously no point in running this test.
+ */
+ len = sizeof(i);
+ if (sysctl(mib, __arraycount(mib), &i, &len, NULL, 0) != 0) e(0);
+ if (len != sizeof(i)) e(0);
+
+ if (i == 0) {
+ printf("skipped\n");
+ cleanup();
+ exit(0);
+ }
+
+ /* Allocate a memory page followed by an unmapped page. */
page_size = getpagesize();
page_ptr = mmap(NULL, page_size * 2, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);
if (m & 0x04) test88c();
if (m & 0x08) test88d();
if (m & 0x10) test88e();
+ if (m & 0x20) test88f();
}
quit();