diff options
author | mbr <mbr@FreeBSD.org> | 2003-05-05 09:22:58 +0000 |
---|---|---|
committer | mbr <mbr@FreeBSD.org> | 2003-05-05 09:22:58 +0000 |
commit | 98d5255d63c13a42c0aa2a18fa0b934016966057 (patch) | |
tree | 9ac126232eca288ee23257e5843e5279793d14e4 | |
parent | b958e56c35be663c8232acc9e8a75afaa2fa26a4 (diff) | |
download | FreeBSD-src-98d5255d63c13a42c0aa2a18fa0b934016966057.zip FreeBSD-src-98d5255d63c13a42c0aa2a18fa0b934016966057.tar.gz |
Change the semantics of sysv shm emulation to take a additional
argument to the functions shm{at,ctl}1 and shm_find_segment_by_shmid{x}.
The BSD semantics didn't allow the usage of shared segment after
being marked for removal through IPC_RMID.
The patch involves the following functions:
- shmat
- shmctl
- shm_find_segment_by_shmid
- shm_find_segment_by_shmidx
- linux_shmat
- linux_shmctl
Submitted by: Orlando Bassotto <orlando.bassotto@ieo-research.it>
Reviewed by: marcel
-rw-r--r-- | sys/compat/linux/linux_ipc.c | 133 | ||||
-rw-r--r-- | sys/kern/sysv_shm.c | 150 | ||||
-rw-r--r-- | sys/sys/syscallsubr.h | 4 |
3 files changed, 179 insertions, 108 deletions
diff --git a/sys/compat/linux/linux_ipc.c b/sys/compat/linux/linux_ipc.c index 92fa13d..4f95277 100644 --- a/sys/compat/linux/linux_ipc.c +++ b/sys/compat/linux/linux_ipc.c @@ -30,6 +30,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/syscallsubr.h> #include <sys/sysproto.h> #include <sys/proc.h> #include <sys/limits.h> @@ -703,81 +704,93 @@ linux_shmctl(struct thread *td, struct linux_shmctl_args *args) struct l_shmid_ds linux_shmid; struct l_shminfo linux_shminfo; struct l_shm_info linux_shm_info; - struct shmctl_args /* { - int shmid; - int cmd; - struct shmid_ds *buf; - } */ bsd_args; + struct shmid_ds bsd_shmid; + size_t bufsz; int error; - caddr_t sg = stackgap_init(); switch (args->cmd & ~LINUX_IPC_64) { - case LINUX_IPC_INFO: - bsd_args.shmid = args->shmid; - bsd_args.cmd = IPC_INFO; - bsd_args.buf = (struct shmid_ds*)stackgap_alloc(&sg, sizeof(struct shminfo)); - if ((error = shmctl(td, &bsd_args))) + case LINUX_IPC_INFO: { + struct shminfo bsd_shminfo; + + /* Perform shmctl wanting removed segments lookup */ + error = kern_shmctl(td, args->shmid, IPC_INFO, + (void *)&bsd_shminfo, &bufsz, 1); + if (error) return error; - bsd_to_linux_shminfo( (struct shminfo *)bsd_args.buf, &linux_shminfo ); - return (linux_shminfo_pushdown(args->cmd & LINUX_IPC_64, - &linux_shminfo, (caddr_t)args->buf)); - - case LINUX_SHM_INFO: - bsd_args.shmid = args->shmid; - bsd_args.cmd = SHM_INFO; - bsd_args.buf = (struct shmid_ds*)stackgap_alloc(&sg, sizeof(struct shm_info)); - if ((error = shmctl(td, &bsd_args))) - return error; - bsd_to_linux_shm_info( (struct shm_info *)bsd_args.buf, &linux_shm_info ); - return copyout(&linux_shm_info, (caddr_t)args->buf, sizeof(struct shm_info)); - - case LINUX_IPC_STAT: - bsd_args.shmid = args->shmid; - bsd_args.cmd = IPC_STAT; - bsd_args.buf = (struct shmid_ds*)stackgap_alloc(&sg, sizeof(struct shmid_ds)); - if ((error = shmctl(td, &bsd_args))) - return error; - bsd_to_linux_shmid_ds(bsd_args.buf, &linux_shmid); - return (linux_shmid_pushdown(args->cmd & LINUX_IPC_64, - &linux_shmid, (caddr_t)args->buf)); + + bsd_to_linux_shminfo(&bsd_shminfo, &linux_shminfo); - case LINUX_SHM_STAT: - bsd_args.shmid = args->shmid; - bsd_args.cmd = SHM_STAT; - bsd_args.buf = (struct shmid_ds*)stackgap_alloc(&sg, sizeof(struct shmid_ds)); - if ((error = shmctl(td, &bsd_args))) { - return error; + return (linux_shminfo_pushdown(args->cmd & LINUX_IPC_64, + &linux_shminfo, (caddr_t)args->buf)); } - bsd_to_linux_shmid_ds(bsd_args.buf, &linux_shmid); - return (linux_shmid_pushdown(args->cmd & LINUX_IPC_64, + + case LINUX_SHM_INFO: { + struct shm_info bsd_shm_info; + + /* Perform shmctl wanting removed segments lookup */ + error = kern_shmctl(td, args->shmid, SHM_INFO, + (void *)&bsd_shm_info, &bufsz, 1); + if (error) + return error; + + bsd_to_linux_shm_info(&bsd_shm_info, &linux_shm_info); + + return copyout(&linux_shm_info, (caddr_t)args->buf, + sizeof(struct l_shm_info)); + } + + case LINUX_IPC_STAT: + /* Perform shmctl wanting removed segments lookup */ + error = kern_shmctl(td, args->shmid, IPC_STAT, + (void *)&bsd_shmid, &bufsz, 1); + if (error) + return error; + + bsd_to_linux_shmid_ds(&bsd_shmid, &linux_shmid); + + return (linux_shmid_pushdown(args->cmd & LINUX_IPC_64, &linux_shmid, (caddr_t)args->buf)); + case LINUX_SHM_STAT: + /* Perform shmctl wanting removed segments lookup */ + error = kern_shmctl(td, args->shmid, IPC_STAT, + (void *)&bsd_shmid, &bufsz, 1); + if (error) + return error; + + bsd_to_linux_shmid_ds(&bsd_shmid, &linux_shmid); + + return (linux_shmid_pushdown(args->cmd & LINUX_IPC_64, + &linux_shmid, (caddr_t)args->buf)); + case LINUX_IPC_SET: error = linux_shmid_pullup(args->cmd & LINUX_IPC_64, &linux_shmid, (caddr_t)args->buf); - if (error != 0) - return error; - bsd_args.buf = (struct shmid_ds*)stackgap_alloc(&sg, sizeof(struct shmid_ds)); - linux_to_bsd_shmid_ds(&linux_shmid, bsd_args.buf); - bsd_args.shmid = args->shmid; - bsd_args.cmd = IPC_SET; - return shmctl(td, &bsd_args); - - case LINUX_IPC_RMID: - bsd_args.shmid = args->shmid; - bsd_args.cmd = IPC_RMID; + if (error) + return error; + + linux_to_bsd_shmid_ds(&linux_shmid, &bsd_shmid); + + /* Perform shmctl wanting removed segments lookup */ + return kern_shmctl(td, args->shmid, IPC_SET, + (void *)&bsd_shmid, &bufsz, 1); + + case LINUX_IPC_RMID: { + void *buf; + if (args->buf == NULL) - bsd_args.buf = NULL; + buf = NULL; else { - error = linux_shmid_pullup(args->cmd & LINUX_IPC_64, - &linux_shmid, (caddr_t)args->buf); - if (error != 0) - return error; - bsd_args.buf = (struct shmid_ds*)stackgap_alloc(&sg, sizeof(struct shmid_ds)); - linux_to_bsd_shmid_ds(&linux_shmid, bsd_args.buf); + error = linux_shmid_pullup(args->cmd & LINUX_IPC_64, + &linux_shmid, (caddr_t)args->buf); + if (error) + return error; + linux_to_bsd_shmid_ds(&linux_shmid, &bsd_shmid); + buf = (void *)&bsd_shmid; } - return shmctl(td, &bsd_args); + return kern_shmctl(td, args->shmid, IPC_RMID, buf, &bufsz, 1); + } case LINUX_SHM_LOCK: case LINUX_SHM_UNLOCK: diff --git a/sys/kern/sysv_shm.c b/sys/kern/sysv_shm.c index ec70337..3600ebe 100644 --- a/sys/kern/sysv_shm.c +++ b/sys/kern/sysv_shm.c @@ -46,6 +46,7 @@ #include <sys/mutex.h> #include <sys/stat.h> #include <sys/syscall.h> +#include <sys/syscallsubr.h> #include <sys/sysent.h> #include <sys/sysproto.h> #include <sys/jail.h> @@ -95,8 +96,8 @@ struct shmmap_state { static void shm_deallocate_segment(struct shmid_ds *); static int shm_find_segment_by_key(key_t); -static struct shmid_ds *shm_find_segment_by_shmid(int); -static struct shmid_ds *shm_find_segment_by_shmidx(int); +static struct shmid_ds *shm_find_segment_by_shmid(int, int); +static struct shmid_ds *shm_find_segment_by_shmidx(int, int); static int shm_delete_mapping(struct vmspace *vm, struct shmmap_state *); static void shmrealloc(void); static void shminit(void); @@ -163,8 +164,7 @@ shm_find_segment_by_key(key) } static struct shmid_ds * -shm_find_segment_by_shmid(shmid) - int shmid; +shm_find_segment_by_shmid(int shmid, int wantrem) { int segnum; struct shmid_ds *shmseg; @@ -173,23 +173,23 @@ shm_find_segment_by_shmid(shmid) if (segnum < 0 || segnum >= shmalloced) return (NULL); shmseg = &shmsegs[segnum]; - if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) - != SHMSEG_ALLOCATED || + if (!((shmseg->shm_perm.mode & SHMSEG_ALLOCATED) || + (wantrem && !(shmseg->shm_perm.mode & SHMSEG_REMOVED))) || shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) return (NULL); return (shmseg); } static struct shmid_ds * -shm_find_segment_by_shmidx(int segnum) +shm_find_segment_by_shmidx(int segnum, int wantrem) { struct shmid_ds *shmseg; if (segnum < 0 || segnum >= shmalloced) return (NULL); shmseg = &shmsegs[segnum]; - if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) - != SHMSEG_ALLOCATED ) + if (!((shmseg->shm_perm.mode & SHMSEG_ALLOCATED) || + (wantrem && !(shmseg->shm_perm.mode & SHMSEG_REMOVED)))) return (NULL); return (shmseg); } @@ -293,9 +293,12 @@ struct shmat_args { * MPSAFE */ int -shmat(td, uap) +kern_shmat(td, shmid, shmaddr, shmflg, wantrem) struct thread *td; - struct shmat_args *uap; + int shmid; + const void *shmaddr; + int shmflg; + int wantrem; { struct proc *p = td->td_proc; int i, flags; @@ -319,13 +322,13 @@ shmat(td, uap) shmmap_s[i].shmid = -1; p->p_vmspace->vm_shm = shmmap_s; } - shmseg = shm_find_segment_by_shmid(uap->shmid); + shmseg = shm_find_segment_by_shmid(shmid, wantrem); if (shmseg == NULL) { error = EINVAL; goto done2; } error = ipcperm(td, &shmseg->shm_perm, - (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); + (shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); if (error) goto done2; for (i = 0; i < shminfo.shmseg; i++) { @@ -343,15 +346,15 @@ shmat(td, uap) #else prot = VM_PROT_READ; #endif - if ((uap->shmflg & SHM_RDONLY) == 0) + if ((shmflg & SHM_RDONLY) == 0) prot |= VM_PROT_WRITE; flags = MAP_ANON | MAP_SHARED; - if (uap->shmaddr) { + if (shmaddr) { flags |= MAP_FIXED; - if (uap->shmflg & SHM_RND) { - attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1); - } else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0) { - attach_va = (vm_offset_t)uap->shmaddr; + if (shmflg & SHM_RND) { + attach_va = (vm_offset_t)shmaddr & ~(SHMLBA-1); + } else if (((vm_offset_t)shmaddr & (SHMLBA-1)) == 0) { + attach_va = (vm_offset_t)shmaddr; } else { error = EINVAL; goto done2; @@ -377,7 +380,7 @@ shmat(td, uap) attach_va, attach_va + size, VM_INHERIT_SHARE); shmmap_s->va = attach_va; - shmmap_s->shmid = uap->shmid; + shmmap_s->shmid = shmid; shmseg->shm_lpid = p->p_pid; shmseg->shm_atime = time_second; shmseg->shm_nattch++; @@ -387,6 +390,14 @@ done2: return (error); } +int +shmat(td, uap) + struct thread *td; + struct shmat_args *uap; +{ + return kern_shmat(td, uap->shmid, uap->shmaddr, uap->shmflg, 0); +} + struct oshmid_ds { struct ipc_perm shm_perm; /* operation perms */ int shm_segsz; /* size of segment (bytes) */ @@ -421,7 +432,7 @@ oshmctl(td, uap) if (!jail_sysvipc_allowed && jailed(td->td_ucred)) return (ENOSYS); mtx_lock(&Giant); - shmseg = shm_find_segment_by_shmid(uap->shmid); + shmseg = shm_find_segment_by_shmid(uap->shmid, 0); if (shmseg == NULL) { error = EINVAL; goto done2; @@ -469,22 +480,26 @@ struct shmctl_args { * MPSAFE */ int -shmctl(td, uap) +kern_shmctl(td, shmid, cmd, buf, bufsz, wantrem) struct thread *td; - struct shmctl_args *uap; + int shmid; + int cmd; + void *buf; + size_t *bufsz; + int wantrem; { int error = 0; - struct shmid_ds inbuf; struct shmid_ds *shmseg; if (!jail_sysvipc_allowed && jailed(td->td_ucred)) return (ENOSYS); + mtx_lock(&Giant); - switch (uap->cmd) { + switch (cmd) { case IPC_INFO: - error = copyout(&shminfo, uap->buf, sizeof(shminfo)); - if (error) - goto done2; + memcpy(buf, &shminfo, sizeof(shminfo)); + if (bufsz) + *bufsz = sizeof(shminfo); td->td_retval[0] = shmalloced; goto done2; case SHM_INFO: { @@ -495,47 +510,48 @@ shmctl(td, uap) shm_info.shm_swp = 0; /*XXX where to get from ? */ shm_info.swap_attempts = 0; /*XXX where to get from ? */ shm_info.swap_successes = 0; /*XXX where to get from ? */ - error = copyout(&shm_info, uap->buf, sizeof(shm_info)); - if (error) - goto done2; + memcpy(buf, &shm_info, sizeof(shm_info)); + if (bufsz) + *bufsz = sizeof(shm_info); td->td_retval[0] = shmalloced; goto done2; } } - if( (uap->cmd) == SHM_STAT ) - shmseg = shm_find_segment_by_shmidx(uap->shmid); + if (cmd == SHM_STAT) + shmseg = shm_find_segment_by_shmidx(shmid, wantrem); else - shmseg = shm_find_segment_by_shmid(uap->shmid); + shmseg = shm_find_segment_by_shmid(shmid, wantrem); if (shmseg == NULL) { error = EINVAL; goto done2; } - switch (uap->cmd) { + switch (cmd) { case SHM_STAT: case IPC_STAT: error = ipcperm(td, &shmseg->shm_perm, IPC_R); if (error) goto done2; - error = copyout(shmseg, uap->buf, sizeof(inbuf)); - if (error) - goto done2; - else if( (uap->cmd) == SHM_STAT ) - td->td_retval[0] = IXSEQ_TO_IPCID( uap->shmid, shmseg->shm_perm ); + memcpy(buf, shmseg, sizeof(struct shmid_ds)); + if (bufsz) + *bufsz = sizeof(struct shmid_ds); + if (cmd == SHM_STAT) + td->td_retval[0] = IXSEQ_TO_IPCID(shmid, shmseg->shm_perm); break; - case IPC_SET: + case IPC_SET: { + struct shmid_ds *shmid; + + shmid = (struct shmid_ds *)buf; error = ipcperm(td, &shmseg->shm_perm, IPC_M); if (error) goto done2; - error = copyin(uap->buf, &inbuf, sizeof(inbuf)); - if (error) - goto done2; - shmseg->shm_perm.uid = inbuf.shm_perm.uid; - shmseg->shm_perm.gid = inbuf.shm_perm.gid; + shmseg->shm_perm.uid = shmid->shm_perm.uid; + shmseg->shm_perm.gid = shmid->shm_perm.gid; shmseg->shm_perm.mode = (shmseg->shm_perm.mode & ~ACCESSPERMS) | - (inbuf.shm_perm.mode & ACCESSPERMS); + (shmid->shm_perm.mode & ACCESSPERMS); shmseg->shm_ctime = time_second; break; + } case IPC_RMID: error = ipcperm(td, &shmseg->shm_perm, IPC_M); if (error) @@ -544,7 +560,7 @@ shmctl(td, uap) shmseg->shm_perm.mode |= SHMSEG_REMOVED; if (shmseg->shm_nattch <= 0) { shm_deallocate_segment(shmseg); - shm_last_free = IPCID_TO_IX(uap->shmid); + shm_last_free = IPCID_TO_IX(shmid); } break; #if 0 @@ -560,6 +576,44 @@ done2: return (error); } +int +shmctl(td, uap) + struct thread *td; + struct shmctl_args *uap; +{ + int error = 0; + struct shmid_ds buf; + size_t bufsz; + + /* IPC_SET needs to copyin the buffer before calling kern_shmctl */ + if (uap->cmd == IPC_SET) { + if ((error = copyin(uap->buf, &buf, sizeof(struct shmid_ds)))) + goto done; + } + + error = kern_shmctl(td, uap->shmid, uap->cmd, (void *)&buf, &bufsz, 0); + if (error) + goto done; + + /* Cases in which we need to copyout */ + switch (uap->cmd) { + case IPC_INFO: + case SHM_INFO: + case SHM_STAT: + case IPC_STAT: + error = copyout(&buf, uap->buf, bufsz); + break; + } + +done: + if (error) { + /* Invalidate the return value */ + td->td_retval[0] = -1; + } + return (error); +} + + #ifndef _SYS_SYSPROTO_H_ struct shmget_args { key_t key; diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index cac8091..9f1eb11 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -70,6 +70,10 @@ int kern_rename(struct thread *td, char *from, char *to, int kern_rmdir(struct thread *td, char *path, enum uio_seg pathseg); int kern_select(struct thread *td, int nd, fd_set *fd_in, fd_set *fd_ou, fd_set *fd_ex, struct timeval *tvp); +int kern_shmat(struct thread *td, int shmid, const void *shmaddr, + int shmflg, int wantrem); +int kern_shmctl(struct thread *td, int shmid, int cmd, void *buf, + size_t *bufsz, int wantrem); int kern_sigaction(struct thread *td, int sig, struct sigaction *act, struct sigaction *oact, int flags); int kern_sigaltstack(struct thread *td, stack_t *ss, stack_t *oss); |