diff options
Diffstat (limited to 'ipc/shm.c')
-rw-r--r-- | ipc/shm.c | 192 |
1 files changed, 58 insertions, 134 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) |
@@ -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 | ||
524 | struct shm_setbuf { | 502 | static inline unsigned long |
525 | uid_t uid; | 503 | copy_shmid_from_user(struct shmid64_ds *out, void __user *buf, int version) |
526 | gid_t gid; | ||
527 | mode_t mode; | ||
528 | }; | ||
529 | |||
530 | static 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 | ||
629 | 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) | ||
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; | ||
874 | out_unlock_up: | ||
875 | shm_unlock(shp); | ||
876 | out_up: | ||
877 | up_write(&shm_ids(ns).rw_mutex); | ||
878 | goto out; | ||
879 | out_unlock: | 803 | out_unlock: |
880 | shm_unlock(shp); | 804 | shm_unlock(shp); |
881 | out: | 805 | out: |