Hello, I want to run multiple instances of PostgreSQL with jail.
Changing UID is not suitable for my case, so I created a simple patch for stable/10 to separate SysV IPC namespace for each jail. In contrast to https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=48471 , this patch comes with; - All objects are visible by ipcs(1) whether in jails or not. - Trying to access the objects beyond the jail will be rejected with EACCES. - Treat (key_t, prison) pair as the key for a named object. - Very simple implementation; I just added to check msqkptr->cred->cr_prison == td->td_ucred->cr_prison, for example. Is this approach suitable for FreeBSD kernel? If you find it is useful, or bugs, please let me know. P.S. There is no way to know from userland which jails own the objects, so far. Regards, kikuchan
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 <sys/syscallsubr.h> #include <sys/sysctl.h> #include <sys/vnode.h> +#include <sys/ipc.h> #include <net/if.h> #include <net/vnet.h> @@ -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..ef339c6 100644 --- a/sys/kern/sysv_ipc.c +++ b/sys/kern/sysv_ipc.c @@ -47,9 +47,13 @@ __FBSDID("$FreeBSD$"); #include <sys/priv.h> #include <sys/proc.h> #include <sys/ucred.h> +#include <sys/jail.h> void (*shmfork_hook)(struct proc *, struct proc *) = NULL; void (*shmexit_hook)(struct vmspace *) = NULL; +void (*sysvshm_cleanup_for_prison_hook)(struct prison *) = NULL; +void (*sysvmsq_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 (sysvmsq_cleanup_for_prison_hook != NULL) + sysvmsq_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..36fe475 100644 --- a/sys/kern/sysv_msg.c +++ b/sys/kern/sysv_msg.c @@ -80,6 +80,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 void msq_remove(struct msqid_kernel *); +static int msq_check_prison(struct ucred *, struct msqid_kernel *); +static void sysvmsq_cleanup_for_prison_myhook(struct prison *); #ifdef MSG_DEBUG @@ -257,6 +260,7 @@ msginit() #endif } mtx_init(&msq_mtx, "msq", NULL, MTX_DEF); + sysvmsq_cleanup_for_prison_hook = &sysvmsq_cleanup_for_prison_myhook; error = syscall_helper_register(msg_syscalls); if (error != 0) @@ -282,6 +286,7 @@ msgunload() #ifdef COMPAT_FREEBSD32 syscall32_helper_unregister(msg32_syscalls); #endif + sysvmsq_cleanup_for_prison_hook = NULL; for (msqid = 0; msqid < msginfo.msgmni; msqid++) { /* @@ -372,6 +377,69 @@ msg_freehdr(msghdr) #endif } +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 int +msq_check_prison(struct ucred *cred, struct msqid_kernel *msqkptr) +{ + + if (msqkptr->cred && msqkptr->cred->cr_prison != cred->cr_prison) + return (EACCES); + return (0); +} + +static void +sysvmsq_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; @@ -447,6 +515,8 @@ kern_msgctl(td, msqid, cmd, msqbuf) case IPC_RMID: { struct msg *msghdr; + if ((error = msq_check_prison(td->td_ucred, msqkptr))) + goto done2; if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M))) goto done2; @@ -468,42 +538,14 @@ 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; case IPC_SET: + if ((error = msq_check_prison(td->td_ucred, msqkptr))) + goto done2; if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M))) goto done2; if (msqbuf->msg_qbytes > msqkptr->u.msg_qbytes) { @@ -530,6 +572,8 @@ kern_msgctl(td, msqid, cmd, msqbuf) break; case IPC_STAT: + if ((error = msq_check_prison(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; @@ -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 = msq_check_prison(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 = msq_check_prison(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; diff --git a/sys/kern/sysv_sem.c b/sys/kern/sysv_sem.c index f9ff217..edd2cd7 100644 --- a/sys/kern/sysv_sem.c +++ b/sys/kern/sysv_sem.c @@ -79,6 +79,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 void sem_remove(int semidx, struct ucred *cred); +static int sem_check_prison(struct ucred *cred, struct semid_kernel *semakptr); +static void sysvsem_cleanup_for_prison_myhook(struct prison *pr); #ifndef _SYS_SYSPROTO_H_ struct __semctl_args; @@ -287,6 +290,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 +317,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 +511,70 @@ semvalid(int semid, struct semid_kernel *semakptr) semakptr->u.sem_perm.seq != IPCID_TO_SEQ(semid) ? EINVAL : 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 int +sem_check_prison(struct ucred *cred, struct semid_kernel *semakptr) +{ + + if (semakptr->cred && semakptr->cred->cr_prison != cred->cr_prison) + return (EACCES); + return (0); +} + +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 +679,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, error = EINVAL; goto done2; } + if ((error = sem_check_prison(td->td_ucred, semakptr)) != 0) + goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_R))) goto done2; #ifdef MAC @@ -645,41 +716,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 = sem_check_prison(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 = sem_check_prison(td->td_ucred, semakptr)) != 0) + goto done2; if ((error = ipcperm(td, &semakptr->u.sem_perm, IPC_M))) goto done2; sbuf = arg->buf; @@ -693,6 +741,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 = sem_check_prison(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 +751,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, case GETNCNT: if ((error = semvalid(semid, semakptr)) != 0) goto done2; + if ((error = sem_check_prison(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 +765,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, case GETPID: if ((error = semvalid(semid, semakptr)) != 0) goto done2; + if ((error = sem_check_prison(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 +779,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, case GETVAL: if ((error = semvalid(semid, semakptr)) != 0) goto done2; + if ((error = sem_check_prison(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 +818,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 = sem_check_prison(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 +832,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, case GETZCNT: if ((error = semvalid(semid, semakptr)) != 0) goto done2; + if ((error = sem_check_prison(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 +846,8 @@ kern_semctl(struct thread *td, int semid, int semnum, int cmd, case SETVAL: if ((error = semvalid(semid, semakptr)) != 0) goto done2; + if ((error = sem_check_prison(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 +880,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 = sem_check_prison(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 +936,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 +1114,8 @@ sys_semop(struct thread *td, struct semop_args *uap) error = EINVAL; goto done2; } + if ((error = sem_check_prison(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 diff --git a/sys/kern/sysv_shm.c b/sys/kern/sysv_shm.c index 66a2a43..a3b47e8 100644 --- a/sys/kern/sysv_shm.c +++ b/sys/kern/sysv_shm.c @@ -120,7 +120,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(key_t, struct prison *); 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 +130,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 void shm_remove(struct shmid_kernel *, int); +static int shm_check_prison(struct ucred *, struct shmid_kernel *); +static void sysvshm_cleanup_for_prison_myhook(struct prison *); /* * Tuneable values. @@ -189,12 +192,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(key_t key, struct prison *pr) { 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); @@ -271,6 +275,45 @@ shm_delete_mapping(struct vmspace *vm, struct shmmap_state *shmmap_s) 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 int +shm_check_prison(struct ucred *cred, struct shmid_kernel *shmseg) +{ + + if (shmseg->cred && shmseg->cred->cr_prison != cred->cr_prison) + return (EACCES); + return (0); +} + +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) { @@ -348,6 +391,9 @@ kern_shmat_locked(struct thread *td, int shmid, const void *shmaddr, shmseg = shm_find_segment(shmid, true); if (shmseg == NULL) return (EINVAL); + error = shm_check_prison(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) @@ -485,6 +531,9 @@ kern_shmctl_locked(struct thread *td, int shmid, int cmd, void *buf, switch (cmd) { case SHM_STAT: case IPC_STAT: + error = shm_check_prison(td->td_ucred, shmseg); + if (error != 0) + return (error); error = ipcperm(td, &shmseg->u.shm_perm, IPC_R); if (error != 0) return (error); @@ -498,6 +547,9 @@ kern_shmctl_locked(struct thread *td, int shmid, int cmd, void *buf, break; case IPC_SET: shmidp = (struct shmid_ds *)buf; + error = shm_check_prison(td->td_ucred, shmseg); + if (error != 0) + return (error); error = ipcperm(td, &shmseg->u.shm_perm, IPC_M); if (error != 0) return (error); @@ -509,15 +561,14 @@ kern_shmctl_locked(struct thread *td, int shmid, int cmd, void *buf, shmseg->u.shm_ctime = time_second; break; case IPC_RMID: + error = shm_check_prison(td->td_ucred, shmseg); + if (error != 0) + return (error); 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 +778,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(uap->key, td->td_ucred->cr_prison); if (segnum >= 0) error = shmget_existing(td, uap, mode, segnum); else if ((uap->shmflg & IPC_CREAT) == 0) @@ -883,6 +934,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 +975,7 @@ shmunload(void) free(shmsegs, M_SHM); shmexit_hook = NULL; shmfork_hook = NULL; + sysvshm_cleanup_for_prison_hook = NULL; sx_destroy(&sysvshmsx); return (0); } @@ -977,6 +1030,9 @@ oshmctl(struct thread *td, struct oshmctl_args *uap) SYSVSHM_UNLOCK(); return (EINVAL); } + error = shm_check_prison(td->td_ucred, shmseg); + if (error != 0) + 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..a53a851 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 (*sysvmsq_cleanup_for_prison_hook)(struct prison *); +extern void (*sysvsem_cleanup_for_prison_hook)(struct prison *); #else /* ! _KERNEL */
_______________________________________________ freebsd-jail@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/freebsd-jail To unsubscribe, send any mail to "freebsd-jail-unsubscr...@freebsd.org"