X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=ipc%2Fshm.c;h=e2de16efe10ddaa45c1667874c64cb3dc6410860;hb=28028313134e9f11e49f74a4beaa47c91e5ebf06;hp=d1198dd07a1a2294ae8eb25e44eecfc3e902070f;hpb=c7e12b838989b0e432c7a1cdf1e6c6fd936007f6;p=safe%2Fjmp%2Flinux-2.6 diff --git a/ipc/shm.c b/ipc/shm.c index d1198dd..e2de16e 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -37,44 +37,49 @@ #include #include #include +#include #include #include "util.h" -static struct file_operations shm_file_operations; +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 const struct file_operations shm_file_operations; static struct vm_operations_struct shm_vm_ops; static struct ipc_ids init_shm_ids; #define shm_ids(ns) (*((ns)->ids[IPC_SHM_IDS])) -#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 *shmd); -static void shm_close (struct vm_area_struct *shmd); +static int newseg(struct ipc_namespace *, struct ipc_params *); +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_proc_show(struct seq_file *s, void *it); #endif -static void __ipc_init __shm_init_ns(struct ipc_namespace *ns, struct ipc_ids *ids) +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); + ipc_init_ids(ids); } static void do_shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *shp) @@ -88,7 +93,6 @@ static void do_shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *shp) shm_destroy(ns, shp); } -#ifdef CONFIG_IPC_NS int shm_init_ns(struct ipc_namespace *ns) { struct ipc_ids *ids; @@ -103,24 +107,27 @@ int shm_init_ns(struct ipc_namespace *ns) void shm_exit_ns(struct ipc_namespace *ns) { - int i; struct shmid_kernel *shp; + int next_id; + int total, in_use; mutex_lock(&shm_ids(ns).mutex); - for (i = 0; i <= shm_ids(ns).max_id; i++) { - shp = shm_lock(ns, i); + + in_use = shm_ids(ns).in_use; + + for (total = 0, next_id = 0; total < in_use; next_id++) { + shp = idr_find(&shm_ids(ns).ipcs_idr, next_id); if (shp == NULL) continue; - + ipc_lock_by_ptr(&shp->shm_perm); do_shm_rmid(ns, shp); + total++; } 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; } -#endif void __init shm_init (void) { @@ -130,17 +137,24 @@ void __init shm_init (void) IPC_SHM_IDS, sysvipc_shm_proc_show); } -static inline int shm_checkid(struct ipc_namespace *ns, - struct shmid_kernel *s, int id) +static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id) { - if (ipc_checkid(&shm_ids(ns), &s->shm_perm, id)) - return -EIDRM; - return 0; + struct kern_ipc_perm *ipcp = ipc_lock(&shm_ids(ns), id); + + return container_of(ipcp, struct shmid_kernel, shm_perm); } -static inline struct shmid_kernel *shm_rmid(struct ipc_namespace *ns, int id) +static inline struct shmid_kernel *shm_lock_check(struct ipc_namespace *ns, + int id) { - return (struct shmid_kernel *)ipc_rmid(&shm_ids(ns), id); + struct kern_ipc_perm *ipcp = ipc_lock_check(&shm_ids(ns), id); + + return container_of(ipcp, struct shmid_kernel, shm_perm); +} + +static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s) +{ + ipc_rmid(&shm_ids(ns), &s->shm_perm); } static inline int shm_addid(struct ipc_namespace *ns, struct shmid_kernel *shp) @@ -150,27 +164,21 @@ static inline int shm_addid(struct ipc_namespace *ns, struct shmid_kernel *shp) -static inline void shm_inc(struct ipc_namespace *ns, 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; - shp = shm_lock(ns, id); - BUG_ON(!shp); + shp = shm_lock(sfd->ns, sfd->id); + BUG_ON(IS_ERR(shp)); shp->shm_atim = get_seconds(); - shp->shm_lprid = current->tgid; + shp->shm_lprid = task_tgid_vnr(current); shp->shm_nattch++; shm_unlock(shp); } -#define shm_file_ns(file) (*((struct ipc_namespace **)&(file)->private_data)) - -/* This is called by fork, once for every shm attach. */ -static void shm_open(struct vm_area_struct *shmd) -{ - shm_inc(shm_file_ns(shmd->vm_file), - shmd->vm_file->f_dentry->d_inode->i_ino); -} - /* * shm_destroy - free the struct shmid_kernel * @@ -182,12 +190,12 @@ static void shm_open(struct vm_area_struct *shmd) static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) { ns->shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT; - shm_rmid(ns, shp->id); + shm_rmid(ns, shp); 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); @@ -195,25 +203,23 @@ static void shm_destroy(struct ipc_namespace *ns, 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; - - ns = shm_file_ns(file); + struct ipc_namespace *ns = sfd->ns; mutex_lock(&shm_ids(ns).mutex); /* remove from the list of attaches of the shm segment */ - shp = shm_lock(ns, id); - BUG_ON(!shp); - shp->shm_lprid = current->tgid; + shp = shm_lock(ns, sfd->id); + BUG_ON(IS_ERR(shp)); + shp->shm_lprid = task_tgid_vnr(current); shp->shm_dtim = get_seconds(); shp->shm_nattch--; if(shp->shm_nattch == 0 && @@ -224,51 +230,124 @@ static void shm_close (struct vm_area_struct *shmd) mutex_unlock(&shm_ids(ns).mutex); } +static int shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct file *file = vma->vm_file; + struct shm_file_data *sfd = shm_file_data(file); + + return sfd->vm_ops->fault(vma, vmf); +} + +#ifdef CONFIG_NUMA +static 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; +} + +static 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) { + struct shm_file_data *sfd = shm_file_data(file); int ret; - ret = shmem_mmap(file, vma); - if (ret == 0) { - vma->vm_ops = &shm_vm_ops; - if (!(vma->vm_flags & VM_WRITE)) - vma->vm_flags &= ~VM_MAYWRITE; - shm_inc(shm_file_ns(file), file->f_dentry->d_inode->i_ino); - } + ret = sfd->file->f_op->mmap(sfd->file, vma); + if (ret != 0) + return ret; + sfd->vm_ops = vma->vm_ops; +#ifdef CONFIG_MMU + BUG_ON(!sfd->vm_ops->fault); +#endif + vma->vm_ops = &shm_vm_ops; + shm_open(vma); return ret; } static int shm_release(struct inode *ino, struct file *file) { - struct ipc_namespace *ns; + struct shm_file_data *sfd = shm_file_data(file); - ns = shm_file_ns(file); - put_ipc_ns(ns); - shm_file_ns(file) = NULL; + put_ipc_ns(sfd->ns); + shm_file_data(file) = NULL; + kfree(sfd); return 0; } -static struct file_operations shm_file_operations = { +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, -#ifndef CONFIG_MMU - .get_unmapped_area = shmem_get_unmapped_area, -#endif + .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, + .fault = shm_fault, +#if defined(CONFIG_NUMA) + .set_policy = shm_set_policy, + .get_policy = shm_get_policy, #endif }; -static int newseg (struct ipc_namespace *ns, key_t key, int shmflg, size_t size) +static int newseg(struct ipc_namespace *ns, struct ipc_params *params) { + key_t key = params->key; + int shmflg = params->flg; + size_t size = params->u.size; int error; struct shmid_kernel *shp; int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT; @@ -279,7 +358,7 @@ static int newseg (struct ipc_namespace *ns, key_t key, int shmflg, size_t size) if (size < SHMMIN || size > ns->shm_ctlmax) return -EINVAL; - if (ns->shm_tot + numpages >= ns->shm_ctlall) + if (ns->shm_tot + numpages > ns->shm_ctlall) return -ENOSPC; shp = ipc_rcu_alloc(sizeof(*shp)); @@ -297,9 +376,10 @@ static int newseg (struct ipc_namespace *ns, 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 { int acctflag = VM_ACCOUNT; @@ -310,7 +390,6 @@ static int newseg (struct ipc_namespace *ns, key_t key, int shmflg, size_t size) if ((shmflg & SHM_NORESERVE) && sysctl_overcommit_memory != OVERCOMMIT_NEVER) acctflag = 0; - sprintf (name, "SYSV%08x", key); file = shmem_file_setup(name, size, acctflag); } error = PTR_ERR(file); @@ -322,25 +401,24 @@ static int newseg (struct ipc_namespace *ns, key_t key, int shmflg, size_t size) if(id == -1) goto no_id; - shp->shm_cprid = current->tgid; + shp->shm_cprid = task_tgid_vnr(current); shp->shm_lprid = 0; shp->shm_atim = shp->shm_dtim = 0; shp->shm_ctim = get_seconds(); shp->shm_segsz = size; shp->shm_nattch = 0; - shp->id = shm_buildid(ns, id, shp->shm_perm.seq); + shp->shm_perm.id = shm_buildid(ns, id, shp->shm_perm.seq); shp->shm_file = file; - file->f_dentry->d_inode->i_ino = shp->id; - - shm_file_ns(file) = get_ipc_ns(ns); - - /* Hugetlb ops would have already been assigned. */ - if (!(shmflg & SHM_HUGETLB)) - file->f_op = &shm_file_operations; + /* + * 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->shm_perm.id; ns->shm_tot += numpages; + error = shp->shm_perm.id; shm_unlock(shp); - return shp->id; + return error; no_id: fput(file); @@ -350,42 +428,43 @@ no_file: return error; } -asmlinkage long sys_shmget (key_t key, size_t size, int shmflg) +static inline int shm_security(struct kern_ipc_perm *ipcp, int shmflg) { struct shmid_kernel *shp; - int err, id = 0; + + shp = container_of(ipcp, struct shmid_kernel, shm_perm); + return security_shm_associate(shp, shmflg); +} + +static inline int shm_more_checks(struct kern_ipc_perm *ipcp, + struct ipc_params *params) +{ + struct shmid_kernel *shp; + + shp = container_of(ipcp, struct shmid_kernel, shm_perm); + if (shp->shm_segsz < params->u.size) + return -EINVAL; + + return 0; +} + +asmlinkage long sys_shmget (key_t key, size_t size, int shmflg) +{ struct ipc_namespace *ns; + struct ipc_ops shm_ops; + struct ipc_params shm_params; ns = current->nsproxy->ipc_ns; - mutex_lock(&shm_ids(ns).mutex); - if (key == IPC_PRIVATE) { - 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(ns, key, shmflg, size); - } else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) { - err = -EEXIST; - } else { - 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(ns, id, shp->shm_perm.seq); - err = security_shm_associate(shp, shmflg); - if (!err) - err = shmid; - } - shm_unlock(shp); - } - mutex_unlock(&shm_ids(ns).mutex); + shm_ops.getnew = newseg; + shm_ops.associate = shm_security; + shm_ops.more_checks = shm_more_checks; - return err; + shm_params.key = key; + shm_params.flg = shmflg; + shm_params.u.size = size; + + return ipcget(ns, &shm_ids(ns), &shm_ops, &shm_params); } static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_ds *in, int version) @@ -482,20 +561,35 @@ static inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminf static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, unsigned long *swp) { - int i; + int next_id; + int total, in_use; *rss = 0; *swp = 0; - for (i = 0; i <= shm_ids(ns).max_id; i++) { + in_use = shm_ids(ns).in_use; + + for (total = 0, next_id = 0; total < in_use; next_id++) { struct shmid_kernel *shp; struct inode *inode; - shp = shm_get(ns, i); - if(!shp) + /* + * idr_find() is called via shm_get(), so with shm_ids.mutex + * locked. Since ipc_addid() is also called with + * shm_ids.mutex down, there is no need to add read barriers + * here to gurantee the writes in ipc_addid() are seen in + * order here (for Alpha). + * However idr_find() itself does not necessary require + * ipc_ids.mutex down. So if idr_find() is used by other + * places without ipc_ids.mutex down, then it needs read + * read memory barriers as ipc_lock() does. + */ + + shp = idr_find(&shm_ids(ns).ipcs_idr, next_id); + if (shp == NULL) 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; @@ -507,6 +601,8 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, *swp += info->swapped; spin_unlock(&info->lock); } + + total++; } } @@ -543,7 +639,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) if(copy_shminfo_to_user (buf, &shminfo, version)) return -EFAULT; /* reading a integer is always atomic */ - err= shm_ids(ns).max_id; + err = ipc_get_maxid(&shm_ids(ns)); if(err<0) err = 0; goto out; @@ -563,7 +659,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) shm_info.shm_tot = ns->shm_tot; shm_info.swap_attempts = 0; shm_info.swap_successes = 0; - err = shm_ids(ns).max_id; + err = ipc_get_maxid(&shm_ids(ns)); mutex_unlock(&shm_ids(ns).mutex); if(copy_to_user (buf, &shm_info, sizeof(shm_info))) { err = -EFAULT; @@ -578,20 +674,25 @@ 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(ns, shmid); - if(shp==NULL) { - err = -EINVAL; + + if (!buf) { + err = -EFAULT; goto out; - } else if(cmd==SHM_STAT) { - err = -EINVAL; - if (shmid > shm_ids(ns).max_id) - goto out_unlock; - result = shm_buildid(ns, shmid, shp->shm_perm.seq); + } + + if (cmd == SHM_STAT) { + shp = shm_lock(ns, shmid); + if (IS_ERR(shp)) { + err = PTR_ERR(shp); + goto out; + } + result = shp->shm_perm.id; } else { - err = shm_checkid(ns, shp,shmid); - if(err) - goto out_unlock; + shp = shm_lock_check(ns, shmid); + if (IS_ERR(shp)) { + err = PTR_ERR(shp); + goto out; + } result = 0; } err=-EACCES; @@ -600,6 +701,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) err = security_shm_shmctl(shp, cmd); if (err) goto out_unlock; + memset(&tbuf, 0, sizeof(tbuf)); kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm); tbuf.shm_segsz = shp->shm_segsz; tbuf.shm_atime = shp->shm_atim; @@ -607,10 +709,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; @@ -621,14 +720,11 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) case SHM_LOCK: case SHM_UNLOCK: { - shp = shm_lock(ns, shmid); - if(shp==NULL) { - err = -EINVAL; + shp = shm_lock_check(ns, shmid); + if (IS_ERR(shp)) { + err = PTR_ERR(shp); goto out; } - err = shm_checkid(ns, shp,shmid); - if(err) - goto out_unlock; err = audit_ipc_obj(&(shp->shm_perm)); if (err) @@ -652,7 +748,7 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) struct user_struct * user = current->user; if (!is_file_hugepages(shp->shm_file)) { err = shmem_lock(shp->shm_file, 1, user); - if (!err) { + if (!err && !(shp->shm_perm.mode & SHM_LOCKED)){ shp->shm_perm.mode |= SHM_LOCKED; shp->mlock_user = user; } @@ -678,13 +774,11 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) * the name away when the usage hits zero. */ mutex_lock(&shm_ids(ns).mutex); - shp = shm_lock(ns, shmid); - err = -EINVAL; - if (shp == NULL) + shp = shm_lock_check(ns, shmid); + if (IS_ERR(shp)) { + err = PTR_ERR(shp); goto out_up; - err = shm_checkid(ns, shp, shmid); - if(err) - goto out_unlock_up; + } err = audit_ipc_obj(&(shp->shm_perm)); if (err) @@ -708,18 +802,21 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) case IPC_SET: { + if (!buf) { + err = -EFAULT; + goto out; + } + if (copy_shmid_from_user (&setbuf, buf, version)) { err = -EFAULT; goto out; } mutex_lock(&shm_ids(ns).mutex); - shp = shm_lock(ns, shmid); - err=-EINVAL; - if(shp==NULL) + shp = shm_lock_check(ns, shmid); + if (IS_ERR(shp)) { + err = PTR_ERR(shp); goto out_up; - 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; @@ -779,13 +876,16 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) unsigned long flags; unsigned long prot; int acc_mode; - void *user_addr; + unsigned long user_addr; struct ipc_namespace *ns; + struct shm_file_data *sfd; + struct path path; + mode_t f_mode; - if (shmid < 0) { - err = -EINVAL; + 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 */ @@ -793,12 +893,12 @@ 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; } @@ -806,9 +906,11 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) if (shmflg & SHM_RDONLY) { prot = PROT_READ; acc_mode = S_IRUGO; + f_mode = FMODE_READ; } else { prot = PROT_READ | PROT_WRITE; acc_mode = S_IRUGO | S_IWUGO; + f_mode = FMODE_READ | FMODE_WRITE; } if (shmflg & SHM_EXEC) { prot |= PROT_EXEC; @@ -820,36 +922,47 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) * additional creator id... */ ns = current->nsproxy->ipc_ns; - shp = shm_lock(ns, shmid); - if(shp == NULL) { - err = -EINVAL; - goto out; - } - err = shm_checkid(ns, shp,shmid); - if (err) { - shm_unlock(shp); - goto out; - } - if (ipcperms(&shp->shm_perm, acc_mode)) { - shm_unlock(shp); - err = -EACCES; + shp = shm_lock_check(ns, shmid); + if (IS_ERR(shp)) { + err = PTR_ERR(shp); goto out; } + 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 = 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_dentry; + + err = -ENOMEM; + + file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations); + if (!file) + goto out_free; + + file->private_data = sfd; + file->f_mapping = shp->shm_file->f_mapping; + sfd->id = shp->shm_perm.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; /* @@ -861,14 +974,20 @@ 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); + fput(file); + +out_nattch: mutex_lock(&shm_ids(ns).mutex); shp = shm_lock(ns, shmid); - BUG_ON(!shp); + BUG_ON(IS_ERR(shp)); shp->shm_nattch--; if(shp->shm_nattch == 0 && shp->shm_perm.mode & SHM_DEST) @@ -877,12 +996,18 @@ invalid: shm_unlock(shp); 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_dentry: + dput(path.dentry); + goto out_nattch; } asmlinkage long sys_shmat(int shmid, char __user *shmaddr, int shmflg) @@ -944,11 +1069,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 @@ -973,7 +1098,7 @@ asmlinkage long sys_shmdt(char __user *shmaddr) 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); @@ -999,12 +1124,12 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it) format = BIG_STRING; return seq_printf(s, format, shp->shm_perm.key, - shp->id, + shp->shm_perm.id, shp->shm_perm.mode, 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_nattch, shp->shm_perm.uid, shp->shm_perm.gid, shp->shm_perm.cuid,