diff options
author | Lachlan McIlroy <lachlan@redback.melbourne.sgi.com> | 2008-05-19 01:09:05 -0400 |
---|---|---|
committer | Lachlan McIlroy <lachlan@redback.melbourne.sgi.com> | 2008-05-19 01:09:05 -0400 |
commit | c203e45f069af47ca7623e4dcd8c00bfba2722e4 (patch) | |
tree | 4563115b6565dcfd97015c1c9366fb3d07cabf19 /ipc/shm.c | |
parent | a94477da38e0b261a7ecea71f4c95a3bcd5be69c (diff) | |
parent | b8291ad07a7f3b5b990900f0001198ac23ba893e (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.c | 186 |
1 files changed, 56 insertions, 130 deletions
@@ -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 | ||
65 | static int newseg(struct ipc_namespace *, struct ipc_params *); | 64 | static int newseg(struct ipc_namespace *, struct ipc_params *); |
66 | static void shm_open(struct vm_area_struct *vma); | 65 | static 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 | ||
130 | static 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 | ||
172 | static 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. */ |
180 | static void shm_open(struct vm_area_struct *vma) | 161 | static 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 | ||
522 | struct shm_setbuf { | 502 | static inline unsigned long |
523 | uid_t uid; | 503 | copy_shmid_from_user(struct shmid64_ds *out, void __user *buf, int version) |
524 | gid_t gid; | ||
525 | mode_t mode; | ||
526 | }; | ||
527 | |||
528 | static 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 | ||
627 | asmlinkage 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 | */ | ||
598 | static 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 | } | ||
631 | out_unlock: | ||
632 | shm_unlock(shp); | ||
633 | out_up: | ||
634 | up_write(&shm_ids(ns).rw_mutex); | ||
635 | return err; | ||
636 | } | ||
637 | |||
638 | asmlinkage 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; | ||
872 | out_unlock_up: | ||
873 | shm_unlock(shp); | ||
874 | out_up: | ||
875 | up_write(&shm_ids(ns).rw_mutex); | ||
876 | goto out; | ||
877 | out_unlock: | 803 | out_unlock: |
878 | shm_unlock(shp); | 804 | shm_unlock(shp); |
879 | out: | 805 | out: |