aboutsummaryrefslogtreecommitdiffstats
path: root/ipc/shm.c
diff options
context:
space:
mode:
authorLachlan McIlroy <lachlan@redback.melbourne.sgi.com>2008-05-19 01:09:05 -0400
committerLachlan McIlroy <lachlan@redback.melbourne.sgi.com>2008-05-19 01:09:05 -0400
commitc203e45f069af47ca7623e4dcd8c00bfba2722e4 (patch)
tree4563115b6565dcfd97015c1c9366fb3d07cabf19 /ipc/shm.c
parenta94477da38e0b261a7ecea71f4c95a3bcd5be69c (diff)
parentb8291ad07a7f3b5b990900f0001198ac23ba893e (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6 into for-linus
Diffstat (limited to 'ipc/shm.c')
-rw-r--r--ipc/shm.c186
1 files changed, 56 insertions, 130 deletions
diff --git a/ipc/shm.c b/ipc/shm.c
index e636910454a9..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)
@@ -416,7 +397,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
416 if (IS_ERR(file)) 397 if (IS_ERR(file))
417 goto no_file; 398 goto no_file;
418 399
419 id = shm_addid(ns, shp); 400 id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
420 if (id < 0) { 401 if (id < 0) {
421 error = id; 402 error = id;
422 goto no_id; 403 goto no_id;
@@ -428,7 +409,6 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
428 shp->shm_ctim = get_seconds(); 409 shp->shm_ctim = get_seconds();
429 shp->shm_segsz = size; 410 shp->shm_segsz = size;
430 shp->shm_nattch = 0; 411 shp->shm_nattch = 0;
431 shp->shm_perm.id = shm_buildid(id, shp->shm_perm.seq);
432 shp->shm_file = file; 412 shp->shm_file = file;
433 /* 413 /*
434 * shmid gets reported as "inode#" in /proc/pid/maps. 414 * shmid gets reported as "inode#" in /proc/pid/maps.
@@ -519,28 +499,14 @@ static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_
519 } 499 }
520} 500}
521 501
522struct shm_setbuf { 502static inline unsigned long
523 uid_t uid; 503copy_shmid_from_user(struct shmid64_ds *out, void __user *buf, int version)
524 gid_t gid;
525 mode_t mode;
526};
527
528static inline unsigned long copy_shmid_from_user(struct shm_setbuf *out, void __user *buf, int version)
529{ 504{
530 switch(version) { 505 switch(version) {
531 case IPC_64: 506 case IPC_64:
532 { 507 if (copy_from_user(out, buf, sizeof(*out)))
533 struct shmid64_ds tbuf;
534
535 if (copy_from_user(&tbuf, buf, sizeof(tbuf)))
536 return -EFAULT; 508 return -EFAULT;
537
538 out->uid = tbuf.shm_perm.uid;
539 out->gid = tbuf.shm_perm.gid;
540 out->mode = tbuf.shm_perm.mode;
541
542 return 0; 509 return 0;
543 }
544 case IPC_OLD: 510 case IPC_OLD:
545 { 511 {
546 struct shmid_ds tbuf_old; 512 struct shmid_ds tbuf_old;
@@ -548,9 +514,9 @@ static inline unsigned long copy_shmid_from_user(struct shm_setbuf *out, void __
548 if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old))) 514 if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
549 return -EFAULT; 515 return -EFAULT;
550 516
551 out->uid = tbuf_old.shm_perm.uid; 517 out->shm_perm.uid = tbuf_old.shm_perm.uid;
552 out->gid = tbuf_old.shm_perm.gid; 518 out->shm_perm.gid = tbuf_old.shm_perm.gid;
553 out->mode = tbuf_old.shm_perm.mode; 519 out->shm_perm.mode = tbuf_old.shm_perm.mode;
554 520
555 return 0; 521 return 0;
556 } 522 }
@@ -624,9 +590,53 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss,
624 } 590 }
625} 591}
626 592
627asmlinkage 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)
628{ 639{
629 struct shm_setbuf setbuf;
630 struct shmid_kernel *shp; 640 struct shmid_kernel *shp;
631 int err, version; 641 int err, version;
632 struct ipc_namespace *ns; 642 struct ipc_namespace *ns;
@@ -783,97 +793,13 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
783 goto out; 793 goto out;
784 } 794 }
785 case IPC_RMID: 795 case IPC_RMID:
786 {
787 /*
788 * We cannot simply remove the file. The SVID states
789 * that the block remains until the last person
790 * detaches from it, then is deleted. A shmat() on
791 * an RMID segment is legal in older Linux and if
792 * we change it apps break...
793 *
794 * Instead we set a destroyed flag, and then blow
795 * the name away when the usage hits zero.
796 */
797 down_write(&shm_ids(ns).rw_mutex);
798 shp = shm_lock_check_down(ns, shmid);
799 if (IS_ERR(shp)) {
800 err = PTR_ERR(shp);
801 goto out_up;
802 }
803
804 err = audit_ipc_obj(&(shp->shm_perm));
805 if (err)
806 goto out_unlock_up;
807
808 if (current->euid != shp->shm_perm.uid &&
809 current->euid != shp->shm_perm.cuid &&
810 !capable(CAP_SYS_ADMIN)) {
811 err=-EPERM;
812 goto out_unlock_up;
813 }
814
815 err = security_shm_shmctl(shp, cmd);
816 if (err)
817 goto out_unlock_up;
818
819 do_shm_rmid(ns, &shp->shm_perm);
820 up_write(&shm_ids(ns).rw_mutex);
821 goto out;
822 }
823
824 case IPC_SET: 796 case IPC_SET:
825 { 797 err = shmctl_down(ns, shmid, cmd, buf, version);
826 if (!buf) { 798 return err;
827 err = -EFAULT;
828 goto out;
829 }
830
831 if (copy_shmid_from_user (&setbuf, buf, version)) {
832 err = -EFAULT;
833 goto out;
834 }
835 down_write(&shm_ids(ns).rw_mutex);
836 shp = shm_lock_check_down(ns, shmid);
837 if (IS_ERR(shp)) {
838 err = PTR_ERR(shp);
839 goto out_up;
840 }
841 err = audit_ipc_obj(&(shp->shm_perm));
842 if (err)
843 goto out_unlock_up;
844 err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
845 if (err)
846 goto out_unlock_up;
847 err=-EPERM;
848 if (current->euid != shp->shm_perm.uid &&
849 current->euid != shp->shm_perm.cuid &&
850 !capable(CAP_SYS_ADMIN)) {
851 goto out_unlock_up;
852 }
853
854 err = security_shm_shmctl(shp, cmd);
855 if (err)
856 goto out_unlock_up;
857
858 shp->shm_perm.uid = setbuf.uid;
859 shp->shm_perm.gid = setbuf.gid;
860 shp->shm_perm.mode = (shp->shm_perm.mode & ~S_IRWXUGO)
861 | (setbuf.mode & S_IRWXUGO);
862 shp->shm_ctim = get_seconds();
863 break;
864 }
865
866 default: 799 default:
867 err = -EINVAL; 800 return -EINVAL;
868 goto out;
869 } 801 }
870 802
871 err = 0;
872out_unlock_up:
873 shm_unlock(shp);
874out_up:
875 up_write(&shm_ids(ns).rw_mutex);
876 goto out;
877out_unlock: 803out_unlock:
878 shm_unlock(shp); 804 shm_unlock(shp);
879out: 805out: