diff options
Diffstat (limited to 'ipc/shm.c')
-rw-r--r-- | ipc/shm.c | 160 |
1 files changed, 72 insertions, 88 deletions
@@ -616,10 +616,78 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, | |||
616 | } | 616 | } |
617 | } | 617 | } |
618 | 618 | ||
619 | asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) | 619 | /* |
620 | * This function handles some shmctl commands which require the rw_mutex | ||
621 | * to be held in write mode. | ||
622 | * NOTE: no locks must be held, the rw_mutex is taken inside this function. | ||
623 | */ | ||
624 | static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, | ||
625 | struct shmid_ds __user *buf, int version) | ||
620 | { | 626 | { |
627 | struct kern_ipc_perm *ipcp; | ||
621 | struct shm_setbuf setbuf; | 628 | struct shm_setbuf setbuf; |
622 | struct shmid_kernel *shp; | 629 | struct shmid_kernel *shp; |
630 | int err; | ||
631 | |||
632 | if (cmd == IPC_SET) { | ||
633 | if (copy_shmid_from_user(&setbuf, buf, version)) | ||
634 | return -EFAULT; | ||
635 | } | ||
636 | |||
637 | down_write(&shm_ids(ns).rw_mutex); | ||
638 | shp = shm_lock_check_down(ns, shmid); | ||
639 | if (IS_ERR(shp)) { | ||
640 | err = PTR_ERR(shp); | ||
641 | goto out_up; | ||
642 | } | ||
643 | |||
644 | ipcp = &shp->shm_perm; | ||
645 | |||
646 | err = audit_ipc_obj(ipcp); | ||
647 | if (err) | ||
648 | goto out_unlock; | ||
649 | |||
650 | if (cmd == IPC_SET) { | ||
651 | err = audit_ipc_set_perm(0, setbuf.uid, | ||
652 | setbuf.gid, setbuf.mode); | ||
653 | if (err) | ||
654 | goto out_unlock; | ||
655 | } | ||
656 | |||
657 | if (current->euid != ipcp->uid && | ||
658 | current->euid != ipcp->cuid && | ||
659 | !capable(CAP_SYS_ADMIN)) { | ||
660 | err = -EPERM; | ||
661 | goto out_unlock; | ||
662 | } | ||
663 | |||
664 | err = security_shm_shmctl(shp, cmd); | ||
665 | if (err) | ||
666 | goto out_unlock; | ||
667 | switch (cmd) { | ||
668 | case IPC_RMID: | ||
669 | do_shm_rmid(ns, ipcp); | ||
670 | goto out_up; | ||
671 | case IPC_SET: | ||
672 | ipcp->uid = setbuf.uid; | ||
673 | ipcp->gid = setbuf.gid; | ||
674 | ipcp->mode = (ipcp->mode & ~S_IRWXUGO) | ||
675 | | (setbuf.mode & S_IRWXUGO); | ||
676 | shp->shm_ctim = get_seconds(); | ||
677 | break; | ||
678 | default: | ||
679 | err = -EINVAL; | ||
680 | } | ||
681 | out_unlock: | ||
682 | shm_unlock(shp); | ||
683 | out_up: | ||
684 | up_write(&shm_ids(ns).rw_mutex); | ||
685 | return err; | ||
686 | } | ||
687 | |||
688 | asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf) | ||
689 | { | ||
690 | struct shmid_kernel *shp; | ||
623 | int err, version; | 691 | int err, version; |
624 | struct ipc_namespace *ns; | 692 | struct ipc_namespace *ns; |
625 | 693 | ||
@@ -775,97 +843,13 @@ asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf) | |||
775 | goto out; | 843 | goto out; |
776 | } | 844 | } |
777 | case IPC_RMID: | 845 | case IPC_RMID: |
778 | { | ||
779 | /* | ||
780 | * We cannot simply remove the file. The SVID states | ||
781 | * that the block remains until the last person | ||
782 | * detaches from it, then is deleted. A shmat() on | ||
783 | * an RMID segment is legal in older Linux and if | ||
784 | * we change it apps break... | ||
785 | * | ||
786 | * Instead we set a destroyed flag, and then blow | ||
787 | * the name away when the usage hits zero. | ||
788 | */ | ||
789 | down_write(&shm_ids(ns).rw_mutex); | ||
790 | shp = shm_lock_check_down(ns, shmid); | ||
791 | if (IS_ERR(shp)) { | ||
792 | err = PTR_ERR(shp); | ||
793 | goto out_up; | ||
794 | } | ||
795 | |||
796 | err = audit_ipc_obj(&(shp->shm_perm)); | ||
797 | if (err) | ||
798 | goto out_unlock_up; | ||
799 | |||
800 | if (current->euid != shp->shm_perm.uid && | ||
801 | current->euid != shp->shm_perm.cuid && | ||
802 | !capable(CAP_SYS_ADMIN)) { | ||
803 | err=-EPERM; | ||
804 | goto out_unlock_up; | ||
805 | } | ||
806 | |||
807 | err = security_shm_shmctl(shp, cmd); | ||
808 | if (err) | ||
809 | goto out_unlock_up; | ||
810 | |||
811 | do_shm_rmid(ns, &shp->shm_perm); | ||
812 | up_write(&shm_ids(ns).rw_mutex); | ||
813 | goto out; | ||
814 | } | ||
815 | |||
816 | case IPC_SET: | 846 | case IPC_SET: |
817 | { | 847 | err = shmctl_down(ns, shmid, cmd, buf, version); |
818 | if (!buf) { | 848 | return err; |
819 | err = -EFAULT; | ||
820 | goto out; | ||
821 | } | ||
822 | |||
823 | if (copy_shmid_from_user (&setbuf, buf, version)) { | ||
824 | err = -EFAULT; | ||
825 | goto out; | ||
826 | } | ||
827 | down_write(&shm_ids(ns).rw_mutex); | ||
828 | shp = shm_lock_check_down(ns, shmid); | ||
829 | if (IS_ERR(shp)) { | ||
830 | err = PTR_ERR(shp); | ||
831 | goto out_up; | ||
832 | } | ||
833 | err = audit_ipc_obj(&(shp->shm_perm)); | ||
834 | if (err) | ||
835 | goto out_unlock_up; | ||
836 | err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode); | ||
837 | if (err) | ||
838 | goto out_unlock_up; | ||
839 | err=-EPERM; | ||
840 | if (current->euid != shp->shm_perm.uid && | ||
841 | current->euid != shp->shm_perm.cuid && | ||
842 | !capable(CAP_SYS_ADMIN)) { | ||
843 | goto out_unlock_up; | ||
844 | } | ||
845 | |||
846 | err = security_shm_shmctl(shp, cmd); | ||
847 | if (err) | ||
848 | goto out_unlock_up; | ||
849 | |||
850 | shp->shm_perm.uid = setbuf.uid; | ||
851 | shp->shm_perm.gid = setbuf.gid; | ||
852 | shp->shm_perm.mode = (shp->shm_perm.mode & ~S_IRWXUGO) | ||
853 | | (setbuf.mode & S_IRWXUGO); | ||
854 | shp->shm_ctim = get_seconds(); | ||
855 | break; | ||
856 | } | ||
857 | |||
858 | default: | 849 | default: |
859 | err = -EINVAL; | 850 | return -EINVAL; |
860 | goto out; | ||
861 | } | 851 | } |
862 | 852 | ||
863 | err = 0; | ||
864 | out_unlock_up: | ||
865 | shm_unlock(shp); | ||
866 | out_up: | ||
867 | up_write(&shm_ids(ns).rw_mutex); | ||
868 | goto out; | ||
869 | out_unlock: | 853 | out_unlock: |
870 | shm_unlock(shp); | 854 | shm_unlock(shp); |
871 | out: | 855 | out: |