From: David van Moolenbroek Date: Tue, 14 Jun 2016 08:03:51 +0000 (+0000) Subject: IPC: use RMIB to handle kern.ipc sysctl subtree X-Git-Url: http://zhaoyanbai.com/repos/migration?a=commitdiff_plain;h=534584945c0f50b9816e7051003856bd31bf8c65;p=minix.git IPC: use RMIB to handle kern.ipc sysctl subtree 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 --- diff --git a/minix/lib/libminc/Makefile b/minix/lib/libminc/Makefile index 5fdc3c4be..5e2ded9b7 100644 --- a/minix/lib/libminc/Makefile +++ b/minix/lib/libminc/Makefile @@ -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/} diff --git a/minix/servers/ipc/inc.h b/minix/servers/ipc/inc.h index 2f5ddd7c8..01c1d710a 100644 --- a/minix/servers/ipc/inc.h +++ b/minix/servers/ipc/inc.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -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 *); diff --git a/minix/servers/ipc/main.c b/minix/servers/ipc/main.c index a54c1391d..02c62c427 100644 --- a/minix/servers/ipc/main.c +++ b/minix/servers/ipc/main.c @@ -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); diff --git a/minix/servers/ipc/sem.c b/minix/servers/ipc/sem.c index 697682602..e1bcfda13 100644 --- a/minix/servers/ipc/sem.c +++ b/minix/servers/ipc/sem.c @@ -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. */ diff --git a/minix/servers/ipc/shm.c b/minix/servers/ipc/shm.c index 1857714c2..11b489765 100644 --- a/minix/servers/ipc/shm.c +++ b/minix/servers/ipc/shm.c @@ -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) diff --git a/minix/servers/ipc/utility.c b/minix/servers/ipc/utility.c index 04f7eac19..41dda5db9 100644 --- a/minix/servers/ipc/utility.c +++ b/minix/servers/ipc/utility.c @@ -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; +} diff --git a/minix/servers/mib/kern.c b/minix/servers/mib/kern.c index 3657a0d89..465a7ab39 100644 --- a/minix/servers/mib/kern.c +++ b/minix/servers/mib/kern.c @@ -5,8 +5,6 @@ #include #include #include -#include -#include #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. */ diff --git a/minix/tests/test88.c b/minix/tests/test88.c index 1d803a646..753bac2d3 100644 --- a/minix/tests/test88.c +++ b/minix/tests/test88.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #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();