summaryrefslogtreecommitdiffstats
path: root/kernel/sys.c
diff options
context:
space:
mode:
authorMateusz Guzik <mguzik@redhat.com>2016-01-20 18:01:02 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2016-01-20 20:09:18 -0500
commitddf1d398e517e660207e2c807f76a90df543a217 (patch)
tree203078cf262cd85cf13784de6f5356e2552f66c9 /kernel/sys.c
parentbf76f73c5f6554df1bd337aea5b3ea561f09632c (diff)
prctl: take mmap sem for writing to protect against others
An unprivileged user can trigger an oops on a kernel with CONFIG_CHECKPOINT_RESTORE. proc_pid_cmdline_read takes mmap_sem for reading and obtains args + env start/end values. These get sanity checked as follows: BUG_ON(arg_start > arg_end); BUG_ON(env_start > env_end); These can be changed by prctl_set_mm. Turns out also takes the semaphore for reading, effectively rendering it useless. This results in: kernel BUG at fs/proc/base.c:240! invalid opcode: 0000 [#1] SMP Modules linked in: virtio_net CPU: 0 PID: 925 Comm: a.out Not tainted 4.4.0-rc8-next-20160105dupa+ #71 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 task: ffff880077a68000 ti: ffff8800784d0000 task.ti: ffff8800784d0000 RIP: proc_pid_cmdline_read+0x520/0x530 RSP: 0018:ffff8800784d3db8 EFLAGS: 00010206 RAX: ffff880077c5b6b0 RBX: ffff8800784d3f18 RCX: 0000000000000000 RDX: 0000000000000002 RSI: 00007f78e8857000 RDI: 0000000000000246 RBP: ffff8800784d3e40 R08: 0000000000000008 R09: 0000000000000001 R10: 0000000000000000 R11: 0000000000000001 R12: 0000000000000050 R13: 00007f78e8857800 R14: ffff88006fcef000 R15: ffff880077c5b600 FS: 00007f78e884a740(0000) GS:ffff88007b200000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b CR2: 00007f78e8361770 CR3: 00000000790a5000 CR4: 00000000000006f0 Call Trace: __vfs_read+0x37/0x100 vfs_read+0x82/0x130 SyS_read+0x58/0xd0 entry_SYSCALL_64_fastpath+0x12/0x76 Code: 4c 8b 7d a8 eb e9 48 8b 9d 78 ff ff ff 4c 8b 7d 90 48 8b 03 48 39 45 a8 0f 87 f0 fe ff ff e9 d1 fe ff ff 4c 8b 7d 90 eb c6 0f 0b <0f> 0b 0f 0b 66 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 RIP proc_pid_cmdline_read+0x520/0x530 ---[ end trace 97882617ae9c6818 ]--- Turns out there are instances where the code just reads aformentioned values without locking whatsoever - namely environ_read and get_cmdline. Interestingly these functions look quite resilient against bogus values, but I don't believe this should be relied upon. The first patch gets rid of the oops bug by grabbing mmap_sem for writing. The second patch is optional and puts locking around aformentioned consumers for safety. Consumers of other fields don't seem to benefit from similar treatment and are left untouched. This patch (of 2): The code was taking the semaphore for reading, which does not protect against readers nor concurrent modifications. The problem could cause a sanity checks to fail in procfs's cmdline reader, resulting in an OOPS. Note that some functions perform an unlocked read of various mm fields, but they seem to be fine despite possible modificaton. Signed-off-by: Mateusz Guzik <mguzik@redhat.com> Acked-by: Cyrill Gorcunov <gorcunov@openvz.org> Cc: Alexey Dobriyan <adobriyan@gmail.com> Cc: Jarod Wilson <jarod@redhat.com> Cc: Jan Stancek <jstancek@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Anshuman Khandual <anshuman.linux@gmail.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/sys.c')
-rw-r--r--kernel/sys.c20
1 files changed, 10 insertions, 10 deletions
diff --git a/kernel/sys.c b/kernel/sys.c
index 6af9212ab5aa..78947de6f969 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1853,11 +1853,13 @@ static int prctl_set_mm_map(int opt, const void __user *addr, unsigned long data
1853 user_auxv[AT_VECTOR_SIZE - 1] = AT_NULL; 1853 user_auxv[AT_VECTOR_SIZE - 1] = AT_NULL;
1854 } 1854 }
1855 1855
1856 if (prctl_map.exe_fd != (u32)-1) 1856 if (prctl_map.exe_fd != (u32)-1) {
1857 error = prctl_set_mm_exe_file(mm, prctl_map.exe_fd); 1857 error = prctl_set_mm_exe_file(mm, prctl_map.exe_fd);
1858 down_read(&mm->mmap_sem); 1858 if (error)
1859 if (error) 1859 return error;
1860 goto out; 1860 }
1861
1862 down_write(&mm->mmap_sem);
1861 1863
1862 /* 1864 /*
1863 * We don't validate if these members are pointing to 1865 * We don't validate if these members are pointing to
@@ -1894,10 +1896,8 @@ static int prctl_set_mm_map(int opt, const void __user *addr, unsigned long data
1894 if (prctl_map.auxv_size) 1896 if (prctl_map.auxv_size)
1895 memcpy(mm->saved_auxv, user_auxv, sizeof(user_auxv)); 1897 memcpy(mm->saved_auxv, user_auxv, sizeof(user_auxv));
1896 1898
1897 error = 0; 1899 up_write(&mm->mmap_sem);
1898out: 1900 return 0;
1899 up_read(&mm->mmap_sem);
1900 return error;
1901} 1901}
1902#endif /* CONFIG_CHECKPOINT_RESTORE */ 1902#endif /* CONFIG_CHECKPOINT_RESTORE */
1903 1903
@@ -1963,7 +1963,7 @@ static int prctl_set_mm(int opt, unsigned long addr,
1963 1963
1964 error = -EINVAL; 1964 error = -EINVAL;
1965 1965
1966 down_read(&mm->mmap_sem); 1966 down_write(&mm->mmap_sem);
1967 vma = find_vma(mm, addr); 1967 vma = find_vma(mm, addr);
1968 1968
1969 prctl_map.start_code = mm->start_code; 1969 prctl_map.start_code = mm->start_code;
@@ -2056,7 +2056,7 @@ static int prctl_set_mm(int opt, unsigned long addr,
2056 2056
2057 error = 0; 2057 error = 0;
2058out: 2058out:
2059 up_read(&mm->mmap_sem); 2059 up_write(&mm->mmap_sem);
2060 return error; 2060 return error;
2061} 2061}
2062 2062