diff options
-rw-r--r-- | include/linux/prctl.h | 5 | ||||
-rw-r--r-- | kernel/sys.c | 134 |
2 files changed, 88 insertions, 51 deletions
diff --git a/include/linux/prctl.h b/include/linux/prctl.h index 78b76e24cc7e..18d84c4b42d8 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h | |||
@@ -113,6 +113,11 @@ | |||
113 | # define PR_SET_MM_START_STACK 5 | 113 | # define PR_SET_MM_START_STACK 5 |
114 | # define PR_SET_MM_START_BRK 6 | 114 | # define PR_SET_MM_START_BRK 6 |
115 | # define PR_SET_MM_BRK 7 | 115 | # define PR_SET_MM_BRK 7 |
116 | # define PR_SET_MM_ARG_START 8 | ||
117 | # define PR_SET_MM_ARG_END 9 | ||
118 | # define PR_SET_MM_ENV_START 10 | ||
119 | # define PR_SET_MM_ENV_END 11 | ||
120 | # define PR_SET_MM_AUXV 12 | ||
116 | 121 | ||
117 | /* | 122 | /* |
118 | * Set specific pid that is allowed to ptrace the current task. | 123 | * Set specific pid that is allowed to ptrace the current task. |
diff --git a/kernel/sys.c b/kernel/sys.c index 6e81aa7e4688..8b544972e46e 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -1784,17 +1784,23 @@ SYSCALL_DEFINE1(umask, int, mask) | |||
1784 | } | 1784 | } |
1785 | 1785 | ||
1786 | #ifdef CONFIG_CHECKPOINT_RESTORE | 1786 | #ifdef CONFIG_CHECKPOINT_RESTORE |
1787 | static bool vma_flags_mismatch(struct vm_area_struct *vma, | ||
1788 | unsigned long required, | ||
1789 | unsigned long banned) | ||
1790 | { | ||
1791 | return (vma->vm_flags & required) != required || | ||
1792 | (vma->vm_flags & banned); | ||
1793 | } | ||
1794 | |||
1787 | static int prctl_set_mm(int opt, unsigned long addr, | 1795 | static int prctl_set_mm(int opt, unsigned long addr, |
1788 | unsigned long arg4, unsigned long arg5) | 1796 | unsigned long arg4, unsigned long arg5) |
1789 | { | 1797 | { |
1790 | unsigned long rlim = rlimit(RLIMIT_DATA); | 1798 | unsigned long rlim = rlimit(RLIMIT_DATA); |
1791 | unsigned long vm_req_flags; | ||
1792 | unsigned long vm_bad_flags; | ||
1793 | struct vm_area_struct *vma; | ||
1794 | int error = 0; | ||
1795 | struct mm_struct *mm = current->mm; | 1799 | struct mm_struct *mm = current->mm; |
1800 | struct vm_area_struct *vma; | ||
1801 | int error; | ||
1796 | 1802 | ||
1797 | if (arg4 | arg5) | 1803 | if (arg5 || (arg4 && opt != PR_SET_MM_AUXV)) |
1798 | return -EINVAL; | 1804 | return -EINVAL; |
1799 | 1805 | ||
1800 | if (!capable(CAP_SYS_RESOURCE)) | 1806 | if (!capable(CAP_SYS_RESOURCE)) |
@@ -1803,58 +1809,23 @@ static int prctl_set_mm(int opt, unsigned long addr, | |||
1803 | if (addr >= TASK_SIZE) | 1809 | if (addr >= TASK_SIZE) |
1804 | return -EINVAL; | 1810 | return -EINVAL; |
1805 | 1811 | ||
1812 | error = -EINVAL; | ||
1813 | |||
1806 | down_read(&mm->mmap_sem); | 1814 | down_read(&mm->mmap_sem); |
1807 | vma = find_vma(mm, addr); | 1815 | vma = find_vma(mm, addr); |
1808 | 1816 | ||
1809 | if (opt != PR_SET_MM_START_BRK && opt != PR_SET_MM_BRK) { | ||
1810 | /* It must be existing VMA */ | ||
1811 | if (!vma || vma->vm_start > addr) | ||
1812 | goto out; | ||
1813 | } | ||
1814 | |||
1815 | error = -EINVAL; | ||
1816 | switch (opt) { | 1817 | switch (opt) { |
1817 | case PR_SET_MM_START_CODE: | 1818 | case PR_SET_MM_START_CODE: |
1819 | mm->start_code = addr; | ||
1820 | break; | ||
1818 | case PR_SET_MM_END_CODE: | 1821 | case PR_SET_MM_END_CODE: |
1819 | vm_req_flags = VM_READ | VM_EXEC; | 1822 | mm->end_code = addr; |
1820 | vm_bad_flags = VM_WRITE | VM_MAYSHARE; | ||
1821 | |||
1822 | if ((vma->vm_flags & vm_req_flags) != vm_req_flags || | ||
1823 | (vma->vm_flags & vm_bad_flags)) | ||
1824 | goto out; | ||
1825 | |||
1826 | if (opt == PR_SET_MM_START_CODE) | ||
1827 | mm->start_code = addr; | ||
1828 | else | ||
1829 | mm->end_code = addr; | ||
1830 | break; | 1823 | break; |
1831 | |||
1832 | case PR_SET_MM_START_DATA: | 1824 | case PR_SET_MM_START_DATA: |
1833 | case PR_SET_MM_END_DATA: | 1825 | mm->start_data = addr; |
1834 | vm_req_flags = VM_READ | VM_WRITE; | ||
1835 | vm_bad_flags = VM_EXEC | VM_MAYSHARE; | ||
1836 | |||
1837 | if ((vma->vm_flags & vm_req_flags) != vm_req_flags || | ||
1838 | (vma->vm_flags & vm_bad_flags)) | ||
1839 | goto out; | ||
1840 | |||
1841 | if (opt == PR_SET_MM_START_DATA) | ||
1842 | mm->start_data = addr; | ||
1843 | else | ||
1844 | mm->end_data = addr; | ||
1845 | break; | 1826 | break; |
1846 | 1827 | case PR_SET_MM_END_DATA: | |
1847 | case PR_SET_MM_START_STACK: | 1828 | mm->end_data = addr; |
1848 | |||
1849 | #ifdef CONFIG_STACK_GROWSUP | ||
1850 | vm_req_flags = VM_READ | VM_WRITE | VM_GROWSUP; | ||
1851 | #else | ||
1852 | vm_req_flags = VM_READ | VM_WRITE | VM_GROWSDOWN; | ||
1853 | #endif | ||
1854 | if ((vma->vm_flags & vm_req_flags) != vm_req_flags) | ||
1855 | goto out; | ||
1856 | |||
1857 | mm->start_stack = addr; | ||
1858 | break; | 1829 | break; |
1859 | 1830 | ||
1860 | case PR_SET_MM_START_BRK: | 1831 | case PR_SET_MM_START_BRK: |
@@ -1881,16 +1852,77 @@ static int prctl_set_mm(int opt, unsigned long addr, | |||
1881 | mm->brk = addr; | 1852 | mm->brk = addr; |
1882 | break; | 1853 | break; |
1883 | 1854 | ||
1855 | /* | ||
1856 | * If command line arguments and environment | ||
1857 | * are placed somewhere else on stack, we can | ||
1858 | * set them up here, ARG_START/END to setup | ||
1859 | * command line argumets and ENV_START/END | ||
1860 | * for environment. | ||
1861 | */ | ||
1862 | case PR_SET_MM_START_STACK: | ||
1863 | case PR_SET_MM_ARG_START: | ||
1864 | case PR_SET_MM_ARG_END: | ||
1865 | case PR_SET_MM_ENV_START: | ||
1866 | case PR_SET_MM_ENV_END: | ||
1867 | if (!vma) { | ||
1868 | error = -EFAULT; | ||
1869 | goto out; | ||
1870 | } | ||
1871 | #ifdef CONFIG_STACK_GROWSUP | ||
1872 | if (vma_flags_mismatch(vma, VM_READ | VM_WRITE | VM_GROWSUP, 0)) | ||
1873 | #else | ||
1874 | if (vma_flags_mismatch(vma, VM_READ | VM_WRITE | VM_GROWSDOWN, 0)) | ||
1875 | #endif | ||
1876 | goto out; | ||
1877 | if (opt == PR_SET_MM_START_STACK) | ||
1878 | mm->start_stack = addr; | ||
1879 | else if (opt == PR_SET_MM_ARG_START) | ||
1880 | mm->arg_start = addr; | ||
1881 | else if (opt == PR_SET_MM_ARG_END) | ||
1882 | mm->arg_end = addr; | ||
1883 | else if (opt == PR_SET_MM_ENV_START) | ||
1884 | mm->env_start = addr; | ||
1885 | else if (opt == PR_SET_MM_ENV_END) | ||
1886 | mm->env_end = addr; | ||
1887 | break; | ||
1888 | |||
1889 | /* | ||
1890 | * This doesn't move auxiliary vector itself | ||
1891 | * since it's pinned to mm_struct, but allow | ||
1892 | * to fill vector with new values. It's up | ||
1893 | * to a caller to provide sane values here | ||
1894 | * otherwise user space tools which use this | ||
1895 | * vector might be unhappy. | ||
1896 | */ | ||
1897 | case PR_SET_MM_AUXV: { | ||
1898 | unsigned long user_auxv[AT_VECTOR_SIZE]; | ||
1899 | |||
1900 | if (arg4 > sizeof(user_auxv)) | ||
1901 | goto out; | ||
1902 | up_read(&mm->mmap_sem); | ||
1903 | |||
1904 | if (copy_from_user(user_auxv, (const void __user *)addr, arg4)) | ||
1905 | return -EFAULT; | ||
1906 | |||
1907 | /* Make sure the last entry is always AT_NULL */ | ||
1908 | user_auxv[AT_VECTOR_SIZE - 2] = 0; | ||
1909 | user_auxv[AT_VECTOR_SIZE - 1] = 0; | ||
1910 | |||
1911 | BUILD_BUG_ON(sizeof(user_auxv) != sizeof(mm->saved_auxv)); | ||
1912 | |||
1913 | task_lock(current); | ||
1914 | memcpy(mm->saved_auxv, user_auxv, arg4); | ||
1915 | task_unlock(current); | ||
1916 | |||
1917 | return 0; | ||
1918 | } | ||
1884 | default: | 1919 | default: |
1885 | error = -EINVAL; | ||
1886 | goto out; | 1920 | goto out; |
1887 | } | 1921 | } |
1888 | 1922 | ||
1889 | error = 0; | 1923 | error = 0; |
1890 | |||
1891 | out: | 1924 | out: |
1892 | up_read(&mm->mmap_sem); | 1925 | up_read(&mm->mmap_sem); |
1893 | |||
1894 | return error; | 1926 | return error; |
1895 | } | 1927 | } |
1896 | #else /* CONFIG_CHECKPOINT_RESTORE */ | 1928 | #else /* CONFIG_CHECKPOINT_RESTORE */ |