X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=ipc%2Fshm.c;h=242c3f66493a1c1cdf578a1b5fd9eb7777ade965;hb=7d69a1f4a72b18876c99c697692b78339d491568;hp=1d6cf08d950b93c9f5223c7ead161b6f08ed9877;hpb=6ade43fbbcc3c12f0ddba112351d14d6c82ae476;p=safe%2Fjmp%2Flinux-2.6 diff --git a/ipc/shm.c b/ipc/shm.c index 1d6cf08..242c3f6 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -13,9 +13,14 @@ * Shared /dev/zero support, Kanoj Sarcar * Move the mm functionality over to mm/shmem.c, Christoph Rohland * + * support for audit of ipc object properties and permission changes + * Dustin Kirkland + * + * namespaces support + * OpenVZ, SWsoft Inc. + * Pavel Emelianov */ -#include #include #include #include @@ -23,104 +28,168 @@ #include #include #include -#include #include #include #include #include +#include #include +#include +#include +#include +#include #include #include "util.h" -#define shm_flags shm_perm.mode +struct shm_file_data { + int id; + struct ipc_namespace *ns; + struct file *file; + const struct vm_operations_struct *vm_ops; +}; + +#define shm_file_data(file) (*((struct shm_file_data **)&(file)->private_data)) -static struct file_operations shm_file_operations; +static const struct file_operations shm_file_operations; static struct vm_operations_struct shm_vm_ops; -static struct ipc_ids shm_ids; +static struct ipc_ids init_shm_ids; -#define shm_lock(id) ((struct shmid_kernel*)ipc_lock(&shm_ids,id)) -#define shm_unlock(shp) ipc_unlock(&(shp)->shm_perm) -#define shm_get(id) ((struct shmid_kernel*)ipc_get(&shm_ids,id)) -#define shm_buildid(id, seq) \ - ipc_buildid(&shm_ids, id, seq) +#define shm_ids(ns) (*((ns)->ids[IPC_SHM_IDS])) -static int newseg (key_t key, int shmflg, size_t size); -static void shm_open (struct vm_area_struct *shmd); -static void shm_close (struct vm_area_struct *shmd); +#define shm_lock(ns, id) \ + ((struct shmid_kernel*)ipc_lock(&shm_ids(ns),id)) +#define shm_unlock(shp) \ + ipc_unlock(&(shp)->shm_perm) +#define shm_get(ns, id) \ + ((struct shmid_kernel*)ipc_get(&shm_ids(ns),id)) +#define shm_buildid(ns, id, seq) \ + ipc_buildid(&shm_ids(ns), id, seq) + +static int newseg (struct ipc_namespace *ns, key_t key, + int shmflg, size_t size); +static void shm_open(struct vm_area_struct *vma); +static void shm_close(struct vm_area_struct *vma); +static void shm_destroy (struct ipc_namespace *ns, struct shmid_kernel *shp); #ifdef CONFIG_PROC_FS -static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data); +static int sysvipc_shm_proc_show(struct seq_file *s, void *it); #endif -size_t shm_ctlmax = SHMMAX; -size_t shm_ctlall = SHMALL; -int shm_ctlmni = SHMMNI; +static void __shm_init_ns(struct ipc_namespace *ns, struct ipc_ids *ids) +{ + ns->ids[IPC_SHM_IDS] = ids; + ns->shm_ctlmax = SHMMAX; + ns->shm_ctlall = SHMALL; + ns->shm_ctlmni = SHMMNI; + ns->shm_tot = 0; + ipc_init_ids(ids, 1); +} + +static void do_shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *shp) +{ + if (shp->shm_nattch){ + shp->shm_perm.mode |= SHM_DEST; + /* Do not find it any more */ + shp->shm_perm.key = IPC_PRIVATE; + shm_unlock(shp); + } else + shm_destroy(ns, shp); +} + +int shm_init_ns(struct ipc_namespace *ns) +{ + struct ipc_ids *ids; + + ids = kmalloc(sizeof(struct ipc_ids), GFP_KERNEL); + if (ids == NULL) + return -ENOMEM; + + __shm_init_ns(ns, ids); + return 0; +} + +void shm_exit_ns(struct ipc_namespace *ns) +{ + int i; + struct shmid_kernel *shp; + + mutex_lock(&shm_ids(ns).mutex); + for (i = 0; i <= shm_ids(ns).max_id; i++) { + shp = shm_lock(ns, i); + if (shp == NULL) + continue; -static int shm_tot; /* total number of shared memory pages */ + do_shm_rmid(ns, shp); + } + mutex_unlock(&shm_ids(ns).mutex); + + ipc_fini_ids(ns->ids[IPC_SHM_IDS]); + kfree(ns->ids[IPC_SHM_IDS]); + ns->ids[IPC_SHM_IDS] = NULL; +} void __init shm_init (void) { - ipc_init_ids(&shm_ids, 1); -#ifdef CONFIG_PROC_FS - create_proc_read_entry("sysvipc/shm", 0, NULL, sysvipc_shm_read_proc, NULL); -#endif + __shm_init_ns(&init_ipc_ns, &init_shm_ids); + ipc_init_proc_interface("sysvipc/shm", + " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime\n", + IPC_SHM_IDS, sysvipc_shm_proc_show); } -static inline int shm_checkid(struct shmid_kernel *s, int id) +static inline int shm_checkid(struct ipc_namespace *ns, + struct shmid_kernel *s, int id) { - if (ipc_checkid(&shm_ids,&s->shm_perm,id)) + if (ipc_checkid(&shm_ids(ns), &s->shm_perm, id)) return -EIDRM; return 0; } -static inline struct shmid_kernel *shm_rmid(int id) +static inline struct shmid_kernel *shm_rmid(struct ipc_namespace *ns, int id) { - return (struct shmid_kernel *)ipc_rmid(&shm_ids,id); + return (struct shmid_kernel *)ipc_rmid(&shm_ids(ns), id); } -static inline int shm_addid(struct shmid_kernel *shp) +static inline int shm_addid(struct ipc_namespace *ns, struct shmid_kernel *shp) { - return ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni); + return ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni); } -static inline void shm_inc (int id) { +/* This is called by fork, once for every shm attach. */ +static void shm_open(struct vm_area_struct *vma) +{ + struct file *file = vma->vm_file; + struct shm_file_data *sfd = shm_file_data(file); struct shmid_kernel *shp; - if(!(shp = shm_lock(id))) - BUG(); + shp = shm_lock(sfd->ns, sfd->id); + BUG_ON(!shp); shp->shm_atim = get_seconds(); shp->shm_lprid = current->tgid; shp->shm_nattch++; shm_unlock(shp); } -/* This is called by fork, once for every shm attach. */ -static void shm_open (struct vm_area_struct *shmd) -{ - shm_inc (shmd->vm_file->f_dentry->d_inode->i_ino); -} - /* * shm_destroy - free the struct shmid_kernel * * @shp: struct to free * - * It has to be called with shp and shm_ids.sem locked, + * It has to be called with shp and shm_ids.mutex locked, * but returns with shp unlocked and freed. */ -static void shm_destroy (struct shmid_kernel *shp) +static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) { - shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT; - shm_rmid (shp->id); + ns->shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT; + shm_rmid(ns, shp->id); shm_unlock(shp); if (!is_file_hugepages(shp->shm_file)) shmem_lock(shp->shm_file, 0, shp->mlock_user); else - user_shm_unlock(shp->shm_file->f_dentry->d_inode->i_size, + user_shm_unlock(shp->shm_file->f_path.dentry->d_inode->i_size, shp->mlock_user); fput (shp->shm_file); security_shm_free(shp); @@ -128,55 +197,144 @@ static void shm_destroy (struct shmid_kernel *shp) } /* - * remove the attach descriptor shmd. + * remove the attach descriptor vma. * free memory for segment if it is marked destroyed. * The descriptor has already been removed from the current->mm->mmap list * and will later be kfree()d. */ -static void shm_close (struct vm_area_struct *shmd) +static void shm_close(struct vm_area_struct *vma) { - struct file * file = shmd->vm_file; - int id = file->f_dentry->d_inode->i_ino; + struct file * file = vma->vm_file; + struct shm_file_data *sfd = shm_file_data(file); struct shmid_kernel *shp; + struct ipc_namespace *ns = sfd->ns; - down (&shm_ids.sem); + mutex_lock(&shm_ids(ns).mutex); /* remove from the list of attaches of the shm segment */ - if(!(shp = shm_lock(id))) - BUG(); + shp = shm_lock(ns, sfd->id); + BUG_ON(!shp); shp->shm_lprid = current->tgid; shp->shm_dtim = get_seconds(); shp->shm_nattch--; if(shp->shm_nattch == 0 && - shp->shm_flags & SHM_DEST) - shm_destroy (shp); + shp->shm_perm.mode & SHM_DEST) + shm_destroy(ns, shp); else shm_unlock(shp); - up (&shm_ids.sem); + mutex_unlock(&shm_ids(ns).mutex); +} + +static struct page *shm_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + struct file *file = vma->vm_file; + struct shm_file_data *sfd = shm_file_data(file); + + return sfd->vm_ops->nopage(vma, address, type); +} + +#ifdef CONFIG_NUMA +int shm_set_policy(struct vm_area_struct *vma, struct mempolicy *new) +{ + struct file *file = vma->vm_file; + struct shm_file_data *sfd = shm_file_data(file); + int err = 0; + if (sfd->vm_ops->set_policy) + err = sfd->vm_ops->set_policy(vma, new); + return err; +} + +struct mempolicy *shm_get_policy(struct vm_area_struct *vma, unsigned long addr) +{ + struct file *file = vma->vm_file; + struct shm_file_data *sfd = shm_file_data(file); + struct mempolicy *pol = NULL; + + if (sfd->vm_ops->get_policy) + pol = sfd->vm_ops->get_policy(vma, addr); + else if (vma->vm_policy) + pol = vma->vm_policy; + else + pol = current->mempolicy; + return pol; } +#endif static int shm_mmap(struct file * file, struct vm_area_struct * vma) { - file_accessed(file); + struct shm_file_data *sfd = shm_file_data(file); + int ret; + + ret = sfd->file->f_op->mmap(sfd->file, vma); + if (ret != 0) + return ret; + sfd->vm_ops = vma->vm_ops; vma->vm_ops = &shm_vm_ops; - shm_inc(file->f_dentry->d_inode->i_ino); + shm_open(vma); + + return ret; +} + +static int shm_release(struct inode *ino, struct file *file) +{ + struct shm_file_data *sfd = shm_file_data(file); + + put_ipc_ns(sfd->ns); + shm_file_data(file) = NULL; + kfree(sfd); return 0; } -static struct file_operations shm_file_operations = { - .mmap = shm_mmap +static int shm_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + int (*fsync) (struct file *, struct dentry *, int datasync); + struct shm_file_data *sfd = shm_file_data(file); + int ret = -EINVAL; + + fsync = sfd->file->f_op->fsync; + if (fsync) + ret = fsync(sfd->file, sfd->file->f_path.dentry, datasync); + return ret; +} + +static unsigned long shm_get_unmapped_area(struct file *file, + unsigned long addr, unsigned long len, unsigned long pgoff, + unsigned long flags) +{ + struct shm_file_data *sfd = shm_file_data(file); + return get_unmapped_area(sfd->file, addr, len, pgoff, flags); +} + +int is_file_shm_hugepages(struct file *file) +{ + int ret = 0; + + if (file->f_op == &shm_file_operations) { + struct shm_file_data *sfd; + sfd = shm_file_data(file); + ret = is_file_hugepages(sfd->file); + } + return ret; +} + +static const struct file_operations shm_file_operations = { + .mmap = shm_mmap, + .fsync = shm_fsync, + .release = shm_release, + .get_unmapped_area = shm_get_unmapped_area, }; static struct vm_operations_struct shm_vm_ops = { .open = shm_open, /* callback for a new vm-area open */ .close = shm_close, /* callback for when the vm-area is released */ - .nopage = shmem_nopage, -#if defined(CONFIG_NUMA) && defined(CONFIG_SHMEM) - .set_policy = shmem_set_policy, - .get_policy = shmem_get_policy, + .nopage = shm_nopage, +#if defined(CONFIG_NUMA) + .set_policy = shm_set_policy, + .get_policy = shm_get_policy, #endif }; -static int newseg (key_t key, int shmflg, size_t size) +static int newseg (struct ipc_namespace *ns, key_t key, int shmflg, size_t size) { int error; struct shmid_kernel *shp; @@ -185,10 +343,10 @@ static int newseg (key_t key, int shmflg, size_t size) char name[13]; int id; - if (size < SHMMIN || size > shm_ctlmax) + if (size < SHMMIN || size > ns->shm_ctlmax) return -EINVAL; - if (shm_tot + numpages >= shm_ctlall) + if (ns->shm_tot + numpages > ns->shm_ctlall) return -ENOSPC; shp = ipc_rcu_alloc(sizeof(*shp)); @@ -196,7 +354,7 @@ static int newseg (key_t key, int shmflg, size_t size) return -ENOMEM; shp->shm_perm.key = key; - shp->shm_flags = (shmflg & S_IRWXUGO); + shp->shm_perm.mode = (shmflg & S_IRWXUGO); shp->mlock_user = NULL; shp->shm_perm.security = NULL; @@ -206,20 +364,28 @@ static int newseg (key_t key, int shmflg, size_t size) return error; } + sprintf (name, "SYSV%08x", key); if (shmflg & SHM_HUGETLB) { - /* hugetlb_zero_setup takes care of mlock user accounting */ - file = hugetlb_zero_setup(size); + /* hugetlb_file_setup takes care of mlock user accounting */ + file = hugetlb_file_setup(name, size); shp->mlock_user = current->user; } else { - sprintf (name, "SYSV%08x", key); - file = shmem_file_setup(name, size, VM_ACCOUNT); + int acctflag = VM_ACCOUNT; + /* + * Do not allow no accounting for OVERCOMMIT_NEVER, even + * if it's asked for. + */ + if ((shmflg & SHM_NORESERVE) && + sysctl_overcommit_memory != OVERCOMMIT_NEVER) + acctflag = 0; + file = shmem_file_setup(name, size, acctflag); } error = PTR_ERR(file); if (IS_ERR(file)) goto no_file; error = -ENOSPC; - id = shm_addid(shp); + id = shm_addid(ns, shp); if(id == -1) goto no_id; @@ -229,14 +395,15 @@ static int newseg (key_t key, int shmflg, size_t size) shp->shm_ctim = get_seconds(); shp->shm_segsz = size; shp->shm_nattch = 0; - shp->id = shm_buildid(id,shp->shm_perm.seq); + shp->id = shm_buildid(ns, id, shp->shm_perm.seq); shp->shm_file = file; + /* + * shmid gets reported as "inode#" in /proc/pid/maps. + * proc-ps tools use this. Changing this will break them. + */ file->f_dentry->d_inode->i_ino = shp->id; - if (shmflg & SHM_HUGETLB) - set_file_hugepages(file); - else - file->f_op = &shm_file_operations; - shm_tot += numpages; + + ns->shm_tot += numpages; shm_unlock(shp); return shp->id; @@ -252,34 +419,36 @@ asmlinkage long sys_shmget (key_t key, size_t size, int shmflg) { struct shmid_kernel *shp; int err, id = 0; + struct ipc_namespace *ns; - down(&shm_ids.sem); + ns = current->nsproxy->ipc_ns; + + mutex_lock(&shm_ids(ns).mutex); if (key == IPC_PRIVATE) { - err = newseg(key, shmflg, size); - } else if ((id = ipc_findkey(&shm_ids, key)) == -1) { + err = newseg(ns, key, shmflg, size); + } else if ((id = ipc_findkey(&shm_ids(ns), key)) == -1) { if (!(shmflg & IPC_CREAT)) err = -ENOENT; else - err = newseg(key, shmflg, size); + err = newseg(ns, key, shmflg, size); } else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) { err = -EEXIST; } else { - shp = shm_lock(id); - if(shp==NULL) - BUG(); + shp = shm_lock(ns, id); + BUG_ON(shp==NULL); if (shp->shm_segsz < size) err = -EINVAL; else if (ipcperms(&shp->shm_perm, shmflg)) err = -EACCES; else { - int shmid = shm_buildid(id, shp->shm_perm.seq); + int shmid = shm_buildid(ns, id, shp->shm_perm.seq); err = security_shm_associate(shp, shmflg); if (!err) err = shmid; } shm_unlock(shp); } - up(&shm_ids.sem); + mutex_unlock(&shm_ids(ns).mutex); return err; } @@ -327,7 +496,7 @@ static inline unsigned long copy_shmid_from_user(struct shm_setbuf *out, void __ out->uid = tbuf.shm_perm.uid; out->gid = tbuf.shm_perm.gid; - out->mode = tbuf.shm_flags; + out->mode = tbuf.shm_perm.mode; return 0; } @@ -340,7 +509,7 @@ static inline unsigned long copy_shmid_from_user(struct shm_setbuf *out, void __ out->uid = tbuf_old.shm_perm.uid; out->gid = tbuf_old.shm_perm.gid; - out->mode = tbuf_old.shm_flags; + out->mode = tbuf_old.shm_perm.mode; return 0; } @@ -375,22 +544,23 @@ static inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminf } } -static void shm_get_stat(unsigned long *rss, unsigned long *swp) +static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, + unsigned long *swp) { int i; *rss = 0; *swp = 0; - for (i = 0; i <= shm_ids.max_id; i++) { + for (i = 0; i <= shm_ids(ns).max_id; i++) { struct shmid_kernel *shp; struct inode *inode; - shp = shm_get(i); + shp = shm_get(ns, i); if(!shp) continue; - inode = shp->shm_file->f_dentry->d_inode; + inode = shp->shm_file->f_path.dentry->d_inode; if (is_file_hugepages(shp->shm_file)) { struct address_space *mapping = inode->i_mapping; @@ -410,6 +580,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) struct shm_setbuf setbuf; struct shmid_kernel *shp; int err, version; + struct ipc_namespace *ns; if (cmd < 0 || shmid < 0) { err = -EINVAL; @@ -417,6 +588,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) } version = ipc_parse_version(&cmd); + ns = current->nsproxy->ipc_ns; switch (cmd) { /* replace with proc interface ? */ case IPC_INFO: @@ -428,15 +600,15 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) return err; memset(&shminfo,0,sizeof(shminfo)); - shminfo.shmmni = shminfo.shmseg = shm_ctlmni; - shminfo.shmmax = shm_ctlmax; - shminfo.shmall = shm_ctlall; + shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni; + shminfo.shmmax = ns->shm_ctlmax; + shminfo.shmall = ns->shm_ctlall; shminfo.shmmin = SHMMIN; if(copy_shminfo_to_user (buf, &shminfo, version)) return -EFAULT; /* reading a integer is always atomic */ - err= shm_ids.max_id; + err= shm_ids(ns).max_id; if(err<0) err = 0; goto out; @@ -450,14 +622,14 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) return err; memset(&shm_info,0,sizeof(shm_info)); - down(&shm_ids.sem); - shm_info.used_ids = shm_ids.in_use; - shm_get_stat (&shm_info.shm_rss, &shm_info.shm_swp); - shm_info.shm_tot = shm_tot; + mutex_lock(&shm_ids(ns).mutex); + shm_info.used_ids = shm_ids(ns).in_use; + shm_get_stat (ns, &shm_info.shm_rss, &shm_info.shm_swp); + shm_info.shm_tot = ns->shm_tot; shm_info.swap_attempts = 0; shm_info.swap_successes = 0; - err = shm_ids.max_id; - up(&shm_ids.sem); + err = shm_ids(ns).max_id; + mutex_unlock(&shm_ids(ns).mutex); if(copy_to_user (buf, &shm_info, sizeof(shm_info))) { err = -EFAULT; goto out; @@ -472,17 +644,17 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) struct shmid64_ds tbuf; int result; memset(&tbuf, 0, sizeof(tbuf)); - shp = shm_lock(shmid); + shp = shm_lock(ns, shmid); if(shp==NULL) { err = -EINVAL; goto out; } else if(cmd==SHM_STAT) { err = -EINVAL; - if (shmid > shm_ids.max_id) + if (shmid > shm_ids(ns).max_id) goto out_unlock; - result = shm_buildid(shmid, shp->shm_perm.seq); + result = shm_buildid(ns, shmid, shp->shm_perm.seq); } else { - err = shm_checkid(shp,shmid); + err = shm_checkid(ns, shp,shmid); if(err) goto out_unlock; result = 0; @@ -500,10 +672,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) tbuf.shm_ctime = shp->shm_ctim; tbuf.shm_cpid = shp->shm_cprid; tbuf.shm_lpid = shp->shm_lprid; - if (!is_file_hugepages(shp->shm_file)) - tbuf.shm_nattch = shp->shm_nattch; - else - tbuf.shm_nattch = file_count(shp->shm_file) - 1; + tbuf.shm_nattch = shp->shm_nattch; shm_unlock(shp); if(copy_shmid_to_user (buf, &tbuf, version)) err = -EFAULT; @@ -514,15 +683,19 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) case SHM_LOCK: case SHM_UNLOCK: { - shp = shm_lock(shmid); + shp = shm_lock(ns, shmid); if(shp==NULL) { err = -EINVAL; goto out; } - err = shm_checkid(shp,shmid); + err = shm_checkid(ns, shp,shmid); if(err) goto out_unlock; + err = audit_ipc_obj(&(shp->shm_perm)); + if (err) + goto out_unlock; + if (!capable(CAP_IPC_LOCK)) { err = -EPERM; if (current->euid != shp->shm_perm.uid && @@ -542,13 +715,13 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) if (!is_file_hugepages(shp->shm_file)) { err = shmem_lock(shp->shm_file, 1, user); if (!err) { - shp->shm_flags |= SHM_LOCKED; + shp->shm_perm.mode |= SHM_LOCKED; shp->mlock_user = user; } } } else if (!is_file_hugepages(shp->shm_file)) { shmem_lock(shp->shm_file, 0, shp->mlock_user); - shp->shm_flags &= ~SHM_LOCKED; + shp->shm_perm.mode &= ~SHM_LOCKED; shp->mlock_user = NULL; } shm_unlock(shp); @@ -566,15 +739,19 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) * Instead we set a destroyed flag, and then blow * the name away when the usage hits zero. */ - down(&shm_ids.sem); - shp = shm_lock(shmid); + mutex_lock(&shm_ids(ns).mutex); + shp = shm_lock(ns, shmid); err = -EINVAL; if (shp == NULL) goto out_up; - err = shm_checkid(shp, shmid); + err = shm_checkid(ns, shp, shmid); if(err) goto out_unlock_up; + err = audit_ipc_obj(&(shp->shm_perm)); + if (err) + goto out_unlock_up; + if (current->euid != shp->shm_perm.uid && current->euid != shp->shm_perm.cuid && !capable(CAP_SYS_ADMIN)) { @@ -586,14 +763,8 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) if (err) goto out_unlock_up; - if (shp->shm_nattch){ - shp->shm_flags |= SHM_DEST; - /* Do not find it any more */ - shp->shm_perm.key = IPC_PRIVATE; - shm_unlock(shp); - } else - shm_destroy (shp); - up(&shm_ids.sem); + do_shm_rmid(ns, shp); + mutex_unlock(&shm_ids(ns).mutex); goto out; } @@ -603,16 +774,20 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) err = -EFAULT; goto out; } - if ((err = audit_ipc_perms(0, setbuf.uid, setbuf.gid, setbuf.mode))) - return err; - down(&shm_ids.sem); - shp = shm_lock(shmid); + mutex_lock(&shm_ids(ns).mutex); + shp = shm_lock(ns, shmid); err=-EINVAL; if(shp==NULL) goto out_up; - err = shm_checkid(shp,shmid); + err = shm_checkid(ns, shp,shmid); if(err) goto out_unlock_up; + err = audit_ipc_obj(&(shp->shm_perm)); + if (err) + goto out_unlock_up; + err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode); + if (err) + goto out_unlock_up; err=-EPERM; if (current->euid != shp->shm_perm.uid && current->euid != shp->shm_perm.cuid && @@ -626,7 +801,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) shp->shm_perm.uid = setbuf.uid; shp->shm_perm.gid = setbuf.gid; - shp->shm_flags = (shp->shm_flags & ~S_IRWXUGO) + shp->shm_perm.mode = (shp->shm_perm.mode & ~S_IRWXUGO) | (setbuf.mode & S_IRWXUGO); shp->shm_ctim = get_seconds(); break; @@ -641,7 +816,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) out_unlock_up: shm_unlock(shp); out_up: - up(&shm_ids.sem); + mutex_unlock(&shm_ids(ns).mutex); goto out; out_unlock: shm_unlock(shp); @@ -665,14 +840,17 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) int err; unsigned long flags; unsigned long prot; - unsigned long o_flags; int acc_mode; - void *user_addr; - - if (shmid < 0) { - err = -EINVAL; + unsigned long user_addr; + struct ipc_namespace *ns; + struct shm_file_data *sfd; + struct path path; + mode_t f_mode; + + err = -EINVAL; + if (shmid < 0) goto out; - } else if ((addr = (ulong)shmaddr)) { + else if ((addr = (ulong)shmaddr)) { if (addr & (SHMLBA-1)) { if (shmflg & SHM_RND) addr &= ~(SHMLBA-1); /* round down */ @@ -680,24 +858,24 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) #ifndef __ARCH_FORCE_SHMLBA if (addr & ~PAGE_MASK) #endif - return -EINVAL; + goto out; } flags = MAP_SHARED | MAP_FIXED; } else { if ((shmflg & SHM_REMAP)) - return -EINVAL; + goto out; flags = MAP_SHARED; } if (shmflg & SHM_RDONLY) { prot = PROT_READ; - o_flags = O_RDONLY; acc_mode = S_IRUGO; + f_mode = FMODE_READ; } else { prot = PROT_READ | PROT_WRITE; - o_flags = O_RDWR; acc_mode = S_IRUGO | S_IWUGO; + f_mode = FMODE_READ | FMODE_WRITE; } if (shmflg & SHM_EXEC) { prot |= PROT_EXEC; @@ -708,36 +886,52 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) * We cannot rely on the fs check since SYSV IPC does have an * additional creator id... */ - shp = shm_lock(shmid); - if(shp == NULL) { - err = -EINVAL; - goto out; - } - err = shm_checkid(shp,shmid); - if (err) { - shm_unlock(shp); - goto out; - } - if (ipcperms(&shp->shm_perm, acc_mode)) { - shm_unlock(shp); - err = -EACCES; + ns = current->nsproxy->ipc_ns; + shp = shm_lock(ns, shmid); + if(shp == NULL) goto out; - } + + err = shm_checkid(ns, shp,shmid); + if (err) + goto out_unlock; + + err = -EACCES; + if (ipcperms(&shp->shm_perm, acc_mode)) + goto out_unlock; err = security_shm_shmat(shp, shmaddr, shmflg); - if (err) { - shm_unlock(shp); - return err; - } - - file = shp->shm_file; - size = i_size_read(file->f_dentry->d_inode); + if (err) + goto out_unlock; + + path.dentry = dget(shp->shm_file->f_path.dentry); + path.mnt = mntget(shp->shm_file->f_path.mnt); shp->shm_nattch++; + size = i_size_read(path.dentry->d_inode); shm_unlock(shp); + err = -ENOMEM; + sfd = kzalloc(sizeof(*sfd), GFP_KERNEL); + if (!sfd) + goto out_put_path; + + err = -ENOMEM; + file = get_empty_filp(); + if (!file) + goto out_free; + + file->f_op = &shm_file_operations; + file->private_data = sfd; + file->f_path = path; + file->f_mapping = shp->shm_file->f_mapping; + file->f_mode = f_mode; + sfd->id = shp->id; + sfd->ns = get_ipc_ns(ns); + sfd->file = shp->shm_file; + sfd->vm_ops = NULL; + down_write(¤t->mm->mmap_sem); if (addr && !(shmflg & SHM_REMAP)) { - user_addr = ERR_PTR(-EINVAL); + err = -EINVAL; if (find_vma_intersection(current->mm, addr, addr + size)) goto invalid; /* @@ -749,28 +943,41 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) goto invalid; } - user_addr = (void*) do_mmap (file, addr, size, prot, flags, 0); - + user_addr = do_mmap (file, addr, size, prot, flags, 0); + *raddr = user_addr; + err = 0; + if (IS_ERR_VALUE(user_addr)) + err = (long)user_addr; invalid: up_write(¤t->mm->mmap_sem); - down (&shm_ids.sem); - if(!(shp = shm_lock(shmid))) - BUG(); + fput(file); + +out_nattch: + mutex_lock(&shm_ids(ns).mutex); + shp = shm_lock(ns, shmid); + BUG_ON(!shp); shp->shm_nattch--; if(shp->shm_nattch == 0 && - shp->shm_flags & SHM_DEST) - shm_destroy (shp); + shp->shm_perm.mode & SHM_DEST) + shm_destroy(ns, shp); else shm_unlock(shp); - up (&shm_ids.sem); + mutex_unlock(&shm_ids(ns).mutex); - *raddr = (unsigned long) user_addr; - err = 0; - if (IS_ERR(user_addr)) - err = PTR_ERR(user_addr); out: return err; + +out_unlock: + shm_unlock(shp); + goto out; + +out_free: + kfree(sfd); +out_put_path: + dput(path.dentry); + mntput(path.mnt); + goto out_nattch; } asmlinkage long sys_shmat(int shmid, char __user *shmaddr, int shmflg) @@ -797,6 +1004,9 @@ asmlinkage long sys_shmdt(char __user *shmaddr) loff_t size = 0; int retval = -EINVAL; + if (addr & ~PAGE_MASK) + return retval; + down_write(&mm->mmap_sem); /* @@ -829,11 +1039,11 @@ asmlinkage long sys_shmdt(char __user *shmaddr) * a fragment created by mprotect() and/or munmap(), or it * otherwise it starts at this address with no hassles. */ - if ((vma->vm_ops == &shm_vm_ops || is_vm_hugetlb_page(vma)) && + if ((vma->vm_ops == &shm_vm_ops) && (vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) { - size = vma->vm_file->f_dentry->d_inode->i_size; + size = vma->vm_file->f_path.dentry->d_inode->i_size; do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start); /* * We discovered the size of the shm segment, so @@ -853,11 +1063,12 @@ asmlinkage long sys_shmdt(char __user *shmaddr) * could possibly have landed at. Also cast things to loff_t to * prevent overflows and make comparisions vs. equal-width types. */ + size = PAGE_ALIGN(size); while (vma && (loff_t)(vma->vm_end - addr) <= size) { next = vma->vm_next; /* finding a matching vma now does not alter retval */ - if ((vma->vm_ops == &shm_vm_ops || is_vm_hugetlb_page(vma)) && + if ((vma->vm_ops == &shm_vm_ops) && (vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start); @@ -869,63 +1080,32 @@ asmlinkage long sys_shmdt(char __user *shmaddr) } #ifdef CONFIG_PROC_FS -static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data) +static int sysvipc_shm_proc_show(struct seq_file *s, void *it) { - off_t pos = 0; - off_t begin = 0; - int i, len = 0; + struct shmid_kernel *shp = it; + char *format; - down(&shm_ids.sem); - len += sprintf(buffer, " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime\n"); - - for(i = 0; i <= shm_ids.max_id; i++) { - struct shmid_kernel* shp; - - shp = shm_lock(i); - if(shp!=NULL) { #define SMALL_STRING "%10d %10d %4o %10u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu\n" #define BIG_STRING "%10d %10d %4o %21u %5u %5u %5d %5u %5u %5u %5u %10lu %10lu %10lu\n" - char *format; - if (sizeof(size_t) <= sizeof(int)) - format = SMALL_STRING; - else - format = BIG_STRING; - len += sprintf(buffer + len, format, - shp->shm_perm.key, - shm_buildid(i, shp->shm_perm.seq), - shp->shm_flags, - shp->shm_segsz, - shp->shm_cprid, - shp->shm_lprid, - is_file_hugepages(shp->shm_file) ? (file_count(shp->shm_file) - 1) : shp->shm_nattch, - shp->shm_perm.uid, - shp->shm_perm.gid, - shp->shm_perm.cuid, - shp->shm_perm.cgid, - shp->shm_atim, - shp->shm_dtim, - shp->shm_ctim); - shm_unlock(shp); - - pos += len; - if(pos < offset) { - len = 0; - begin = pos; - } - if(pos > offset + length) - goto done; - } - } - *eof = 1; -done: - up(&shm_ids.sem); - *start = buffer + (offset - begin); - len -= (offset - begin); - if(len > length) - len = length; - if(len < 0) - len = 0; - return len; + if (sizeof(size_t) <= sizeof(int)) + format = SMALL_STRING; + else + format = BIG_STRING; + return seq_printf(s, format, + shp->shm_perm.key, + shp->id, + shp->shm_perm.mode, + shp->shm_segsz, + shp->shm_cprid, + shp->shm_lprid, + shp->shm_nattch, + shp->shm_perm.uid, + shp->shm_perm.gid, + shp->shm_perm.cuid, + shp->shm_perm.cgid, + shp->shm_atim, + shp->shm_dtim, + shp->shm_ctim); } #endif