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();