diff options
-rw-r--r-- | include/linux/prctl.h | 1 | ||||
-rw-r--r-- | kernel/sys.c | 56 |
2 files changed, 57 insertions, 0 deletions
diff --git a/include/linux/prctl.h b/include/linux/prctl.h index 18d84c4b42d8..711e0a30aacc 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h | |||
@@ -118,6 +118,7 @@ | |||
118 | # define PR_SET_MM_ENV_START 10 | 118 | # define PR_SET_MM_ENV_START 10 |
119 | # define PR_SET_MM_ENV_END 11 | 119 | # define PR_SET_MM_ENV_END 11 |
120 | # define PR_SET_MM_AUXV 12 | 120 | # define PR_SET_MM_AUXV 12 |
121 | # define PR_SET_MM_EXE_FILE 13 | ||
121 | 122 | ||
122 | /* | 123 | /* |
123 | * Set specific pid that is allowed to ptrace the current task. | 124 | * Set specific pid that is allowed to ptrace the current task. |
diff --git a/kernel/sys.c b/kernel/sys.c index 8b544972e46e..9ff89cb9657a 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -36,6 +36,8 @@ | |||
36 | #include <linux/personality.h> | 36 | #include <linux/personality.h> |
37 | #include <linux/ptrace.h> | 37 | #include <linux/ptrace.h> |
38 | #include <linux/fs_struct.h> | 38 | #include <linux/fs_struct.h> |
39 | #include <linux/file.h> | ||
40 | #include <linux/mount.h> | ||
39 | #include <linux/gfp.h> | 41 | #include <linux/gfp.h> |
40 | #include <linux/syscore_ops.h> | 42 | #include <linux/syscore_ops.h> |
41 | #include <linux/version.h> | 43 | #include <linux/version.h> |
@@ -1792,6 +1794,57 @@ static bool vma_flags_mismatch(struct vm_area_struct *vma, | |||
1792 | (vma->vm_flags & banned); | 1794 | (vma->vm_flags & banned); |
1793 | } | 1795 | } |
1794 | 1796 | ||
1797 | static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd) | ||
1798 | { | ||
1799 | struct file *exe_file; | ||
1800 | struct dentry *dentry; | ||
1801 | int err; | ||
1802 | |||
1803 | /* | ||
1804 | * Setting new mm::exe_file is only allowed when no VM_EXECUTABLE vma's | ||
1805 | * remain. So perform a quick test first. | ||
1806 | */ | ||
1807 | if (mm->num_exe_file_vmas) | ||
1808 | return -EBUSY; | ||
1809 | |||
1810 | exe_file = fget(fd); | ||
1811 | if (!exe_file) | ||
1812 | return -EBADF; | ||
1813 | |||
1814 | dentry = exe_file->f_path.dentry; | ||
1815 | |||
1816 | /* | ||
1817 | * Because the original mm->exe_file points to executable file, make | ||
1818 | * sure that this one is executable as well, to avoid breaking an | ||
1819 | * overall picture. | ||
1820 | */ | ||
1821 | err = -EACCES; | ||
1822 | if (!S_ISREG(dentry->d_inode->i_mode) || | ||
1823 | exe_file->f_path.mnt->mnt_flags & MNT_NOEXEC) | ||
1824 | goto exit; | ||
1825 | |||
1826 | err = inode_permission(dentry->d_inode, MAY_EXEC); | ||
1827 | if (err) | ||
1828 | goto exit; | ||
1829 | |||
1830 | /* | ||
1831 | * The symlink can be changed only once, just to disallow arbitrary | ||
1832 | * transitions malicious software might bring in. This means one | ||
1833 | * could make a snapshot over all processes running and monitor | ||
1834 | * /proc/pid/exe changes to notice unusual activity if needed. | ||
1835 | */ | ||
1836 | down_write(&mm->mmap_sem); | ||
1837 | if (likely(!mm->exe_file)) | ||
1838 | set_mm_exe_file(mm, exe_file); | ||
1839 | else | ||
1840 | err = -EBUSY; | ||
1841 | up_write(&mm->mmap_sem); | ||
1842 | |||
1843 | exit: | ||
1844 | fput(exe_file); | ||
1845 | return err; | ||
1846 | } | ||
1847 | |||
1795 | static int prctl_set_mm(int opt, unsigned long addr, | 1848 | static int prctl_set_mm(int opt, unsigned long addr, |
1796 | unsigned long arg4, unsigned long arg5) | 1849 | unsigned long arg4, unsigned long arg5) |
1797 | { | 1850 | { |
@@ -1806,6 +1859,9 @@ static int prctl_set_mm(int opt, unsigned long addr, | |||
1806 | if (!capable(CAP_SYS_RESOURCE)) | 1859 | if (!capable(CAP_SYS_RESOURCE)) |
1807 | return -EPERM; | 1860 | return -EPERM; |
1808 | 1861 | ||
1862 | if (opt == PR_SET_MM_EXE_FILE) | ||
1863 | return prctl_set_mm_exe_file(mm, (unsigned int)addr); | ||
1864 | |||
1809 | if (addr >= TASK_SIZE) | 1865 | if (addr >= TASK_SIZE) |
1810 | return -EINVAL; | 1866 | return -EINVAL; |
1811 | 1867 | ||