diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index e9c71ca..cf63196 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -60,6 +60,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -2330,6 +2331,9 @@ prison_remove_one(struct prison *pr) pr->pr_flags &= ~PR_PERSIST; } + /* SysV IPC cleanup for the prison */ + ipc_cleanup_for_prison(pr); + /* * jail_remove added a reference. If that's the only one, remove * the prison now. diff --git a/sys/kern/sysv_ipc.c b/sys/kern/sysv_ipc.c index e402cb5..9985b87 100644 --- a/sys/kern/sysv_ipc.c +++ b/sys/kern/sysv_ipc.c @@ -47,9 +47,13 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include void (*shmfork_hook)(struct proc *, struct proc *) = NULL; void (*shmexit_hook)(struct vmspace *) = NULL; +void (*sysvshm_cleanup_for_prison_hook)(struct prison *) = NULL; +void (*sysvmsg_cleanup_for_prison_hook)(struct prison *) = NULL; +void (*sysvsem_cleanup_for_prison_hook)(struct prison *) = NULL; /* called from kern_fork.c */ void @@ -72,6 +76,19 @@ shmexit(struct vmspace *vm) return; } +/* called from kern_jail.c */ +void +ipc_cleanup_for_prison(struct prison *pr) +{ + + if (sysvshm_cleanup_for_prison_hook != NULL) + sysvshm_cleanup_for_prison_hook(pr); + if (sysvmsg_cleanup_for_prison_hook != NULL) + sysvmsg_cleanup_for_prison_hook(pr); + if (sysvsem_cleanup_for_prison_hook != NULL) + sysvsem_cleanup_for_prison_hook(pr); +} + /* * Check for IPC permission. * diff --git a/sys/kern/sysv_msg.c b/sys/kern/sysv_msg.c index d58cb7e..99edabe 100644 --- a/sys/kern/sysv_msg.c +++ b/sys/kern/sysv_msg.c @@ -68,6 +68,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -80,6 +81,9 @@ static MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues"); static int msginit(void); static int msgunload(void); static int sysvmsg_modload(struct module *, int, void *); +static int cr_cansee_msq(struct ucred *, struct msqid_kernel *); +static void msq_remove(struct msqid_kernel *); +static void sysvmsg_cleanup_for_prison_myhook(struct prison *); #ifdef MSG_DEBUG @@ -257,6 +261,7 @@ msginit() #endif } mtx_init(&msq_mtx, "msq", NULL, MTX_DEF); + sysvmsg_cleanup_for_prison_hook = &sysvmsg_cleanup_for_prison_myhook; error = syscall_helper_register(msg_syscalls); if (error != 0) @@ -282,6 +287,7 @@ msgunload() #ifdef COMPAT_FREEBSD32 syscall32_helper_unregister(msg32_syscalls); #endif + sysvmsg_cleanup_for_prison_hook = NULL; for (msqid = 0; msqid < msginfo.msgmni; msqid++) { /* @@ -372,6 +378,69 @@ msg_freehdr(msghdr) #endif } +static int +cr_cansee_msq(struct ucred *cred, struct msqid_kernel *msqkptr) +{ + + if (msqkptr->cred == NULL || prison_check(cred, msqkptr->cred)) + return (EINVAL); + return (0); +} + +static void +msq_remove(struct msqid_kernel *msqkptr) +{ + struct msg *msghdr; + + racct_sub_cred(msqkptr->cred, RACCT_NMSGQ, 1); + racct_sub_cred(msqkptr->cred, RACCT_MSGQQUEUED, msqkptr->u.msg_qnum); + racct_sub_cred(msqkptr->cred, RACCT_MSGQSIZE, msqkptr->u.msg_cbytes); + crfree(msqkptr->cred); + msqkptr->cred = NULL; + + /* 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); + } + + if (msqkptr->u.msg_cbytes != 0) + panic("msg_cbytes is screwed up"); + if (msqkptr->u.msg_qnum != 0) + panic("msg_qnum is screwed up"); + + msqkptr->u.msg_qbytes = 0; /* Mark it as free */ + +#ifdef MAC + mac_sysvmsq_cleanup(msqkptr); +#endif + + wakeup(msqkptr); +} + +static void +sysvmsg_cleanup_for_prison_myhook(struct prison *pr) +{ + int i; + struct msqid_kernel *msqkptr; + + mtx_lock(&msq_mtx); + for (i = 0; i < msginfo.msgmni; i++) { + msqkptr = &msqids[i]; + if (msqkptr->u.msg_qbytes != 0 && + msqkptr->cred && msqkptr->cred->cr_prison == pr) + msq_remove(msqkptr); + } + mtx_unlock(&msq_mtx); +} + #ifndef _SYS_SYSPROTO_H_ struct msgctl_args { int msqid; @@ -433,6 +502,9 @@ kern_msgctl(td, msqid, cmd, msqbuf) error = EINVAL; goto done2; } + error = cr_cansee_msq(td->td_ucred, msqkptr); + if (error != 0) + goto done2; #ifdef MAC error = mac_sysvmsq_check_msqctl(td->td_ucred, msqkptr, cmd); if (error != 0) @@ -446,7 +518,9 @@ kern_msgctl(td, msqid, cmd, msqbuf) case IPC_RMID: { +#ifdef MAC struct msg *msghdr; +#endif if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M))) goto done2; @@ -468,37 +542,7 @@ kern_msgctl(td, msqid, cmd, msqbuf) } #endif - racct_sub_cred(msqkptr->cred, RACCT_NMSGQ, 1); - racct_sub_cred(msqkptr->cred, RACCT_MSGQQUEUED, msqkptr->u.msg_qnum); - racct_sub_cred(msqkptr->cred, RACCT_MSGQSIZE, msqkptr->u.msg_cbytes); - crfree(msqkptr->cred); - msqkptr->cred = NULL; - - /* 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); - } - - if (msqkptr->u.msg_cbytes != 0) - panic("msg_cbytes is screwed up"); - if (msqkptr->u.msg_qnum != 0) - panic("msg_qnum is screwed up"); - - msqkptr->u.msg_qbytes = 0; /* Mark it as free */ - -#ifdef MAC - mac_sysvmsq_cleanup(msqkptr); -#endif - - wakeup(msqkptr); + msq_remove(msqkptr); } break; @@ -578,6 +622,7 @@ sys_msgget(td, uap) for (msqid = 0; msqid < msginfo.msgmni; msqid++) { msqkptr = &msqids[msqid]; if (msqkptr->u.msg_qbytes != 0 && + msqkptr->cred && msqkptr->cred->cr_prison == cred->cr_prison && msqkptr->u.msg_perm.key == key) break; } @@ -718,6 +763,8 @@ kern_msgsnd(td, msqid, msgp, msgsz, msgflg, mtype) goto done2; } + if ((error = cr_cansee_msq(td->td_ucred, msqkptr))) + goto done2; if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_W))) { DPRINTF(("requester doesn't have write access\n")); goto done2; @@ -1081,6 +1128,8 @@ kern_msgrcv(td, msqid, msgp, msgsz, msgtyp, msgflg, mtype) goto done2; } + if ((error = cr_cansee_msq(td->td_ucred, msqkptr))) + goto done2; if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) { DPRINTF(("requester doesn't have read access\n")); goto done2; @@ -1320,9 +1369,37 @@ sys_msgrcv(td, uap) static int sysctl_msqids(SYSCTL_HANDLER_ARGS) { + int error; + struct sbuf sb; + struct msqid_kernel tmp, empty; + int i; + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) + goto done; + sbuf_new_for_sysctl(&sb, NULL, sizeof(struct msqid_kernel) * msginfo.msgmni, req); + + bzero(&empty, sizeof(empty)); + for (i = 0; i < msginfo.msgmni; i++) { + struct msqid_kernel *msqkptr; + + msqkptr = &msqids[i]; + if (msqkptr->u.msg_qbytes == 0 || + cr_cansee_msq(req->td->td_ucred, msqkptr)) { + msqkptr = ∅ + } else if (req->td->td_ucred->cr_prison != msqkptr->cred->cr_prison) { + bcopy(msqkptr, &tmp, sizeof(tmp)); + msqkptr = &tmp; + msqkptr->u.msg_perm.key = IPC_PRIVATE; + } - return (SYSCTL_OUT(req, msqids, - sizeof(struct msqid_kernel) * msginfo.msgmni)); + sbuf_bcat(&sb, msqkptr, sizeof(*msqkptr)); + } + error = sbuf_finish(&sb); + sbuf_delete(&sb); + +done: + return (error); } SYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0, diff --git a/sys/kern/sysv_sem.c b/sys/kern/sysv_sem.c index f9ff217..5b9e1ca 100644 --- a/sys/kern/sysv_sem.c +++ b/sys/kern/sysv_sem.c @@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -79,6 +80,9 @@ static int semunload(void); static void semexit_myhook(void *arg, struct proc *p); static int sysctl_sema(SYSCTL_HANDLER_ARGS); static int semvalid(int semid, struct semid_kernel *semakptr); +static int cr_cansee_sem(struct ucred *cred, struct semid_kernel *semakptr); +static void sem_remove(int semidx, struct ucred *cred); +static void sysvsem_cleanup_for_prison_myhook(struct prison *pr); #ifndef _SYS_SYSPROTO_H_ struct __semctl_args; @@ -287,6 +291,7 @@ seminit(void) mtx_init(&sem_undo_mtx, "semu", NULL, MTX_DEF); semexit_tag = EVENTHANDLER_REGISTER(process_exit, semexit_myhook, NULL, EVENTHANDLER_PRI_ANY); + sysvsem_cleanup_for_prison_hook = &sysvsem_cleanup_for_prison_myhook; error = syscall_helper_register(sem_syscalls); if (error != 0) @@ -313,6 +318,7 @@ semunload(void) #endif syscall_helper_unregister(sem_syscalls); EVENTHANDLER_DEREGISTER(process_exit, semexit_tag); + sysvsem_cleanup_for_prison_hook = NULL; #ifdef MAC for (i = 0; i < seminfo.semmni; i++) mac_sysvsem_destroy(&sema[i]); @@ -506,6 +512,70 @@ semvalid(int semid, struct semid_kernel *semakptr) semakptr->u.sem_perm.seq != IPCID_TO_SEQ(semid) ? EINVAL : 0); } +static int +cr_cansee_sem(struct ucred *cred, struct semid_kernel *semakptr) +{ + + if (semakptr->cred == NULL || prison_check(cred, semakptr->cred)) + return (EINVAL); + return (0); +} + +static void +sem_remove(int semidx, struct ucred *cred) +{ + int i; + struct semid_kernel *semakptr; + + KASSERT(semidx >= 0 && semidx < seminfo.semmni, ("semidx out of bounds")); + semakptr = &sema[semidx]; + semakptr->u.sem_perm.cuid = cred ? cred->cr_uid : 0; + semakptr->u.sem_perm.uid = cred ? cred->cr_uid : 0; + semakptr->u.sem_perm.mode = 0; + racct_sub_cred(semakptr->cred, RACCT_NSEM, semakptr->u.sem_nsems); + crfree(semakptr->cred); + semakptr->cred = NULL; + SEMUNDO_LOCK(); + semundo_clear(semidx, -1); + SEMUNDO_UNLOCK(); +#ifdef MAC + mac_sysvsem_cleanup(semakptr); +#endif + wakeup(semakptr); + for (i = 0; i < seminfo.semmni; i++) { + if ((sema[i].u.sem_perm.mode & SEM_ALLOC) && + sema[i].u.sem_base > semakptr->u.sem_base) + mtx_lock_flags(&sema_mtx[i], LOP_DUPOK); + } + for (i = semakptr->u.sem_base - sem; i < semtot; i++) + sem[i] = sem[i + semakptr->u.sem_nsems]; + for (i = 0; i < seminfo.semmni; i++) { + if ((sema[i].u.sem_perm.mode & SEM_ALLOC) && + sema[i].u.sem_base > semakptr->u.sem_base) { + sema[i].u.sem_base -= semakptr->u.sem_nsems; + mtx_unlock(&sema_mtx[i]); + } + } + semtot -= semakptr->u.sem_nsems; +} + +static void +sysvsem_cleanup_for_prison_myhook(struct prison *pr) +{ + int i; + + mtx_lock(&sem_mtx); + for (i = 0; i < seminfo.semmni; i++) { + if ((sema[i].u.sem_perm.mode & SEM_ALLOC) && + sema[i].cred && sema[i].cred->cr_prison == pr) { + mtx_lock(&sema_mtx[i]); + sem_remove(i, NULL); + mtx_unlock(&sema_mtx[i]); + } + } + mtx_unlock(&sem_mtx); +} + /* * Note that the user-mode half of this passes a union, not a pointer. */ @@ -610,6 +680,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, error = EINVAL; goto done2; } + if ((error = cr_cansee_sem(td->td_ucred, semakptr)) != 0) + goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; #ifdef MAC @@ -632,6 +704,7 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, if (cmd == IPC_RMID) mtx_lock(&sem_mtx); mtx_lock(sema_mtxp); + #ifdef MAC error = mac_sysvsem_check_semctl(cred, semakptr, cmd); if (error != 0) @@ -645,41 +718,18 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, case IPC_RMID: if ((error = semvalid(semid, semakptr)) != 0) goto done2; + if ((error = cr_cansee_sem(td->td_ucred, semakptr)) != 0) + goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_M))) goto done2; - semakptr->u.sem_perm.cuid = cred->cr_uid; - semakptr->u.sem_perm.uid = cred->cr_uid; - semakptr->u.sem_perm.mode = 0; - racct_sub_cred(semakptr->cred, RACCT_NSEM, semakptr->u.sem_nsems); - crfree(semakptr->cred); - semakptr->cred = NULL; - SEMUNDO_LOCK(); - semundo_clear(semidx, -1); - SEMUNDO_UNLOCK(); -#ifdef MAC - mac_sysvsem_cleanup(semakptr); -#endif - wakeup(semakptr); - for (i = 0; i < seminfo.semmni; i++) { - if ((sema[i].u.sem_perm.mode & SEM_ALLOC) && - sema[i].u.sem_base > semakptr->u.sem_base) - mtx_lock_flags(&sema_mtx[i], LOP_DUPOK); - } - for (i = semakptr->u.sem_base - sem; i < semtot; i++) - sem[i] = sem[i + semakptr->u.sem_nsems]; - for (i = 0; i < seminfo.semmni; i++) { - if ((sema[i].u.sem_perm.mode & SEM_ALLOC) && - sema[i].u.sem_base > semakptr->u.sem_base) { - sema[i].u.sem_base -= semakptr->u.sem_nsems; - mtx_unlock(&sema_mtx[i]); - } - } - semtot -= semakptr->u.sem_nsems; + sem_remove(semidx, cred); break; case IPC_SET: if ((error = semvalid(semid, semakptr)) != 0) goto done2; + if ((error = cr_cansee_sem(td->td_ucred, semakptr)) != 0) + goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_M))) goto done2; sbuf = arg->buf; @@ -693,6 +743,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, case IPC_STAT: if ((error = semvalid(semid, semakptr)) != 0) goto done2; + if ((error = cr_cansee_sem(td->td_ucred, semakptr)) != 0) + goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; bcopy(&semakptr->u, arg->buf, sizeof(struct semid_ds)); @@ -701,6 +753,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, case GETNCNT: if ((error = semvalid(semid, semakptr)) != 0) goto done2; + if ((error = cr_cansee_sem(td->td_ucred, semakptr)) != 0) + goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { @@ -713,6 +767,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, case GETPID: if ((error = semvalid(semid, semakptr)) != 0) goto done2; + if ((error = cr_cansee_sem(td->td_ucred, semakptr)) != 0) + goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { @@ -725,6 +781,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, case GETVAL: if ((error = semvalid(semid, semakptr)) != 0) goto done2; + if ((error = cr_cansee_sem(td->td_ucred, semakptr)) != 0) + goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { @@ -762,6 +820,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, if ((error = semvalid(semid, semakptr)) != 0) goto done2; KASSERT(count == semakptr->u.sem_nsems, ("nsems changed")); + if ((error = cr_cansee_sem(td->td_ucred, semakptr)) != 0) + goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; for (i = 0; i < semakptr->u.sem_nsems; i++) @@ -774,6 +834,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, case GETZCNT: if ((error = semvalid(semid, semakptr)) != 0) goto done2; + if ((error = cr_cansee_sem(td->td_ucred, semakptr)) != 0) + goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { @@ -786,6 +848,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, case SETVAL: if ((error = semvalid(semid, semakptr)) != 0) goto done2; + if ((error = cr_cansee_sem(td->td_ucred, semakptr)) != 0) + goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_W))) goto done2; if (semnum < 0 || semnum >= semakptr->u.sem_nsems) { @@ -818,6 +882,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, if ((error = semvalid(semid, semakptr)) != 0) goto done2; KASSERT(count == semakptr->u.sem_nsems, ("nsems changed")); + if ((error = cr_cansee_sem(td->td_ucred, semakptr)) != 0) + goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_W))) goto done2; for (i = 0; i < semakptr->u.sem_nsems; i++) { @@ -872,6 +938,7 @@ sys_semget(struct thread *td, struct semget_args *uap) if (key != IPC_PRIVATE) { for (semid = 0; semid < seminfo.semmni; semid++) { if ((sema[semid].u.sem_perm.mode & SEM_ALLOC) && + sema[semid].cred && sema[semid].cred->cr_prison == cred->cr_prison && sema[semid].u.sem_perm.key == key) break; } @@ -1049,6 +1116,8 @@ sys_semop(struct thread *td, struct semop_args *uap) error = EINVAL; goto done2; } + if ((error = cr_cansee_sem(td->td_ucred, semakptr)) != 0) + goto done2; /* * Initial pass thru sops to see what permissions are needed. * Also perform any checks that don't need repeating on each @@ -1372,9 +1441,37 @@ semexit_myhook(void *arg, struct proc *p) static int sysctl_sema(SYSCTL_HANDLER_ARGS) { + int error; + struct sbuf sb; + struct semid_kernel tmp, empty; + int i; + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) + goto done; + sbuf_new_for_sysctl(&sb, NULL, sizeof(struct semid_kernel) * seminfo.semmni, req); - return (SYSCTL_OUT(req, sema, - sizeof(struct semid_kernel) * seminfo.semmni)); + bzero(&empty, sizeof(empty)); + for (i = 0; i < seminfo.semmni; i++) { + struct semid_kernel *semakptr; + + semakptr = &sema[i]; + if ((semakptr->u.sem_perm.mode & SEM_ALLOC) == 0 || + cr_cansee_sem(req->td->td_ucred, semakptr)) { + semakptr = ∅ + } else if (req->td->td_ucred->cr_prison != semakptr->cred->cr_prison) { + bcopy(semakptr, &tmp, sizeof(tmp)); + semakptr = &tmp; + semakptr->u.sem_perm.key = IPC_PRIVATE; + } + + sbuf_bcat(&sb, semakptr, sizeof(*semakptr)); + } + error = sbuf_finish(&sb); + sbuf_delete(&sb); + +done: + return (error); } #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ diff --git a/sys/kern/sysv_shm.c b/sys/kern/sysv_shm.c index 66a2a43..52760cf 100644 --- a/sys/kern/sysv_shm.c +++ b/sys/kern/sysv_shm.c @@ -86,6 +86,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include @@ -120,7 +121,7 @@ struct shmmap_state { }; static void shm_deallocate_segment(struct shmid_kernel *); -static int shm_find_segment_by_key(key_t); +static int shm_find_segment_by_key(struct prison *, key_t); static struct shmid_kernel *shm_find_segment(int, bool); static int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *); static void shmrealloc(void); @@ -130,6 +131,9 @@ static int shmunload(void); static void shmexit_myhook(struct vmspace *vm); static void shmfork_myhook(struct proc *p1, struct proc *p2); static int sysctl_shmsegs(SYSCTL_HANDLER_ARGS); +static int cr_cansee_shm(struct ucred *, struct shmid_kernel *); +static void shm_remove(struct shmid_kernel *, int); +static void sysvshm_cleanup_for_prison_myhook(struct prison *); /* * Tuneable values. @@ -189,12 +193,13 @@ static struct sx sysvshmsx; #define SYSVSHM_ASSERT_LOCKED() sx_assert(&sysvshmsx, SA_XLOCKED) static int -shm_find_segment_by_key(key_t key) +shm_find_segment_by_key(struct prison *pr, key_t key) { int i; for (i = 0; i < shmalloced; i++) if ((shmsegs[i].u.shm_perm.mode & SHMSEG_ALLOCATED) && + shmsegs[i].cred && shmsegs[i].cred->cr_prison == pr && shmsegs[i].u.shm_perm.key == key) return (i); return (-1); @@ -272,6 +277,45 @@ shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s) } static int +cr_cansee_shm(struct ucred *cred, struct shmid_kernel *shmseg) +{ + + if (shmseg->cred == NULL || prison_check(cred, shmseg->cred)) + return (EINVAL); + return (0); +} + +static void +shm_remove(struct shmid_kernel *shmseg, int segnum) +{ + + shmseg->u.shm_perm.key = IPC_PRIVATE; + shmseg->u.shm_perm.mode |= SHMSEG_REMOVED; + if (shmseg->u.shm_nattch <= 0) { + shm_deallocate_segment(shmseg); + shm_last_free = segnum; + } +} + +static void +sysvshm_cleanup_for_prison_myhook(struct prison *pr) +{ + int i; + + SYSVSHM_LOCK(); + for (i = 0; i < shmalloced; i++) { + struct shmid_kernel *shmseg; + + shmseg = &shmsegs[i]; + if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) && + shmseg->cred->cr_prison == pr) { + shm_remove(shmseg, i); + } + } + SYSVSHM_UNLOCK(); +} + +static int kern_shmdt_locked(struct thread *td, const void *shmaddr) { struct proc *p = td->td_proc; @@ -348,6 +392,9 @@ kern_shmat_locked(struct thread *td, int shmid, const void *shmaddr, shmseg = shm_find_segment(shmid, true); if (shmseg == NULL) return (EINVAL); + error = cr_cansee_shm(td->td_ucred, shmseg); + if (error != 0) + return (error); error = ipcperm(td, &shmseg->u.shm_perm, (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); if (error != 0) @@ -477,6 +524,9 @@ kern_shmctl_locked(struct thread *td, int shmid, int cmd, void *buf, shmseg = shm_find_segment(shmid, cmd != SHM_STAT); if (shmseg == NULL) return (EINVAL); + error = cr_cansee_shm(td->td_ucred, shmseg); + if (error != 0) + return (error); #ifdef MAC error = mac_sysvshm_check_shmctl(td->td_ucred, shmseg, cmd); if (error != 0) @@ -512,12 +562,8 @@ kern_shmctl_locked(struct thread *td, int shmid, int cmd, void *buf, error = ipcperm(td, &shmseg->u.shm_perm, IPC_M); if (error != 0) return (error); - shmseg->u.shm_perm.key = IPC_PRIVATE; - shmseg->u.shm_perm.mode |= SHMSEG_REMOVED; - if (shmseg->u.shm_nattch <= 0) { - shm_deallocate_segment(shmseg); - shm_last_free = IPCID_TO_IX(shmid); - } + + shm_remove(shmseg, IPCID_TO_IX(shmid)); break; #if 0 case SHM_LOCK: @@ -727,7 +773,7 @@ sys_shmget(struct thread *td, struct shmget_args *uap) if (uap->key == IPC_PRIVATE) { error = shmget_allocate_segment(td, uap, mode); } else { - segnum = shm_find_segment_by_key(uap->key); + segnum = shm_find_segment_by_key(td->td_ucred->cr_prison, uap->key); if (segnum >= 0) error = shmget_existing(td, uap, mode, segnum); else if ((uap->shmflg & IPC_CREAT) == 0) @@ -883,6 +929,7 @@ shminit(void) sx_init(&sysvshmsx, "sysvshmsx"); shmexit_hook = &shmexit_myhook; shmfork_hook = &shmfork_myhook; + sysvshm_cleanup_for_prison_hook = &sysvshm_cleanup_for_prison_myhook; error = syscall_helper_register(shm_syscalls); if (error != 0) @@ -923,6 +970,7 @@ shmunload(void) free(shmsegs, M_SHM); shmexit_hook = NULL; shmfork_hook = NULL; + sysvshm_cleanup_for_prison_hook = NULL; sx_destroy(&sysvshmsx); return (0); } @@ -931,9 +979,38 @@ static int sysctl_shmsegs(SYSCTL_HANDLER_ARGS) { int error; + struct sbuf sb; + struct shmid_kernel tmp, empty; + int i; SYSVSHM_LOCK(); - error = SYSCTL_OUT(req, shmsegs, shmalloced * sizeof(shmsegs[0])); + + error = sysctl_wire_old_buffer(req, 0); + if (error != 0) + goto done; + sbuf_new_for_sysctl(&sb, NULL, shmalloced * sizeof(shmsegs[0]), req); + + bzero(&empty, sizeof(empty)); + empty.u.shm_perm.mode = SHMSEG_FREE; + for (i = 0; i < shmalloced; i++) { + struct shmid_kernel *shmseg; + + shmseg = &shmsegs[i]; + if ((shmseg->u.shm_perm.mode & SHMSEG_ALLOCATED) == 0 || + cr_cansee_shm(req->td->td_ucred, &shmsegs[i])) { + shmseg = ∅ + } else if (req->td->td_ucred->cr_prison != shmseg->cred->cr_prison) { + bcopy(shmseg, &tmp, sizeof(tmp)); + shmseg = &tmp; + shmseg->u.shm_perm.key = IPC_PRIVATE; + } + + sbuf_bcat(&sb, shmseg, sizeof(*shmseg)); + } + error = sbuf_finish(&sb); + sbuf_delete(&sb); + +done: SYSVSHM_UNLOCK(); return (error); } @@ -977,6 +1054,11 @@ oshmctl(struct thread *td, struct oshmctl_args *uap) SYSVSHM_UNLOCK(); return (EINVAL); } + error = cr_cansee_shm(td->td_ucred, shmseg); + if (error != 0) { + SYSVSHM_UNLOCK(); + return (error); + } error = ipcperm(td, &shmseg->u.shm_perm, IPC_R); if (error != 0) { SYSVSHM_UNLOCK(); diff --git a/sys/sys/ipc.h b/sys/sys/ipc.h index e643d48..88b5f14 100644 --- a/sys/sys/ipc.h +++ b/sys/sys/ipc.h @@ -126,6 +126,7 @@ struct ipc_perm { struct thread; struct proc; struct vmspace; +struct prison; #if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || \ defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD7) @@ -133,9 +134,13 @@ void ipcperm_old2new(struct ipc_perm_old *, struct ipc_perm *); void ipcperm_new2old(struct ipc_perm *, struct ipc_perm_old *); #endif +void ipc_cleanup_for_prison(struct prison *); int ipcperm(struct thread *, struct ipc_perm *, int); extern void (*shmfork_hook)(struct proc *, struct proc *); extern void (*shmexit_hook)(struct vmspace *); +extern void (*sysvshm_cleanup_for_prison_hook)(struct prison *); +extern void (*sysvmsg_cleanup_for_prison_hook)(struct prison *); +extern void (*sysvsem_cleanup_for_prison_hook)(struct prison *); #else /* ! _KERNEL */