From 2f09e77b825a6f635d6faaa774114aebbd7691ce Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Thu, 17 Dec 2015 12:57:54 +0000 Subject: [PATCH] MIB: add support for System V IPC information node The kernel.ipc.sysvipc_info node is the gateway from NetBSD ipcs(1) and ipcrm(1) to the IPC server, and thus necessary for a clean import of these two utilities. The MIB service implementation uses the preexisting (Linux-specific) information calls on the IPC server to obtain the information. Change-Id: I85d1e193162d6b689f114764254dd7f314d2cfa0 --- minix/lib/libminc/Makefile | 10 +- minix/servers/mib/kern.c | 167 +++++++++++++++++++++++++++++- minix/usr.bin/trace/service/mib.c | 49 +++++++++ 3 files changed, 220 insertions(+), 6 deletions(-) diff --git a/minix/lib/libminc/Makefile b/minix/lib/libminc/Makefile index 7b545093b..5fdc3c4be 100644 --- a/minix/lib/libminc/Makefile +++ b/minix/lib/libminc/Makefile @@ -285,11 +285,11 @@ CLEANFILES+= ${f:C/\.o/.bc/} .for f in \ access.o brk.o close.o environ.o execve.o fork.o fsync.o \ getgid.o getpid.o geteuid.o getuid.o gettimeofday.o getvfsstat.o \ - init.o kernel_utils.o link.o loadname.o lseek.o _mcontext.o mknod.o \ - mmap.o nanosleep.o open.o pread.o pwrite.o read.o sbrk.o \ - select.o setuid.o sigprocmask.o stack_utils.o stat.o stime.o \ - svrctl.o syscall.o _ucontext.o umask.o unlink.o wait4.o write.o \ - kill.o __sysctl.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 \ + 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/} OBJS+= ${f} CLEANFILES+= ${f} diff --git a/minix/servers/mib/kern.c b/minix/servers/mib/kern.c index e1565a230..3657a0d89 100644 --- a/minix/servers/mib/kern.c +++ b/minix/servers/mib/kern.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "servers/vfs/const.h" #include "servers/vfs/dmap.h" @@ -298,9 +300,172 @@ mib_kern_boottime(struct mib_call * call __unused, return mib_copyout(oldp, 0, &tv, sizeof(tv)); } +/* + * 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. + */ +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 sem_sysctl_info semsi; + struct shm_sysctl_info shmsi; + struct semid_ds semds; + struct shmid_ds shmds; + ssize_t r, off; + int i, max; + + 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; +} + /* The CTL_KERN KERN_SYSVIPC nodes. */ static struct mib_node mib_kern_ipc_table[] = { -/* 1*/ /* KERN_SYSVIPC_INFO: not yet supported */ +/* 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 " diff --git a/minix/usr.bin/trace/service/mib.c b/minix/usr.bin/trace/service/mib.c index 32cf8c4db..d98a0f619 100644 --- a/minix/usr.bin/trace/service/mib.c +++ b/minix/usr.bin/trace/service/mib.c @@ -234,6 +234,54 @@ put_kern_boottime(struct trace_proc * proc, const char * name, return TRUE; } +/* + * Print CTL_KERN KERN_SYSVIPC KERN_SYSVIPC_INFO. + */ +static int +put_kern_sysvipc_info(struct trace_proc * proc, const char * name, + int type, const void * ptr __unused, vir_bytes addr, size_t size) +{ + const int *mib; + const char *text; + int i; + + /* + * TODO: print the obtained structure(s). For now we are just + * concerned with the name components. + */ + if (type != ST_NAME) { + put_ptr(proc, name, addr); + + return TRUE; + } + + mib = (const int *)ptr; + + for (i = 0; i < size; i++) { + text = NULL; + + if (i == 0) { + switch (mib[i]) { + case KERN_SYSVIPC_SEM_INFO: text = ""; break; + case KERN_SYSVIPC_SHM_INFO: text = ""; break; + case KERN_SYSVIPC_MSG_INFO: text = ""; break; + } + } + + if (!valuesonly && text != NULL) + put_field(proc, NULL, text); + else + put_value(proc, NULL, "%d", mib[i]); + } + + return 0; +} + +/* The CTL_KERN KERN_SYSVIPC table. */ +static const struct sysctl_tab kern_sysvipc_tab[] = { + PROC(KERN_SYSVIPC_INFO, 0, put_kern_sysvipc_info), +}; + /* The CTL_KERN table. */ static const struct sysctl_tab kern_tab[] = { PROC(KERN_CLOCKRATE, sizeof(struct clockinfo), put_kern_clockrate), @@ -242,6 +290,7 @@ static const struct sysctl_tab kern_tab[] = { PROC(KERN_CP_TIME, sizeof(uint64_t) * CPUSTATES, put_kern_cp_time), PROC(KERN_CONSDEV, sizeof(dev_t), put_kern_consdev), PROC(KERN_DRIVERS, 0, put_kern_drivers), + NODE(KERN_SYSVIPC, kern_sysvipc_tab), PROC(KERN_BOOTTIME, 0, put_kern_boottime), }; -- 2.44.0