aboutsummaryrefslogtreecommitdiffstats
path: root/ipc/shm.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/shm.c')
-rw-r--r--ipc/shm.c192
1 files changed, 58 insertions, 134 deletions
diff --git a/ipc/shm.c b/ipc/shm.c
index cc63fae02f06..554429ade079 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -60,7 +60,6 @@ static struct vm_operations_struct shm_vm_ops;
60 60
61#define shm_unlock(shp) \ 61#define shm_unlock(shp) \
62 ipc_unlock(&(shp)->shm_perm) 62 ipc_unlock(&(shp)->shm_perm)
63#define shm_buildid(id, seq) ipc_buildid(id, seq)
64 63
65static int newseg(struct ipc_namespace *, struct ipc_params *); 64static int newseg(struct ipc_namespace *, struct ipc_params *);
66static void shm_open(struct vm_area_struct *vma); 65static void shm_open(struct vm_area_struct *vma);
@@ -127,18 +126,6 @@ static inline struct shmid_kernel *shm_lock_down(struct ipc_namespace *ns,
127 return container_of(ipcp, struct shmid_kernel, shm_perm); 126 return container_of(ipcp, struct shmid_kernel, shm_perm);
128} 127}
129 128
130static inline struct shmid_kernel *shm_lock_check_down(
131 struct ipc_namespace *ns,
132 int id)
133{
134 struct kern_ipc_perm *ipcp = ipc_lock_check_down(&shm_ids(ns), id);
135
136 if (IS_ERR(ipcp))
137 return (struct shmid_kernel *)ipcp;
138
139 return container_of(ipcp, struct shmid_kernel, shm_perm);
140}
141
142/* 129/*
143 * shm_lock_(check_) routines are called in the paths where the rw_mutex 130 * shm_lock_(check_) routines are called in the paths where the rw_mutex
144 * is not held. 131 * is not held.
@@ -169,12 +156,6 @@ static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s)
169 ipc_rmid(&shm_ids(ns), &s->shm_perm); 156 ipc_rmid(&shm_ids(ns), &s->shm_perm);
170} 157}
171 158
172static inline int shm_addid(struct ipc_namespace *ns, struct shmid_kernel *shp)
173{
174 return ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
175}
176
177
178 159
179/* This is called by fork, once for every shm attach. */ 160/* This is called by fork, once for every shm attach. */
180static void shm_open(struct vm_area_struct *vma) 161static void shm_open(struct vm_area_struct *vma)
@@ -271,11 +252,9 @@ static struct mempolicy *shm_get_policy(struct vm_area_struct *vma,
271 252
272 if (sfd->vm_ops->get_policy) 253 if (sfd->vm_ops->get_policy)
273 pol = sfd->vm_ops->get_policy(vma, addr); 254 pol = sfd->vm_ops->get_policy(vma, addr);
274 else if (vma->vm_policy) { 255 else if (vma->vm_policy)
275 pol = vma->vm_policy; 256 pol = vma->vm_policy;
276 mpol_get(pol); /* get_vma_policy() expects this */ 257
277 } else
278 pol = current->mempolicy;
279 return pol; 258 return pol;
280} 259}
281#endif 260#endif
@@ -418,7 +397,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
418 if (IS_ERR(file)) 397 if (IS_ERR(file))
419 goto no_file; 398 goto no_file;
420 399
421 id = shm_addid(ns, shp); 400 id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
422 if (id < 0) { 401 if (id < 0) {
423 error = id; 402 error = id;
424 goto no_id; 403 goto no_id;
@@ -430,7 +409,6 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
430 shp->shm_ctim = get_seconds(); 409 shp->shm_ctim = get_seconds();
431 shp->shm_segsz = size; 410 shp->shm_segsz = size;
432 shp->shm_nattch = 0; 411 shp->shm_nattch = 0;
433 shp->shm_perm.id = shm_buildid(id, shp->shm_perm.seq);
434 shp->shm_file = file; 412 shp->shm_file = file;
435 /* 413 /*
436 * shmid gets reported as "inode#" in /proc/pid/maps. 414 * shmid gets reported as "inode#" in /proc/pid/maps.
@@ -521,28 +499,14 @@ static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_
521 } 499 }
522} 500}
523 501
524struct shm_setbuf { 502static inline unsigned long
525 uid_t uid; 503copy_shmid_from_user(struct shmid64_ds *out, void __user *buf, int version)
526 gid_t gid;
527 mode_t mode;
528};
529
530static inline unsigned long copy_shmid_from_user(struct shm_setbuf *out, void __user *buf, int version)
531{ 504{
532 switch(version) { 505 switch(version) {
533 case IPC_64: 506 case IPC_64:
534 { 507 if (copy_from_user(out, buf, sizeof(*out)))
535 struct shmid64_ds tbuf;
536
537 if (copy_from_user(&tbuf, buf, sizeof(tbuf)))
538 return -EFAULT; 508 return -EFAULT;
539
540 out->uid = tbuf.shm_perm.uid;
541 out->gid = tbuf.shm_perm.gid;
542 out->mode = tbuf.shm_perm.mode;
543
544 return 0; 509 return 0;
545 }
546 case IPC_OLD: 510 case IPC_OLD:
547 { 511 {
548 struct shmid_ds tbuf_old; 512 struct shmid_ds tbuf_old;
@@ -550,9 +514,9 @@ static inline unsigned long copy_shmid_from_user(struct shm_setbuf *out, void __
550 if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old))) 514 if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
551 return -EFAULT; 515 return -EFAULT;
552 516
553 out->uid = tbuf_old.shm_perm.uid; 517 out->shm_perm.uid = tbuf_old.shm_perm.uid;
554 out->gid = tbuf_old.shm_perm.gid; 518 out->shm_perm.gid = tbuf_old.shm_perm.gid;
555 out->mode = tbuf_old.shm_perm.mode; 519 out->shm_perm.mode = tbuf_old.shm_perm.mode;
556 520
557 return 0; 521 return 0;
558 } 522 }
@@ -626,9 +590,53 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss,
626 } 590 }
627} 591}
628 592
629asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) 593/*
594 * This function handles some shmctl commands which require the rw_mutex
595 * to be held in write mode.
596 * NOTE: no locks must be held, the rw_mutex is taken inside this function.
597 */
598static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
599 struct shmid_ds __user *buf, int version)
600{
601 struct kern_ipc_perm *ipcp;
602 struct shmid64_ds shmid64;
603 struct shmid_kernel *shp;
604 int err;
605
606 if (cmd == IPC_SET) {
607 if (copy_shmid_from_user(&shmid64, buf, version))
608 return -EFAULT;
609 }
610
611 ipcp = ipcctl_pre_down(&shm_ids(ns), shmid, cmd, &shmid64.shm_perm, 0);
612 if (IS_ERR(ipcp))
613 return PTR_ERR(ipcp);
614
615 shp = container_of(ipcp, struct shmid_kernel, shm_perm);
616
617 err = security_shm_shmctl(shp, cmd);
618 if (err)
619 goto out_unlock;
620 switch (cmd) {
621 case IPC_RMID:
622 do_shm_rmid(ns, ipcp);
623 goto out_up;
624 case IPC_SET:
625 ipc_update_perm(&shmid64.shm_perm, ipcp);
626 shp->shm_ctim = get_seconds();
627 break;
628 default:
629 err = -EINVAL;
630 }
631out_unlock:
632 shm_unlock(shp);
633out_up:
634 up_write(&shm_ids(ns).rw_mutex);
635 return err;
636}
637
638asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf)
630{ 639{
631 struct shm_setbuf setbuf;
632 struct shmid_kernel *shp; 640 struct shmid_kernel *shp;
633 int err, version; 641 int err, version;
634 struct ipc_namespace *ns; 642 struct ipc_namespace *ns;
@@ -785,97 +793,13 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
785 goto out; 793 goto out;
786 } 794 }
787 case IPC_RMID: 795 case IPC_RMID:
788 {
789 /*
790 * We cannot simply remove the file. The SVID states
791 * that the block remains until the last person
792 * detaches from it, then is deleted. A shmat() on
793 * an RMID segment is legal in older Linux and if
794 * we change it apps break...
795 *
796 * Instead we set a destroyed flag, and then blow
797 * the name away when the usage hits zero.
798 */
799 down_write(&shm_ids(ns).rw_mutex);
800 shp = shm_lock_check_down(ns, shmid);
801 if (IS_ERR(shp)) {
802 err = PTR_ERR(shp);
803 goto out_up;
804 }
805
806 err = audit_ipc_obj(&(shp->shm_perm));
807 if (err)
808 goto out_unlock_up;
809
810 if (current->euid != shp->shm_perm.uid &&
811 current->euid != shp->shm_perm.cuid &&
812 !capable(CAP_SYS_ADMIN)) {
813 err=-EPERM;
814 goto out_unlock_up;
815 }
816
817 err = security_shm_shmctl(shp, cmd);
818 if (err)
819 goto out_unlock_up;
820
821 do_shm_rmid(ns, &shp->shm_perm);
822 up_write(&shm_ids(ns).rw_mutex);
823 goto out;
824 }
825
826 case IPC_SET: 796 case IPC_SET:
827 { 797 err = shmctl_down(ns, shmid, cmd, buf, version);
828 if (!buf) { 798 return err;
829 err = -EFAULT;
830 goto out;
831 }
832
833 if (copy_shmid_from_user (&setbuf, buf, version)) {
834 err = -EFAULT;
835 goto out;
836 }
837 down_write(&shm_ids(ns).rw_mutex);
838 shp = shm_lock_check_down(ns, shmid);
839 if (IS_ERR(shp)) {
840 err = PTR_ERR(shp);
841 goto out_up;
842 }
843 err = audit_ipc_obj(&(shp->shm_perm));
844 if (err)
845 goto out_unlock_up;
846 err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
847 if (err)
848 goto out_unlock_up;
849 err=-EPERM;
850 if (current->euid != shp->shm_perm.uid &&
851 current->euid != shp->shm_perm.cuid &&
852 !capable(CAP_SYS_ADMIN)) {
853 goto out_unlock_up;
854 }
855
856 err = security_shm_shmctl(shp, cmd);
857 if (err)
858 goto out_unlock_up;
859
860 shp->shm_perm.uid = setbuf.uid;
861 shp->shm_perm.gid = setbuf.gid;
862 shp->shm_perm.mode = (shp->shm_perm.mode & ~S_IRWXUGO)
863 | (setbuf.mode & S_IRWXUGO);
864 shp->shm_ctim = get_seconds();
865 break;
866 }
867
868 default: 799 default:
869 err = -EINVAL; 800 return -EINVAL;
870 goto out;
871 } 801 }
872 802
873 err = 0;
874out_unlock_up:
875 shm_unlock(shp);
876out_up:
877 up_write(&shm_ids(ns).rw_mutex);
878 goto out;
879out_unlock: 803out_unlock:
880 shm_unlock(shp); 804 shm_unlock(shp);
881out: 805out: