int msgctl1(struct proc *p, int msqid, int cmd, caddr_t buf, int (*ds_copyin)(const void *, void *, size_t), int (*ds_copyout)(const void *, void *, size_t)) { struct ucred *cred = p->p_ucred; struct msqid_ds msqbuf; struct msqid_kernel *msqkptr; struct msg *msghdr; int ix, error = 0; DPRINTF(("call to msgctl(%d, %d, %p)\n", msqid, cmd, buf)); ix = IPCID_TO_IX(msqid); if (ix < 0 || ix >= msginfo.msgmni) { DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", ix, msginfo.msgmni)); return (EINVAL); } msqkptr = &msqids[ix]; if (msqkptr->u.msg_qbytes == 0) { DPRINTF(("no such msqid\n")); return (EINVAL); } if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) { DPRINTF(("wrong sequence number\n")); return (EINVAL); } #ifdef MAC error = mac_sysvmsq_check_msqctl(cred, msqkptr, cmd); if (error) return (error); #endif switch (cmd) { case IPC_RMID: if ((error = ipcperm(cred, &msqkptr->u.msg_perm, IPC_M)) != 0) return (error); #ifdef MAC /* * Check that the process has MAC access permissions to * individual msghdrs. Note: We need to do this in a separate * loop because the actual loop alters the msq/msghdr info as * it progresses, and there is no going back if half the way * we discover that the process cannot free a certain msghdr. * The msq will get into an inconsistent state. */ for (msghdr = msqkptr->u.msg_first; msghdr != NULL; msghdr = msghdr->msg_next) { error = mac_sysvmsq_check_msgrmid(cred, msghdr); if (error) return (error); } #endif /* Free the message headers */ msghdr = msqkptr->u.msg_first; while (msghdr != NULL) { struct msg *msghdr_tmp; /* Free the segments of each message */ msqkptr->u.msg_cbytes -= msghdr->msg_ts; msqkptr->u.msg_qnum--; msghdr_tmp = msghdr; msghdr = msghdr->msg_next; msg_freehdr(msghdr_tmp); } #ifdef DIAGNOSTIC if (msqkptr->u.msg_cbytes != 0) panic("sys_msgctl: msg_cbytes is screwed up"); if (msqkptr->u.msg_qnum != 0) panic("sys_msgctl: msg_qnum is screwed up"); #endif #ifdef MAC mac_sysvmsq_cleanup(msqkptr); #endif msqkptr->u.msg_qbytes = 0; /* Mark it as free */ wakeup(msqkptr); break; case IPC_SET: if ((error = ipcperm(cred, &msqkptr->u.msg_perm, IPC_M))) return (error); if ((error = ds_copyin(buf, &msqbuf, sizeof(msqbuf))) != 0) return (error); if (msqbuf.msg_qbytes > msqkptr->u.msg_qbytes && cred->cr_uid != 0) return (EPERM); if (msqbuf.msg_qbytes > msginfo.msgmnb) { DPRINTF(("can't increase msg_qbytes beyond %d " "(truncating)\n", msginfo.msgmnb)); /* silently restrict qbytes to system limit */ msqbuf.msg_qbytes = msginfo.msgmnb; } if (msqbuf.msg_qbytes == 0) { DPRINTF(("can't reduce msg_qbytes to 0\n")); return (EINVAL); /* non-standard errno! */ } msqkptr->u.msg_perm.uid = msqbuf.msg_perm.uid; msqkptr->u.msg_perm.gid = msqbuf.msg_perm.gid; msqkptr->u.msg_perm.mode = (msqkptr->u.msg_perm.mode & ~0777) | (msqbuf.msg_perm.mode & 0777); msqkptr->u.msg_qbytes = msqbuf.msg_qbytes; msqkptr->u.msg_ctime = time_second; break; case IPC_STAT: if ((error = ipcperm(cred, &msqkptr->u.msg_perm, IPC_R))) { DPRINTF(("requester doesn't have read access\n")); return (error); } error = ds_copyout(&msqkptr->u, buf, sizeof(struct msqid_ds)); break; default: DPRINTF(("invalid command %d\n", cmd)); return (EINVAL); } return (error); }
int msgctl(struct proc *p, struct msgctl_args *uap, int32_t *retval) { int msqid = uap->msqid; int cmd = uap->cmd; kauth_cred_t cred = kauth_cred_get(); int rval, eval; struct user_msqid_ds msqbuf; struct msqid_kernel *msqptr; SYSV_MSG_SUBSYS_LOCK(); if (!msginit(0)) { eval = ENOMEM; goto msgctlout; } #ifdef MSG_DEBUG_OK printf("call to msgctl(%d, %d, 0x%qx)\n", msqid, cmd, uap->buf); #endif AUDIT_ARG(svipc_cmd, cmd); AUDIT_ARG(svipc_id, msqid); msqid = IPCID_TO_IX(msqid); if (msqid < 0 || msqid >= msginfo.msgmni) { #ifdef MSG_DEBUG_OK printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, msginfo.msgmni); #endif eval = EINVAL; goto msgctlout; } msqptr = &msqids[msqid]; if (msqptr->u.msg_qbytes == 0) { #ifdef MSG_DEBUG_OK printf("no such msqid\n"); #endif eval = EINVAL; goto msgctlout; } if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) { #ifdef MSG_DEBUG_OK printf("wrong sequence number\n"); #endif eval = EINVAL; goto msgctlout; } #if CONFIG_MACF eval = mac_sysvmsq_check_msqctl(kauth_cred_get(), msqptr, cmd); if (eval) goto msgctlout; #endif eval = 0; rval = 0; switch (cmd) { case IPC_RMID: { struct msg *msghdr; if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_M))) goto msgctlout; #if CONFIG_MACF /* * Check that the thread has MAC access permissions to * individual msghdrs. Note: We need to do this in a * separate loop because the actual loop alters the * msq/msghdr info as it progresses, and there is no going * back if half the way through we discover that the * thread cannot free a certain msghdr. The msq will get * into an inconsistent state. */ for (msghdr = msqptr->u.msg_first; msghdr != NULL; msghdr = msghdr->msg_next) { eval = mac_sysvmsq_check_msgrmid(kauth_cred_get(), msghdr); if (eval) goto msgctlout; } #endif /* Free the message headers */ msghdr = msqptr->u.msg_first; while (msghdr != NULL) { struct msg *msghdr_tmp; /* Free the segments of each message */ msqptr->u.msg_cbytes -= msghdr->msg_ts; msqptr->u.msg_qnum--; msghdr_tmp = msghdr; msghdr = msghdr->msg_next; msg_freehdr(msghdr_tmp); } if (msqptr->u.msg_cbytes != 0) panic("msg_cbytes is messed up"); if (msqptr->u.msg_qnum != 0) panic("msg_qnum is messed up"); msqptr->u.msg_qbytes = 0; /* Mark it as free */ #if CONFIG_MACF mac_sysvmsq_label_recycle(msqptr); #endif wakeup((caddr_t)msqptr); } break; case IPC_SET: if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_M))) goto msgctlout; SYSV_MSG_SUBSYS_UNLOCK(); if (IS_64BIT_PROCESS(p)) { struct user64_msqid_ds tmpds; eval = copyin(uap->buf, &tmpds, sizeof(tmpds)); msqid_ds_user64tokernel(&tmpds, &msqbuf); } else { struct user32_msqid_ds tmpds; eval = copyin(uap->buf, &tmpds, sizeof(tmpds)); msqid_ds_user32tokernel(&tmpds, &msqbuf); } if (eval) return(eval); SYSV_MSG_SUBSYS_LOCK(); if (msqbuf.msg_qbytes > msqptr->u.msg_qbytes) { eval = suser(cred, &p->p_acflag); if (eval) goto msgctlout; } /* compare (msglen_t) value against restrict (int) value */ if (msqbuf.msg_qbytes > (user_msglen_t)msginfo.msgmnb) { #ifdef MSG_DEBUG_OK printf("can't increase msg_qbytes beyond %d (truncating)\n", msginfo.msgmnb); #endif msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ } if (msqbuf.msg_qbytes == 0) { #ifdef MSG_DEBUG_OK printf("can't reduce msg_qbytes to 0\n"); #endif eval = EINVAL; goto msgctlout; } msqptr->u.msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */ msqptr->u.msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */ msqptr->u.msg_perm.mode = (msqptr->u.msg_perm.mode & ~0777) | (msqbuf.msg_perm.mode & 0777); msqptr->u.msg_qbytes = msqbuf.msg_qbytes; msqptr->u.msg_ctime = sysv_msgtime(); break; case IPC_STAT: if ((eval = ipcperm(cred, &msqptr->u.msg_perm, IPC_R))) { #ifdef MSG_DEBUG_OK printf("requester doesn't have read access\n"); #endif goto msgctlout; } SYSV_MSG_SUBSYS_UNLOCK(); if (IS_64BIT_PROCESS(p)) { struct user64_msqid_ds msqid_ds64 = {}; msqid_ds_kerneltouser64(&msqptr->u, &msqid_ds64); eval = copyout(&msqid_ds64, uap->buf, sizeof(msqid_ds64)); } else { struct user32_msqid_ds msqid_ds32 = {}; msqid_ds_kerneltouser32(&msqptr->u, &msqid_ds32); eval = copyout(&msqid_ds32, uap->buf, sizeof(msqid_ds32)); } SYSV_MSG_SUBSYS_LOCK(); break; default: #ifdef MSG_DEBUG_OK printf("invalid command %d\n", cmd); #endif eval = EINVAL; goto msgctlout; } if (eval == 0) *retval = rval; msgctlout: SYSV_MSG_SUBSYS_UNLOCK(); return(eval); }