]> Zhao Yanbai Git Server - minix.git/commitdiff
IPC: use RMIB to handle kern.ipc sysctl subtree 26/3326/1
authorDavid van Moolenbroek <david@minix3.org>
Tue, 14 Jun 2016 08:03:51 +0000 (08:03 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Sat, 18 Jun 2016 12:47:24 +0000 (12:47 +0000)
With this patch, the IPC service is changed to use the new RMIB
facility to register and handle the "kern.ipc" sysctl subtree itself.
The subtree was previously handled by the MIB service directly.  This
change improves locality of handling: especially the
kern.ipc.sysvipc_info node has some peculiarities specific to the IPC
service and is therefore better handled there.  Also, since the IPC
service is essentially optional to the system, this rearrangement
yields a cleaner situation when the IPC service is not running: in
that case, the MIB service will expose a few basic kern.ipc nodes
indicating that no SysV IPC facilities are present.  Those nodes will
be overridden through RMIB when the IPC service is running.

It should be easier to add the remaining (from NetBSD) kern.ipc nodes
as well now.

Test88 is extended with a new subtest that verifies that sysctl-based
information retrieval for semaphore sets works as expected.

Change-Id: I6b7730e85305b64cfd8418c0cc56bde64b22c584

minix/lib/libminc/Makefile
minix/servers/ipc/inc.h
minix/servers/ipc/main.c
minix/servers/ipc/sem.c
minix/servers/ipc/shm.c
minix/servers/ipc/utility.c
minix/servers/mib/kern.c
minix/tests/test88.c

index 5fdc3c4be3f42b1e8496631c9d8033f266a90f46..5e2ded9b78169a94283b1b9b52688e32080bd181 100644 (file)
@@ -287,7 +287,7 @@ CLEANFILES+= ${f:C/\.o/.bc/}
        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/}
index 2f5ddd7c88beb289925f5c8e1cfcba62f16be674..01c1d710a8a37a1667f8095293092354ef7da6d2 100644 (file)
@@ -9,6 +9,7 @@
 #include <minix/const.h>
 #include <minix/type.h>
 #include <minix/syslib.h>
+#include <minix/rmib.h>
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -48,6 +49,7 @@ int do_shmget(message *);
 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);
 
@@ -55,8 +57,10 @@ 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 *);
index a54c1391d24209085f683b7bf8bf6aa39c233a97..02c62c42718785e8a47ab26ec6090c130864645d 100644 (file)
@@ -19,14 +19,78 @@ static int (* const call_vec[])(message *) = {
        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;
 }
 
@@ -44,8 +108,11 @@ sef_cb_signal_handler(int signo)
         * 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");
 }
@@ -177,6 +244,13 @@ main(int argc, char ** argv)
                        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);
 
index 6976826024c3ad7ade7de63ab077bc7858b9f1ad..e1bcfda13e3ba5104128e7c34bafe0c57e4466e6 100644 (file)
@@ -422,6 +422,46 @@ check_set(struct sem_struct * sem)
        } 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.
  */
@@ -527,32 +567,7 @@ do_semctl(message * m)
                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)
@@ -763,6 +778,75 @@ out_free:
        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.
  */
index 1857714c2b7fc24934d99741f530938d44f120cb..11b4897659af4770448b55d935160f171fc00de5 100644 (file)
@@ -241,6 +241,22 @@ do_shmdt(message * m)
        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)
 {
@@ -320,12 +336,7 @@ 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;
@@ -359,6 +370,79 @@ do_shmctl(message * m)
        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)
index 04f7eac19cd4ef7ab29395e5a65fe57fcd8857be..41dda5db90a26bf80998d734c68073eb96048a0a 100644 (file)
@@ -30,3 +30,20 @@ check_perm(struct ipc_perm * req, endpoint_t who, int mode)
 
        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;
+}
index 3657a0d893bc8652f77f7cd97763284760338098..465a7ab395f26526d19bf69365a3ea1b387e4f95 100644 (file)
@@ -5,8 +5,6 @@
 #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"
@@ -301,183 +299,33 @@ mib_kern_boottime(struct mib_call * call __unused,
 }
 
 /*
- * 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. */
index 1d803a64615a39a5b89b342abc181b6e216f5b93..753bac2d38d4dc66511ca626cd0728fc120a22bf 100644 (file)
@@ -8,6 +8,7 @@
 #include <sys/sem.h>
 #include <sys/wait.h>
 #include <sys/mman.h>
+#include <sys/sysctl.h>
 #include <signal.h>
 
 #include "common.h"
@@ -2764,13 +2765,188 @@ test88e(void)
                                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());
@@ -2780,6 +2956,21 @@ test88_init(void)
        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);
@@ -2811,6 +3002,7 @@ main(int argc, char ** argv)
                if (m & 0x04) test88c();
                if (m & 0x08) test88d();
                if (m & 0x10) test88e();
+               if (m & 0x20) test88f();
        }
 
        quit();